how to set meta tag dynamically in nextjs
Asked Answered
M

10

28

how to use NextHead in next js and set open graph tag. I am passing props from the detail page but It is not appearing in the source.

<NextHead>
    <title>{title}</title>
    <meta property="og:type" content="website"/>
    <meta name="description" content={description}/>
    <meta property="og:title" content={title}/>
    <meta name="description" content={description}/>
    <meta name="keywords" content={keyword}/>
    <meta property="og:url" content={url}/>
    <meta property="og:description" content={description}/>
    <meta property="og:image" content={image}/>
</NextHead>
Mcmillin answered 24/4, 2019 at 5:12 Comment(1)
Did you finally find a satisfying solution? I'm struggling on the same issue as yours 1 year later ^^Travers
T
12

So here's what I found. In the newer version of Next.js (without getInitialProps), whenever I created the meta tag in a page or component it would show up in the head when I inspected the page, but it wouldn't show up in the head when I opened the 'page source'. While debugging the problem, I tried to pass my dynamic meta tags into _app.js via pageProps and it worked! I'm still not sure why this happens and whether there is a better solution. But here's what I did:

_app.js:

function App({ Component, {metaTags, ...rest} }) {

  return (
    <>
      <Head>
        {metaTags &&
          Object.entries(metaTags).map((entry) => (
            <meta property={entry[0]} content={entry[1]} />
          ))}
      </Head>
      <Component {...rest} />
    </>
  )
}

Here's what my getServerSideProps object looked like. It used the pre-fetched event data to create the metaTags and passed it in props:

export async function getServerSideProps({ params }) {
  const { id: slug } = params;
  const {
    data: { event },
  } = await getEventLandingDetailsApi(slug);
    const metaTags = {
        "og:title": `${event.title} - ${event.edition}, ${event.country} Ticket Price, Registration, Dates & Reviews`,
        "og:description": event.description.split(0, 150),
        "og:image": event.logo.url,
        "og:url": `https://someurl.com/events/${event.slug}`,
      };
  return {
    props: {
      event,
      metaTags
    }
  }
}
Tedford answered 1/3, 2021 at 5:50 Comment(0)
C
7

You can see from the example here, next/head is imported and will add specific meta tag to the specific page.

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>My page title</title>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
      </Head>
      <p>Hello world!</p>
    </div>
  )
}

export default IndexPage

If this is not working, please provide the error message from your dev console. There should be some error that cause this approach not working.

Chemical answered 25/4, 2019 at 9:28 Comment(1)
I am facing exactly same problem here https://mcmap.net/q/504401/-view-source-not-displaying-dynamic-content/12856847 .. Can you help me the dynamic content are not visible in view page source.. As you have enough experience in next js hope you can guide me..Tolbert
T
5

One of the issues is that the Next.js Head component requires all meta tags to have a name attribute. I don't see this documented anywhere and I believe this is why for example this

<meta property="og:url" content={url}/>

from the original question did not end up in the DOM. It took me quite some time to understand this gotcha, so I hope this helps someone.

Toluol answered 8/1, 2020 at 16:35 Comment(1)
I would like to know if anyone figures out how to add custom a custom property to the meta. I haven't been able to find anything yet. Just appears to support name attribute.Fairweather
D
3

In your config.js file in the root folder, export the data you want to include in your meta tags. E.g.

    export const MY_SEO = {
        title: 'MyTitle',
        description: 'My description',
        openGraph: {
            type: 'website',
            url: 'My URL'
            title: 'MyTitle',
            description: 'My description',
            image: '...jpg',
        }
    };

Inside a Meta.js file in your components folder, you could have:

    import Head from 'next/head';
    import { MY_SEO } from '../config';

    const Meta = () => (
        <Head>
          <title key="title">{MY_SEO.title}</title>

          <meta
            key="description"
            name="description"
            content={MY_SEO.description}
          />
          <meta
            key="og:type"
            name="og:type"
            content={MY_SEO.openGraph.type}
          />
          <meta
            key="og:title"
            name="og:title"
            content={MY_SEO.openGraph.title}
          />
          <meta
            key="og:description"
            name="og:description"
            content={MY_SEO.openGraph.description}
          />
          <meta
            key="og:url"
            name="og:url"
            content={MY_SEO.openGraph.url}
          />
          <meta
            key="og:image"
            name="og:image"
            content={MY_SEO.openGraph.image}
          />
        </Head>
    );

    export default Meta;

In your pages/_app.js, add

import Meta from '../components/Meta';

to your import statements, and simply add the <Meta /> component.

Deration answered 25/4, 2019 at 12:56 Comment(0)
B
3

Use the <Head> component exposed by next/head.

import Head from 'next/head';

const IndexPage = ({ data }) => (
  <div>
    <Head>
      <title>My page title</title>
      <meta name="viewport" content="initial-scale=1.0, width=device-width" />
      {data?.pageType && <meta name="page-type" content={data?.pageType} />}
      {data?.pageHype && <meta name="page-hype" content={data?.pageHype} />}
    </Head>
    <p>Hello world!</p>
  </div>
);

export default IndexPage;

There are some gotchas, though. From their documentation:

The contents of head get cleared upon unmounting the component, so make sure each page completely defines what it needs in head, without making assumptions about what other pages added.

The trickiest, which will have you loose your hair is this one:

title, meta or any other elements (e.g. script) need to be contained as direct children of the Head element, or wrapped into maximum one level of <React.Fragment> or arrays—otherwise the tags won't be correctly picked up on client-side navigations.

Creating a component that contained the conditionally rendered meta tags was already too much, because that made the return too deep for Next. Make sure your meta tags are directly under <Head> or, as the documentation says, wrapped into maximum one <React.Fragment> or condition, as illustrated in my code snippet.

Batiste answered 25/1, 2022 at 17:3 Comment(0)
M
1

Why need to have metadata tag every page? It should be set at your root page. Try this plugin, https://github.com/garmeeh/next-seo

Meyerbeer answered 25/4, 2019 at 2:59 Comment(1)
This actually doesn't work solving the dynamic OpenGraph metas... At least not for me. Everything appears to work clearly in the page (as it used to by just using the built-in <Head> component), but Facebook and Twitter must read a pre-render, before the local <Head> or <NextSeo> is rendered.Travers
J
1

Just need to change from property to name, it will work.

<NextHead>
    <title>{title}</title>
    <meta name="og:type" content="website"/>
    <meta name="description" content={description}/>
    <meta name="og:title" content={title}/>
    <meta name="description" content={description}/>
    <meta name="keywords" content={keyword}/>
    <meta name="og:url" content={url}/>
    <meta name="og:description" content={description}/>
    <meta name="og:image" content={image}/>
</NextHead>
Jeffersonjeffery answered 18/4, 2020 at 3:8 Comment(1)
I really don't understand why, but this actually worksBautzen
C
1

I'm writing this as a reference for everyone, If your running next.js version from 9.5.2 just add the following <Head /> as an example create a document inside your pages dir i.e _document.js that will overwrite a default document provided by next.js like so

_document.js

import Document, { Html, Head, Main, NextScript } from 'next/document';


class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head />  // do this
          <meta charSet="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <link
            rel="stylesheet"
            href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
          
        
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
      
    );
  }
}

export default MyDocument;
Conservatoire answered 29/8, 2020 at 14:37 Comment(0)
T
0

Now NextHead is changed to Head. Just make it like

import Head from 'next/head';
<Head>
    <title>{title}</title>
    <meta name="og:type" content="website"/>
    <meta name="description" content={description}/>
    <meta name="og:title" content={title}/>
    <meta name="description" content={description}/>
    <meta name="keywords" content={keyword}/>
    <meta name="og:url" content={url}/>
    <meta name="og:description" content={description}/>
    <meta name="og:image" content={image}/>
</Head>
Toddy answered 13/7, 2023 at 10:16 Comment(0)
M
0

Just my 2 cents as I spent some time debugging dynamically generated meta tags, specifically for OG attributes. Make sure you're not applying these tags in a <Head> component somewhere on the client side as it seems that they won't get scraped.

In my case I had a useMounted hook that was checking if my Layout component had mounted on the client side to prevent a hydration error, but this was preventing those OG tags from getting picked up even though I was passing the props from a SSG page.

Mesne answered 1/11, 2023 at 12:39 Comment(2)
what next version do you use? 13+?Lambdoid
Yeah correct, at the time I was using v13.2.0Mesne

© 2022 - 2025 — McMap. All rights reserved.