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

Written by
AuthorDevarshi
Published onJuly 2, 2024

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

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() {}

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) {}

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",
    };
    

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

  • Template Object

    export const metadata: Metadata = {
      title: {
        template: "%s | Next.js",
        default: "Next.js",
      },
    };
    

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

  • Absolute

    export const metadata: Metadata = {
      title: {
        absolute: "About Us",
      },
    };
    

    Output: <title>About Us</title>

Description

export const metadata: Metadata = {
  description: "The React Framework for the Web",
};

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" }],
};

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" />

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",
  },
};

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" />

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"],
  },
};

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" />

Robots

export const metadata: Metadata = {
  robots: {
    index: false,
    follow: true,
    googleBot: {
      index: true,
      follow: false,
    },
  },
};

Output:

<meta name="robots" content="noindex, follow" />
<meta name="googlebot" content="index, nofollow" />

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",
  },
};

Output:

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

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

Share this article: