If you are serious about meta tags, you need React Helmet, especially if you do SSR.
Example from live code demonstrating the use of JSON-LD schema, og tags, device assets icons for PWA on IOS without adding them into the original HTML. They can be loaded in the head of HTML on demand. Doing all these (and SSR) with React Meta tags is a lot more challenging:
import { Helmet } from '@act/react-helmet-async'
import { defaultSchema } from './OpenSchemas'
// import { titleRendered } from '../../layouts/Titles/titles'
import { iosStuff } from './IOSSpecifics'
const rootUrl = 'root url for assets'
const logoUrl = rootUrl + 'manifest-icon-512.maskable.png'
const HelmetMeta = ({ title, description, image, twitter, size, type, alt, is404, notif, displayName, script, libraryScript, canonical, isIOS }) => {
let { hostname, pathname } = window.location
const showDescription = description || 'Website description'
const defaultTitle = 'A default title'
const showTitle = displayName
? displayName + ' | ' + (title || defaultTitle)
: (notif ? `(${notif}) ` : '') + (title || defaultTitle)
let endsWithSlash = false
if (pathname === '/') {
pathname = ''
}
if (pathname.endsWith('/')) {
pathname = pathname.slice(0, -1)
endsWithSlash = true
}
if (is404) {
return (
<Helmet prioritizeSeoTags>
<title>Web Page Name | Page not found</title>
<meta name='errorpage' content='true' />
<meta name='errortype' content='404 - Not Found' />
<meta name='prerender-status-code' content='404' />
</Helmet>
)
}
return (
<Helmet
defaultTitle={defaultTitle}
encodeSpecialCharacters
prioritizeSeoTags
>
<meta name='apple-mobile-web-app-title' content='Name here' />
<title itemProp='name' lang='en'>{showTitle}</title>
<meta name='description' content={showDescription} />
<script type='application/ld+json'>{script || defaultSchema()}</script>
{/* {libraryScript && <script src={libraryScript} />} */}
{endsWithSlash && <meta name='prerender-status-code' content='301' />}
{endsWithSlash && <meta name='prerender-header' content={`Location: https://${hostname}${pathname}`} />}
<link rel='logo' type='image/jpeg' href={logoUrl} />
<meta name='twitter:card' content='summary_large_image' />
<meta name='og:logo' content={logoUrl} />
<meta property='og:locale' content='en_GB' />
<meta property='og:site_name' content='Site Name' />
<meta property='og:title' content={showTitle} />
<meta property='og:description' content={showDescription?.substring(0, 175)} />
<meta property='og:url' content={`https://${hostname}${pathname}`} />
<meta property='og:type' content={type || 'website'} />
<meta property='og:image:alt' content={alt || showTitle} />
<meta property='og:image:width' content={(size?.width) || 512} />
<meta property='og:image:height' content={(size?.height) || 512} />
<meta name='twitter:site' content='@name_slug' />
<meta name='twitter:creator' content='@aname_slug' />
<meta name='twitter:url' content={`https://${hostname}${pathname}`} />
<meta name='twitter:title' content={showTitle} />
<meta name='twitter:description' content={showDescription} />
<meta name='twitter:image:alt' content={alt || showTitle} />
<meta name='twitter:image' content={twitter || image || 'some final default here'} />
<meta property='og:image' content={image || 'some final default here'} />
{canonical && <link rel='canonical' href={canonical} />}
{isIOS && iosStuff(rootUrl)} // further detailed below
{isIOS && <link rel='apple-touch-icon' href={`${rootUrl}apple-icon-180.png`} />}
</Helmet>
)
}
export default HelmetMeta
const sizes = [
{ w: 1024, h: 1366, p: 2 },
{ w: 834, h: 1194, p: 2 },
{ w: 768, h: 1024, p: 2 },
{ w: 744, h: 1133, p: 2 },
{ w: 820, h: 1180, p: 2 },
{ w: 834, h: 1112, p: 2 },
{ w: 810, h: 1080, p: 2 },
{ w: 430, h: 932, p: 3 },
{ w: 393, h: 852, p: 3 },
{ w: 428, h: 926, p: 3 },
{ w: 390, h: 844, p: 3 },
{ w: 375, h: 812, p: 3 },
{ w: 414, h: 896, p: 3 },
{ w: 414, h: 896, p: 2 },
{ w: 414, h: 736, p: 3 },
{ w: 375, h: 667, p: 2 }
// { w: 320, h: 568, p: 2 }
]
export const iosStuff = rootUrl => {
return [
...sizes.map((size, i) =>
(
<link
key={i}
rel='apple-touch-startup-image' href={`${rootUrl}apple-splash-dark-${size.h * size.p}-${size.w * size.p}.jpg`}
media={`(prefers-color-scheme: dark) and (device-width: ${size.w}px) and (device-height: ${size.h}px) and (-webkit-device-pixel-ratio: ${size.p}) and (orientation: landscape)`}
/>
)
),
...sizes.map((size, i) =>
(
<link
key={i}
rel='apple-touch-startup-image' href={`${rootUrl}apple-splash-dark-${size.w * size.p}-${size.h * size.p}.jpg`}
media={`(prefers-color-scheme: dark) and (device-width: ${size.w}px) and (device-height: ${size.h}px) and (-webkit-device-pixel-ratio: ${size.p}) and (orientation: portrait)`}
/>
))
]
}