7

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

WDP
  • 275
  • 1
  • 5
  • 15

6 Answers6

9

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 */
    </>
  );
}
  • 1
    I know it goes against the whole "everything needs to be declarative" pattern React devs adhere to, but I kinda love this. – corysimmons Feb 07 '22 at 15:35
4

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>
);
Darryl RN
  • 7,432
  • 4
  • 26
  • 46
  • Thank you for your reply. Your first example works well with a static text like "default text". However I would like to find a way to load dynamic data in the `head`. Basically what I want to achieve is; How do I retrieve a page specific title,description text and add it to the ``? I do not understand the second solution. When I console.log children I get undefined. – WDP Sep 28 '19 at 19:24
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);
WDP
  • 275
  • 1
  • 5
  • 15
0
  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>
    </>
       );
    };
    
Zavlagas
  • 1
  • 1
0

I have found, in my opinion, best solution. Create layout that will be global. In Layout component add Head component

   export default function RootLayout({ children}) {
  
    return (
    <>
          <Head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={children.props.description} />
<title>{children.props.title}</title>
</Head>
      {children}                  
   </>
    );
  }

and at the end of any component just add use "getLayoutMethod" and add props for "Head" component.

Component.getLayout = function getLayout(page) {
  return <Layout title="Some title" description="Some description">{page}</Layout>;
};
export default Component;
0

Since Next.js 13, the <Head> solution does not work anymore.

Instead generateMetadata function should be used :

import { Metadata, ResolvingMetadata } from 'next';

type Props = {
  params: { title = 'default text', description = 'default description'  };
};

// set dynamic metadata
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  return {
    title: params.title,
    description: params.description,
  };
}

// page content
export default function Page({ params }: Props) {
  return (
    <></>
  );
}

PS: see here for mor details: https://www.slingacademy.com/article/next-js-how-to-set-page-title-and-meta-description/

Flo
  • 1
  • 2