Routing and SEO Metadata in Next.js 13
Tags: webdev, javascript, nextjs, react
Published: 2023-08-02 12:07:05

Written by [Vishwas Gopinath](https://twitter.com/CodevolutionWeb)

When it comes to building web applications, ensuring proper search engine optimization (SEO) and web shareability is crucial for increasing visibility and attracting users. In version 13, Next.js introduced the Metadata API, which allows you to define metadata for each page, ensuring accurate and relevant information is displayed when your pages are shared or indexed. In this blog post, we will explore how to leverage the Metadata API to enhance routing metadata.

## Importance of routing metadata

When users navigate to different pages within your Next.js application, it's important to provide, at the very least, appropriate document titles and descriptions. By default, the root layout of a Next.js application created using `create-next-app` includes a metadata object in the `layout.tsx` file.

```ts
// app/layout.tsx
export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}
```

We will learn about the metadata object in a minute but it is quite clear that the metadata object contains a `title` property set to "Create Next App" and a `description` property set to “Generated by create next app”. The `title` property determines the document title displayed in the browser, while the `description` property corresponds to the meta tag in the head section of the document.

However, this predefined metadata might not provide the desired SEO benefits for individual pages in your application. The same document title and description are set regardless of the visited route.

If the landing page is titled “Builder.io - Visual Headless CMS”, it is carried across to the documentation page, the careers page, the pricing page, and every other page in the application. From the perspective of search engines, having appropriate document titles and descriptions for each page is essential for increasing the chances of users discovering your website.

## Configuring metadata in relation to routing

There are two ways to configure metadata in a `layout.tsx` or `page.tsx` file:

1. Export a static `metadata` object or
2. Export a dynamic `generateMetadata` function

### Static metadata

To define static metadata, export a `Metadata` object from a `layout.tsx` or `page.tsx` file. Here’s an example `metadata` object defined in `app/page.tsx`:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Builder.io - Visual Headless CMS',
  description: 'Build digital experiences for any tech stack, visually.',
}
 
export default function Home() {
  // Home component
}
```

When you navigate to `localhost:3000` and inspect the head section of the document, you should see:

```ts
<title>Next.js</title>
<meta name="description" content="Build digital experiences for any tech stack, visually.">
```

### Dynamic metadata

Dynamic metadata depends on dynamic information, such as the current route parameters, external data, or `metadata` in parent segments.

To define dynamic metadata, export a `generateMetadata` function that returns a `Metadata` object from a `layout.tsx` or `page.tsx` file.

Here’s the `generateMetadata` function defined in `app/products/[productId]/page.tsx`:

```ts
import { Metadata } from "next";

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

export const generateMetadata = ({ params }: Props): Metadata => {
  return {
    title: `Product - ${params.productId}`,
  };
};

export default function ProductDetails() {
  // Product details component
}
```

When you navigate to `localhost:3000/products/camera` and inspect the head section of the document, you should get:

```ts
<title>Product - Camera</title>
```

And here’s an async `generateMetadata` function when we need external data:

```ts
import { Metadata } from "next";

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

export const generateMetadata = async (
  props: Props
): Promise<Metadata> => {
  const { params } = props
  const product = await fetchProductById(params.productId)
  return {
    title: product.title,
  };
};
```

`generateMetadata` accepts a `props` parameter, which is an object containing the parameters of the current route. The `props` object contains two properties:

1. `params`: An object that contains the dynamic route parameters from the root segment down to the segment where `generateMetadata` is called. For example, if the route is `app/products/[productId]/page.js` and the URL is `/products/1`, the `params` object would be `{ productId: '1' }`.
2. `searchParams`: An object that contains the current URL's search parameters. For instance, if the URL is `/products?productId=1`, the `searchParams` object would be `{ a: '1' }`.

The presented example uses `params`, but you can also use `searchParams` with equal ease.

If the metadata doesn't depend on runtime information, it should be defined using the static `metadata` object rather than `generateMetadata` function.

## Merging metadata

Metadata can be exported from both a `layout.tsx` file and a `page.tsx` file. Consequently, there may be multiple metadata exports for various segments within the same route.

In such cases, the metadata objects are combined to generate the final metadata output of a route. Duplicate keys are replaced starting from the root segment down to the segment closest to the final `page.js` segment.

Let's consider `app/layout.tsx` with the following metadata export:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Builder.io - Visual Headless CMS',
  description: 'Build digital experiences for any tech stack, visually.',
}
```

And `app/blog/page.tsx` with the following metadata export:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Builder.io Blog'
}
```

When you visit `localhost:3000/blog`, the resulting metadata output is as follows:

```ts
<title>Builder.io Blog</title>
<meta name="description" content="Build digital experiences for any tech stack, visually.">
```

In this case, the `title` from the root layout is overwritten by the title in the `page.tsx` file within the blog folder. However, the description from the root layout is present in the merged metadata object, resulting in the blog page also containing the same description.

To configure metadata in relation to routing, there are a few important points to note:

1. Metadata can be exported from both a `layout.tsx` file and a `page.tsx` file. Metadata defined in a layout applies to all pages within that layout, while metadata defined in a page only applies to that specific page.
2. Metadata is evaluated in order, starting from the root segment down to the segment closest to the final `page.js` segment.
3. Metadata objects exported from multiple segments within the same route are merged to form the final metadata output of a route. Duplicate keys are replaced based on their ordering. In other words, metadata in a page overwrites similar metadata in a layout.

By default, the metadata object is generated in the root layout when creating a Next.js app. However, you have the flexibility to define the metadata object in both the layout and the page, with the page metadata taking precedence over the layout metadata.

## `title` field

Among the numerous metadata fields that can be specified, there is one field that holds significant importance, particularly from a routing perspective. This field is none other than the `title` field. Its primary purpose is to define the title of the document. It can be defined as a simple string or an optional template object.

### String

The most direct way to set the title attribute is by using a string. For example, in the file `layout.tsx` or `page.tsx`, you can define the title as follows:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Builder.io - Visual Headless CMS'
}
```

This outputs the following HTML code in the `head` section:

```ts
<title>Builder.io - Visual Headless CMS</title>
```

### Template object

Next.js also allows you to define the title field using a template object, which provides more flexibility. For example, in the file `layout.tsx` or `page.tsx`, you can define the title as follows:

```ts
import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '...',
    default: '...',
    absolute: '...',
  },
}
```

- The `template` property in the `title` object can be used to add a prefix or a suffix to titles defined in child route segments.
- The `default` property is used as a fallback title for child route segments that don't define a title.
- The `absolute` property, if defined, overrides the `template` set in parent segments.

### Default `title`

The `title.default` property comes in handy when you want to provide a fallback title for child route segments that don't explicitly define a title.

For example, in the `app/layout.tsx` file, you can set the default title as follows:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    default: 'Builder.io - Visual Headless CMS',
  },
}
```

If a child route segment, such as `app/blog/page.tsx`, doesn't have a title defined, it will fall back to the default title. So the output will be:

```ts
<title>'Builder.io - Visual Headless CMS'</title>
```

### Template `title`

To create dynamic titles by adding a prefix or a suffix, you can use the `title.template` property. This property applies to child route segments and not the segment in which it's defined.

In the `app/layout.tsx` file, define the metadata as follows:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | Builder.io',
    default: 'Builder.io - Visual Headless CMS',
  },
}
```

In the `app/blog/page.tsx` file, you can then define the title as usual:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Blog',
}
```

The output is:

```ts
<title>Blog | Builder.io</title>
```

### Absolute `title`

If you want to provide a title that completely ignores the `title.template` set in parent segments, you can use the `title.absolute` property.

In the `app/layout.tsx` file, define the metadata as follows:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | Builder.io',
    default: 'Builder.io - Visual Headless CMS',
  },
}
```

In the `app/blog/page.tsx` file, define the title as follows:

```ts
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    absolute: 'Blog',
  },
}
```

The output will be:

```ts
<title>Blog</title>
```

Let’s summarize the behavior of the title field.

In the `layout.js` file:

- The `title` (as a string) and `title.default` are used to set a default title for child sections that don't have their own title. If a child section doesn't have a title, it will inherit the title template from the nearest parent section and use it as a basis.
- The `title.absolute` is also used to set a default title for child sections, but it ignores the title template set in parent sections.

In the `page.js` file:

- If a page doesn't have its own title, it will use the title of the nearest parent section.
- The `title` (as a string) is used to define the title for the specific page. Similar to child sections, if a page doesn't have a title, it will inherit the title template from the nearest parent section and use it as a basis.
- The `title.absolute` is used to define the title for the specific page, and it ignores the title template set in parent sections.
- The `title.template` doesn't have any effect in `page.js` because a page is always the final section of a route and doesn't have any child sections.

### Conclusion

Next.js provides powerful tools for managing routing metadata, enabling you to optimize your application for SEO and web shareability.

By leveraging the Metadata API, we can define metadata for each page, ensuring accurate and relevant information is displayed when pages are shared or indexed.

Configuring metadata in relation to routing is crucial for optimizing individual pages. We can use static metadata or dynamic metadata, depending on the specific needs of our application. Static metadata allows us to define metadata objects directly, while dynamic metadata relies on dynamic information.

Merging metadata enables us to combine metadata from different segments within the same route, resulting in the final metadata output. This allows for granular control over metadata at different levels of the application.

The `title` field can be defined as a string or a template object, providing flexibility in setting the document title. The template object allows for dynamic titles with prefixes or suffixes, default titles for segments without explicitly defined titles, and absolute titles that override parent templates.

By understanding and utilizing the Metadata API in Next.js, we can optimize our web applications for search engines, improve web shareability, and provide a better user experience.

 ![](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F83677fb912dd4a539a3f3bb5c0c82de1?width=405)<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F77cc5249f71948568195e7314bcc5766%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=77cc5249f71948568195e7314bcc5766&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>

Visually build with your components

[Builder.io](https://www.builder.io/m/developers) is a headless CMS that lets you [drag and drop](https://www.builder.io/m/products) with [**your** components](https://www.builder.io/m/developers) right within your existing site.

[Try it out](https://builder.io/signup) [Learn more](/m/developers) 
```ts
// Dynamically render your components
export function MyPage({ json }) {
  return <BuilderComponent content={json} />
}

registerComponents([MyHero, MyProducts])
```

##### _Read the [full post](https://www.builder.io/blog/routing-seo-metadata-nextjs-13) on the [Builder.io blog](https://www.builder.io/blog)_