Next.js: How to Set Page Title and Meta Description

Updated: May 29, 2023 By: Frienzied Flame 2 comments

This article shows you 2 ways to set page titles and meta descriptions in Next.js. The first approach is only available for server components in Next.js 13 and above (with /app directory), while the second one works well with older versions of the framework (with /pages directory).

Using Metadata API

Static title and meta description

To define static metadata, export a Metadata object from a layout.tsx or static page.tsx file:

// app/layout.tsx

import './globals.css';
import { Metadata } from 'next';
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'Sling Academy',
  description:
    'This is a meta description. Welcome to slingacademy.com. Happy coding and have a nice day',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='en'>
      <body className={inter.className}>{children}</body>
    </html>
  );
}

Screenshot:

Dynamic title and meta description

In case you want to use dynamic information to set page title and meta description (for example, use data fetched from an API), use the generatedMetadata() function:

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  // read route params then fetch data

  // return an object
  return {
    title: blogPost.title,
    description: blogPost.description,
  };
}

In the complete and working example below, we will fetch a dummy blog post by its ID from this public API:

https://api.slingacademy.com/v1/sample-data/blog-posts/[id]

The detailed information on this API is described on this page: Sample Blog Posts – Public REST API for Practice.

Let’s see how we implement the generateMetadata() function in action. In the app directory of your project, add a folder named blog-posts, then inside that folder, add another new folder named [id]. Create a file called page.tsx inside the [id] folder.

// app/blog-posts/[id]/page.tsx

import { Metadata, ResolvingMetadata } from 'next';

type Props = {
  params: { id: string };
};

// set dynamic metadata
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  // read route params
  const id = params.id;
  const url = 'https://api.slingacademy.com/v1/sample-data/blog-posts/' + id;

  // fetch data
  const data = await fetch(url).then((res) => res.json());
  const blogPost = data.blog;
  console.log(blogPost);

  return {
    title: blogPost.title,
    description: blogPost.description,
  };
}

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

Now go to http://localhost:3000/blog-posts/2 and see the result (you can change the ID to get another blog post):

Note that you cannot export both the metadata object and generateMetadata() function from the same route segment.

Another important point is that in pages and layouts, you can freely fetch data where it’s used without the need to worry about performance. Next.js will automatically dedupe requests in a tree. For example, it doesn’t matter if you send 2 GET requests to the same API endpoint in the 2 files layout.tsx and page.tsx to get the same data.

Using the Head component

In Next.js, you can set the title, description, and other metadata for a page by using the <Head> component. Import it like this:

import Head from 'next/head'

Static metadata

The code:

// pages/index.js
import Head from 'next/head'

export default function Home(props) {
  return <>
    <Head>
      <title>Sling Academy</title>
      <meta name='description' content='I hope this tutorial is helpful for you' />
    </Head>
    <div style={{ padding: 30 }}>
      <h1>Hello there!</h1>
    </div>
  </>
}

Output:

Dynamic Routes & Dynamic Metadata

You can fetch data from a backend by using getStaticProps, getStaticPaths, or getServerSideProps and use this data to set the page title and description like this:

// pages/[id].js
import Head from 'next/head'

const Product = (props) => {
    const { title, description } = props;
    return <>
        <Head>
            <title>{title}</title>
            <meta name='description' content={description} />
        </Head>

        {/* page content here */}
        <div>
            {content}
        </div>
    </>
}

export default Product

export async function getServerSideProps(context) {
    // Retrieve id
    const { params } = context;
    const id = params.id;

    // Fetch data
    const result = await fetch(`[your API]/${id}`);
    const data = await result.json();

    return {
        props: {
            title: data.title,
            description: data.description,
            content: data.content
        }
    }
}

Note: This isn’t a complete example. You need to make the necessary changes for it to work (use your own API URL, inspect data structure, etc.).

Setting Global Metadata

If you want to set the same metadata for all pages, you can put a component in pages/_app.js like this:

// pages/_app.js

import '../styles/globals.css'
import Head from 'next/head'

function MyApp({ Component, pageProps }) {
  return <>
    <Head>
      <meta name="author" content="John Doe"/>
    </Head>
    <Component {...pageProps} />
  </>
}

export default MyApp

Note: If you have multiple <Head> components on a page and its child components, Next.js will automatically merge their content, and your app will never crash because of conflict. The latest values would be used.

Congratulations! You’ve learned how to use <Head> to set the title and description for a page in Next.js. Happy coding, and have a nice day!

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments