DEV Community

Cover image for Managing Metadata in Next.js 14 for Enhanced SEO and User Experience
Devarshi Shimpi
Devarshi Shimpi

Posted on • Edited on • Originally published at devarshi.dev

Managing Metadata in Next.js 14 for Enhanced SEO and User Experience

Introduction

In Next.js 14, there are two primary ways to manage metadata for your application: using a static metadata object or a dynamic generateMetadata function. Below is a detailed guide on how to utilize both options effectively.

This guide provides an in-depth look at both approaches, helping you understand how to effectively implement metadata in your Next.js application.

Next.js Demo with Metadata Output

In this live example, we'll create a simple Next.js application showcasing the usage of metadata. Then, we'll view the rendered output in the browser and visualise the metadata in action.

We start by creating a new Next.js project with create-next-app

npx create-next-app@latest
Enter fullscreen mode Exit fullscreen mode

Here's the options I chose while creating it.

create-next-app-choices

Add this sample code in the following block and let's preview it in the browser.

Code:

nextjs-metadata-code

Output:

And here's the browser output with get with the tags, keep reading the entire article to see how you could implement in your code.

nextjs-metadata-output

Static Metadata

To define static metadata, you export a Metadata object from a layout.ts or page.ts file using the App Router.

Example:

import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Static Page Title',
  description: 'This is a static page description',
}

export default function Page() {}
Enter fullscreen mode Exit fullscreen mode

This method is suitable when the metadata does not depend on runtime information.

Dynamic Metadata

Dynamic metadata allows you to generate metadata based on dynamic data, such as route parameters or fetched data.

Example:

import { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const id = params.id
  const product = await fetch(`https://api.example.com/products/${id}`).then((res) => res.json())

  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.name,
    description: product.description,
    openGraph: {
      images: [product.imageUrl, ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}
Enter fullscreen mode Exit fullscreen mode

This method is useful when the metadata depends on dynamic data or needs to extend parent metadata.

Key Points

  • Server Components Only: Both metadata and generateMetadata are only supported in Server Components.
  • Single Export Rule: You cannot export both the metadata object and generateMetadata function from the same route segment.

Metadata Fields

Next.js provides a variety of metadata fields to customize the metadata of your application comprehensively.

Title

  • String
  export const metadata: Metadata = {
    title: 'Next.js Application',
  }
Enter fullscreen mode Exit fullscreen mode

Output: <title>Next.js Application</title>

  • Template Object
  export const metadata: Metadata = {
    title: {
      template: '%s | Next.js',
      default: 'Next.js',
    },
  }
Enter fullscreen mode Exit fullscreen mode

Output in child routes: <title>About | Next.js</title>

  • Absolute
  export const metadata: Metadata = {
    title: {
      absolute: 'About Us',
    },
  }
Enter fullscreen mode Exit fullscreen mode

Output: <title>About Us</title>

Description

export const metadata: Metadata = {
  description: 'The React Framework for the Web',
}
Enter fullscreen mode Exit fullscreen mode

Output: <meta name="description" content="The React Framework for the Web" />

Basic Fields

export const metadata: Metadata = {
  generator: 'Next.js',
  applicationName: 'Next.js App',
  keywords: ['Next.js', 'React', 'JavaScript'],
  authors: [{ name: 'Author Name', url: 'https://example.com' }],
}
Enter fullscreen mode Exit fullscreen mode

Output:

<meta name="generator" content="Next.js" />
<meta name="application-name" content="Next.js App" />
<meta name="keywords" content="Next.js,React,JavaScript" />
<meta name="author" content="Author Name" />
<link rel="author" href="https://example.com" />
Enter fullscreen mode Exit fullscreen mode

Open Graph

export const metadata: Metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    url: 'https://nextjs.org',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.org/og.png',
        width: 800,
        height: 600,
      },
    ],
    locale: 'en_US',
    type: 'website',
  },
}
Enter fullscreen mode Exit fullscreen mode

Output:

<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="https://nextjs.org/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
Enter fullscreen mode Exit fullscreen mode

Twitter

export const metadata: Metadata = {
  twitter: {
    card: 'summary_large_image',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    images: ['https://nextjs.org/og.png'],
  },
}
Enter fullscreen mode Exit fullscreen mode

Output:

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
Enter fullscreen mode Exit fullscreen mode

Robots

export const metadata: Metadata = {
  robots: {
    index: false,
    follow: true,
    googleBot: {
      index: true,
      follow: false,
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

Output:

<meta name="robots" content="noindex, follow" />
<meta name="googlebot" content="index, nofollow" />
Enter fullscreen mode Exit fullscreen mode

MetadataBase

metadataBase allows setting a base URL prefix for metadata fields that require a fully qualified URL.

Example:

export const metadata: Metadata = {
  metadataBase: new URL('https://example.com'),
  openGraph: {
    images: '/og-image.png',
  },
}
Enter fullscreen mode Exit fullscreen mode

Output:

<meta property="og:image" content="https://example.com/og-image.png" />
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Use Static Metadata When Possible: If metadata doesn't depend on runtime information, prefer static metadata for simplicity.
  • Dynamic Metadata for Dynamic Data: Use generateMetadata for routes where metadata depends on dynamic data or needs to extend parent metadata.

  • Combine Metadata Fields: Utilize a combination of different metadata fields to ensure comprehensive metadata coverage for SEO and social media integration.

  • Utilize metadataBase: Set a metadataBase in your root layout to simplify URL-based metadata field definitions.

Conclusion

By following these guidelines, you can effectively manage metadata in your Next.js 14 application, enhancing both SEO and user experience.

Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!

Read more

Top comments (0)