'How can I create dynamic titles in next.js <head> section?

I have a fully functioning website but the head sections doesn't retrieve page specific <title> and <meta description>.

For example, if I'm on the about page, with the title tag "About me" I would like to have "About me" rendered in the <title> tag. What I basically need is to load props, vars or something similar in my Head component that consist of an actual title and description. I've already read about some examples in the official documentation but there are no examples with updating the head with dynamic data.

This is the current code of my component:

import React from 'react';
import NextHead from 'next/head';

const Head = ({ title, description }) => (
    <NextHead>
        <meta charSet="UTF-8" />
        <title>My Website{title}</title>
        <meta name="description" content={description || ''} />
        <meta name="viewport" content="width=device-width, initial-scale=1" key="viewport" />
    </NextHead>
);

I've created a Layout.js component for the posts and pages. The <Head> I described earlier is imported in the layout component.

import React from 'react';
import Head from '../components/Head';


export default ({ children, as, settings = {}, title="default text" }) => (
    <main>
        <Head>
            <title>{title}</title>
        </Head>
        <div className="app__container">
            <div className="row">{children}</div>
        </div>
    </main>
);

I expected that when I'm for example at /work/great-client I would see an updated <title> tag with great-client



Solution 1:[1]

I got it working after trying a little more with the second example.

Little bit more explanation follows in the code below.

Below example code shows Post.js including a shared component <Layout>. In layout i've added and added {props.meta.title}. Now its possible to retrieve dynamic title tags for page/post specific.

const Post = props => {
    const {
        content,
        meta: { title },
    } = props;


    return (
        <Layout>
            <Head>
                <title>{props.meta.title}</title>
            </Head>
            <div className="post">
                <div className="content-inner">
                        <h1>{title}</h1>
                    <article dangerouslySetInnerHTML={{ __html: content }} />
                </div>
            </div>
        </Layout>
    );
};

Post.getInitialProps = function(reqOrContext) {
    const { slug } = reqOrContext.query;
    let content = require(`../work/${slug}.md`);
    const converter = new Converter({ metadata: true });
    content = converter.makeHtml(content);
    const meta = converter.getMetadata();
    return { content, meta };
};

export default withRouter(Post);

Solution 2:[2]

It should be:

import React from 'react';
import Head from '../components/Head';

export default ({ children, as, settings = {}, title="default text" }) => (
    <main>
        <Head title={title}>
        <div className="app__container">
            <div className="row">{children}</div>
        </div>
    </main>
);

If you want to use it as children of Head, then you have to modify your Head component:

import React from 'react';
import NextHead from 'next/head';

const Head = ({ title, description, children }) => (
    <NextHead>
        <meta charSet="UTF-8" />
        {children}
        <meta name="description" content={description || ''} />
        <meta name="viewport" content="width=device-width, initial-scale=1" key="viewport" />
    </NextHead>
);

Solution 3:[3]

I found this solution a bit simpler and with fewer lines of code.

_app.js:

  export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <title>{Component.title}</title>        
      </Head>
      <Layout>
        <Component {...pageProps} />
      </Layout>
    </>
  );
}

Home.js:

Home.title = 'Homepage';
export default function Home() {
  return (
    <>
      /* Some code */
    </>
  );
}

Solution 4:[4]

  1. Create a layout component

  2. Destructure children from props

  3. Add to title {children.type.name}

    console.log(children);
    return (
    <>
      <Head>
        <title>{children.type.name.toLowerCase()}</title>
      </Head>
      <div className="layout">{children}</div>
    </>
       );
    };
    

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 WDP
Solution 2 Darryl RN
Solution 3 Lucian Murmurache
Solution 4 Zavlagas