Trendy Coder Logo
  • Home
  • About
  • Blog
  • Newsletter

Top 5 SEO Optimization Tips in Next.js 14

Posted on: June 11 2024
By Dave Becker
Hero image for Top 5 SEO Optimization Tips in Next.js 14

Since the original release of Next.js there have been some major version changes and improvements to the framework in the area of SEO optimization.

After the release of Next.js 13 there has been a lot of focus to support React Server Components (RSC). In this article we'll take a look at the top 5 SEO optimization tips for Next.js, including:

  • Improved page title and templates
  • Simplified SEO Metadata support
  • Social Media Optimization with Open Graph
  • Static image optimization
  • Sitemap generation

Read on to learn about these essential common tips and best practices for SEO optimization in Next.js.

What is Next.js SEO optimization?

Next.js has updated its SEO optimization API which has been revised to be more in alignment with React Server Components (RSC).

As writing Server Side Rendered (SSR) code becomes much easier with RSC in Next.js, the SEO API is now also RSC.

Why is Next.js SEO optimization important?

The new framework API changes make it very simple to provide extensive coverage for page SEO optimization, including:

  • Image caching
  • Simplified Static Site Generation (SSG)
  • Dynamic page titles with templating
  • Full metadata coverage
  • Reduce Content Layout Shifting (CLS)
  • Better web presence on social platforms
  • and more

The good news is that the new API changes provides these capabilities. So you can focus on development rather than building your own API or third party library to do the job.

Tip 1 - Page Titles

Page titles and descriptions are probably one of the primary ways that a website can convey the intent of the page content to the user as well as search engines.

Why use page titles?

The primary reasons to use unique page titles are:

  • Provides meaningful bookmark titles for users
  • Represents the primary topic of your page content for users and search engines
  • If you're blogging, the title should match your opening <h1> tag

How to add page titles in Next.js

Next.js has changed the way it sets page titles. It used to dynamically hoist them on the client side but now they must be added on RSC components.

Consider the following project structure with a root layout, nested product layout and a product page.tsx.

 your-project
  └── app
      ├── product
      │   ├── page.tsx
      │   └── layout.tsx
      └── layout.tsx (root layout)
  

Using the new <Head> component

The first change that should be made is to switch to the new <Head> component from next/head in the root layout, this way Next.js can manage the head tag output.

You can include a <title> tag as default base title in the root layout.


import Head from 'next/head';

export const RootLayout = ({ children }) => {
  return (
    <html lang="en">
      <Head>
        <title>ACME Corp</title>
        {/* define any global meta tags here */}
      </Head>
      <body>
        {children}
      </body>
    </html>
  );
};

export default RootLayout;


Exporting Metadata Information

There are two options for adding page titles.

  • Use the Metadata title only
  • Use the Metadata template with a placeholder

You can add titles by simply exporting a named metadata object from any RSC component modules, including:

  • layout.tsx
  • page.tsx (RSC only)

Next.js will compute the title based on every nested child metadata export it finds on route segments.


Using Metadata title only

To simply add just a page title, you can just set the title field to a string value.

Title only metadata
export const metadata: Metadata = {
  title: 'ACME Corp'
}

Using a Metadata template

To create a template, simply set the title field to an object with the following fields.

  • template: a string with a placehoder %s for the page.
  • default: a default title if nested pages don't provide a unique title.

Using a template allows you to still keep your website name in the title including the more specific page title.

Metadata with template
export const metadata: Metadata = {
  title: {
    template: '%s | ACME Corp',
    default: 'ACME Corp'
  }
}

Let's add a template export for the product layout so that all the product pages can use the same template.

import { Metadata } from 'next';

export const metadata: Metadata = {
  title: {
    template: '%s | ACME Corp',
    default: 'ACME Corp'
  }
}

Each nested product page that inherits the layout template metadata from its parent route segment, can specify a unique page title and description.


import { Metadata } from 'next';

export const metadata: Metadata = {
  title: "Product Page",
  description: "Some product page description"
}


If you access the /product route, the title and description in the browser would read.

Product Page | ACME Corp

Some product page description

You can have as many nested metadata exports as needed. Next.js will use the closest nested parent template in the route segments path to build the title.

A couple tips on RSC components:

  • Export templates on layout.tsx and treat them like hubs for many pages the same relevent content.
  • Export title and description metadata on page.tsx RSC components.

If your page.tsx uses the use client indicator, you may need to rethink how you'll export the metadata title and description since it can only be added on RSC components.

It's usually better to have page.tsx as an RSC and break the client code into smaller nested client components if possible.


Tip 2 - Optimize HTML Metadata Tags

<meta> tags, also known as Metadata in the Next.js framework, are tags that are added to your page to further inform search engines about your content, including:

  • keywords: A space delimited string of keywords.
  • category: The category meta name property.
  • icons: The icons for the document. Defaults to rel="icon".
  • robots: The robots setting for the document.
  • viewport: The viewport setting for the document.
  • authors: a list of authors for a paritculuar page.

These are just a few of the basic meta tags from the Metadata API but the API has very thorough coverage for just about all of the tags you'll need for SEO optimization.

Why do I need a metadata tags?

Metadata tags provide information which is used by search engines to indicate the type of content on a web page.

Utimately, the actual content will determine how it ranks on a search engine's results page but the meta tags should also be added to support the type of content on your page.

How to add metadata in Next.js

All of the <head> tags can be set by exporting the generateMetadata function and returning the Metadata object. This is the same Metadata object used to define the title and template as we saw earlier.

The generateMetadata function is used when you need to gather meta information that might be fetched async.

For example, let's say we add a details page that has a product id=1234 param and fetches the meta information.

sample JSON response
 {
  "id": "1234",
  "title": "Product 1234",
  "description": "Product 1234 description",
  "author": {
    "url": "example.com/url/to/bio/lc-bio",
    "name": "Lloyd Christmas"
  },
  "keywords": ["nextjs, SEO"],
  "category": "SEO Optimization",
  "icons": [
    "https://example.com/icon.png"
  ]
 }


import { Metadata } from 'next';

export async function generateMetadata({
  params: { id },
}: {
  params: { id: string };
}): Promise<Metadata> {
  const { title, description, author, keywords, category, icons } = (await fetch(
    `/api/product/detail/${id}`
  )) as any;

  return {
    title,
    description,
    authors: [author],
    keywords,
    category,
    icons: icons.map((url) => { rel: "icon", url }),
    /*
    robots might not come from product info but could 
    be set for all product pages since the page uses an [id] slug
    */
    robots: { index: true, follow: true },
    /*
    viewport could just be set at the root layout but 
    to demonstrate how it can be set for any page.
    */
    viewport: {
      width: 'device-width',
      initialScale: '1.0'
    }
  };
}

function Page({ params: { id } }: { params: { id: string } }) {
  return (
    <section>
      <h1>Product Details</h1>
    </section>
  );
}

export default Page;


Accessing the new app route /product/detail/1234 will generate the following HTML tags in the head.

<head>
  <title>Product 1234 | ACME Corp</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="Product 1234 description" />
  <meta name="keywords" content="nextjs, SEO" />
  <meta name="category" content="SEO Optimization" />
  <meta name="author" content="Lloyd Christmas" />
  <link rel="author" href="example.com/url/to/bio/lc-bio"></link>
  <link rel="icon" href="https://example.com/icon.png" />
  <meta name="robots" content="index, follow" />
</head>

Using the Metadata object makes adding a single or an array of tags using object notation very easy.

Tip 3 - Use Open Graph meta tags

The Open Graph protocol was created by facebook to standardize the use of meta tags to improve the way links are shared on presented on social media.

X (Twitter) has its own set of meta tags to control the way card posts are displayed but will default to Open Graph meta tags if X meta tags are not present.

By defining some simple <meta> tags, any website can greatly improve and optimize how their URL links are displayed.

Why do I need Open Graph meta tags?

Your web page content or landing page Ads might not display as expected on these platforms without these tags.

The Open Graph meta tags allow for a rich user experience and will draw more attention and trust with users on social media.

This is really essential for promotional advertisements or even blog articles but in general it's good practice and easy to do on Next.js using the Metadata object.

For example, here's what a post with Open Graph meta tags would look like.

Image showing a rich social media card using Open Graph meta tags

And here's an example without Open Graph.

Image showing a bad social media card when not using Open Graph meta tags

Without these tags, social media platforms will just use the standard meta tags and probably won't display as you would expect.

How to add Open Graph meta tags in Next.js

The great thing about the new API is that everything is built into the Metadata object, including Open Graph support.

So now we can build on the previous meta tags and add some Open Graph tags to the example.

Here's a revised version of generateMetadata function we created earlier.


import { Metadata } from 'next';

export async function generateMetadata({
  params: { id },
}: {
  params: { id: string };
}): Promise<Metadata> {

  const siteUrl = "https://acme.com";
  const siteName = "ACME";

  const { title, description, author, keywords, category, icons } = (await fetch(
    `/api/product/detail/${id}`
  )) as any;
  return {
    title,
    description,
    authors: [author],
    keywords,
    category,
    icons: icons.map((url) => { rel: "icon", url }),
    robots: { index: true, follow: true },
    viewport: {
      width: 'device-width',
      initialScale: '1.0'
    }

    // adding Open Graph and X/Twitter specific tags here
    openGraph: {
      type: 'website',
      title,
      description,
      url: siteUrl,
      siteName: siteName,
    },
    twitter: {
      title,
      description,
      creator: author?.name,
      card: 'summary_large_image',
      site: siteName,
    },
  };
}

function Page({ params: { id } }: { params: { id: string } }) {
  return (
    <section>
      <h1>Product Details</h1>
    </section>
  );
}

export default Page;


By addding these additional fields to the Metadata object response, it will generate the following tags in the <head>, assuming we are using the same JSON sample product data.


Generated Open Graph and Twitter meta tags
<head>
  <title>Product 1234 | ACME Corp</title>
  <meta name="description" content="Product 1234 description" />
  <meta name="keywords" content="nextjs, SEO" />
  <meta name="category" content="SEO Optimization" />
  <meta name="author" content="Lloyd Christmas" />
  <link rel="author" href="example.com/url/to/bio/lc-bio"></link>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="icon" href="https://example.com/icon.png" />
  <meta name="robots" content="index, follow" />

  <!-- Open Graph and X (Twitter) here -->
  <meta property="og:title" content="Product 1234 | ACME Corp">
  <meta property="og:description" content="Product 1234 description">
  <meta property="og:url" content="https://acme.com">
  <meta property="og:site_name" content="ACME Corp">
  <meta property="og:type" content="website">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="ACME Corp">
  <meta name="twitter:creator" content="Lloyd Christmas">
  <meta name="twitter:title" content="Product 1234 | ACME Corp">
  <meta name="twitter:description" content="Product 1234 description">
</head>

The Open Graph and Twitter types in Next.js are quite extensive, so I've narrowed it down to the bare minimum you'll need.

To get more information on these types, you can inspect the Metadata module and see the metadata-interface.d.ts file that opens in your IDE.

The Metadata object can even generate arbitrary <meta> tags by using the other object key.

For example:

Adding arbitrary meta tags

export function generateMetadata(): Metadata {
  return {
     other: {
        'some:arbitrary:key': 'SEO Optimization'
     }
  }
}


Generated arbitrary meta tag
<head>
  <meta name="some:arbitrary:key" content="SEO Optimization">
</head>

Tip 4 - Use a Sitemap

A sitemap.xml file is what search engines will use as an index to discover all of the available public pages that you want the search engine bots to crawl and parse.

Here's a snippet from the Next.js docs of what a sitemap.xml file looks like.


<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://acme.com</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
    <changefreq>yearly</changefreq>
    <priority>1</priority>
  </url>
  <url>
    <loc>https://acme.com/about</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://acme.com/blog</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.5</priority>
  </url>
</urlset>

Here's what these fields represent:

  • loc: Url to access the page.
  • lastmod: Used to indicate when the file was last modified.
  • changefreq: This field represents the change frequency of modifications.
  • priority: This is the imporance of the url compared to the other urls on the site and it ranges from 0.0 - 1.0.

Most of the research says lastmod, changefreq and priority are most likely ignored by search engines since they might not be properly maintained by Webmasters.

As long as url and lastmod are added to this list is probably good enough.

Why do I need a sitemap?

As search engine bots crawl your website they can only crawl to other pages if there is a relative link from one page to the other page. However, this is not always the case and many pages will only be accessible by direct link only.

So a sitemap.xml file serves as a complete index of all the available URL's you want the bot to crawl and parse.

It can contain up to 50,000 URL entries but if you need more you can generate multiple sitemap.xml pages or have nested sitemap.xml files in subdirectories.

How to create a sitemap in Next.js

Next.js provides the sitemap.ts file and will call the default exported function at build time and automatically generate a sitemap.xml.

You can have multiple sitemaps.ts at any route segment but in most cases, placing one at the app root is usually sufficient enough for most websites.

Here's a simple example of how a blog site might generate a sitmap.xml file.


import { MetadataRoute } from 'next';
import { getPosts } from '@/lib/blog/posts';
import moment from 'moment';

export default async function sitemap(): MetadataRoute.Sitemap {
    const siteUrl = "https://acme.com";

    const posts = await getPosts().map((post) => {
      const {slug, dateModified} = post;
      return {
        url: `${siteUrl}/blog/${slug}`,
        dateModified: moment(`${dateModified}`).toDate()
      };
    });
    return [
      {
        url: `${siteUrl}/dashboard`,
        lastmod: moment().toDate()
      },
      {
        url: `${siteUrl}/resources`
        lastmod: moment().toDate()
      },
      {
        url: `${siteUrl}/blog`
        lastmod: moment().toDate()
      },
      ...posts
    ];
}


And the output for the sitemap.xml would be as follows.

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://acme.com/dashboard</loc>
    <lastmod>2024-03-15T07:52:57.880Z</lastmod>
  </url>
  <url>
    <loc>https://acme.com/resources</loc>
    <lastmod>2024-03-15T07:52:57.880Z</lastmod>
  </url>
  <url>
    <loc>https://acme.com/blog</loc>
    <lastmod>2024-03-15T07:52:57.880Z</lastmod>
  </url>
  <url>
    <loc>https://acme.com/blog/blog_1</loc>
    <lastmod>2024-03-15T07:52:57.880Z</lastmod>
  </url>
  <url>
    <loc>https://acme.com/blog/blog_2</loc>
    <lastmod>2024-03-28T19:24:55.853Z</lastmod>
  </url>
</urlset>


Tip 5 - Use Next.js Image API

In the latest version of Next.js they have provided a finalized version of their <Image> API tag, making it very easy to optimize image resources.

Images are no doubt the primary focal point of websites but can also come with a performance impact.

If not used in an optimized way they can also have an unpleasent end user experience with content shifting around during loading.

Slow page load times can also have an impact on SEO page ranking. The longer it takes your page to load, the lower it might be ranked on a search engine results page (SERP).

Why do I need Next.js Image API?

The <Image> component from the next/image API provides some benefits, including:

  • Image caching at build time.
  • Improved Cumulative Layout Shift (CLS) (a.k.a Content Layout Shift) which greatly reduces page shift during image loading.
  • Optimized image content types like image/avif and image/webp.
  • Remote image resource loaders.
  • Accessibility tag improvements.
  • and more, just to name a few.

How to create a Next.js Image

To use the <Image> component, simply import from the next/image and also import the local image from your public directory.

For this example I'll just import a local hero background image.

  • Dimension: 1200 x 800
  • File Size: 63KB
  • Content type: image/jpg.

I'm passing the following props to the <Image> component:

  • src: A local import image.
  • alt: Requires an alt attribute for Accessibility.
  • width: Requires a width of the image in pixels, unless the image is local static import.
  • height: Requires a height of the image in pixels, unless the image is a local static import.

The width and height should be used to set the optimal dimensions of the image you want. Next.js will generate an optimized image based on these settings.

You can still use classes and styles for styling to fit the web page layout.

import Image from 'next/image';
import HeroImage from '@/public/demo/home-decor-bg.jpg';

function Page() {
  return (
    <div>
      <Image
        src={HeroImage}
        width={1200}
        height={800}
        alt="Image of SEO optimize image using Next.js image API"
        className="w-full"
      />
    </div>
  );
}

export default Page;


The image will now be cached automatically and optimized and served through an API endpoint.

If you open a Chrome inspector network tab, you'll see that the image is now being loaded from a reserved internal Next.js endpoint for images.

localhost:3000/_next/image

Image of Chrome inspector network tab showing performance benefits of using Next.js Image component

After using the <Image> component, we can now see the following details:

  • Dimension: 1200 x 800
  • File Size: 36KB
  • Content type: image/webp.

So the file size has been reduced to half of its original size and the image content type is now image/webp and fully cached.

The file size is optimized and will greatly reduce layout CLS shifting when loading.

You can imagine how larger background or banner images will benefit from this new feature.

Conclusion

Now that we've examined some of the new features in Next.js for SEO optimization and performance, I hope you can apply them to your project for some huge benefits.

Just remember that performance is an essential part of SEO optimization among other factors.

As React web application make their migration to being mostly server side rendered (SSR) or even statically render (SSG), we'll definitely see more great features from the Next.js team to accelerate web application development.

Topics

SEOLinuxSecuritySSHEmail MarketingMore posts...

Related Posts

Hero image for How does Astro Content Framework support SEO?
Posted on: July 10 2024
By Dave Becker
How does Astro Content Framework support SEO?
Hero image for Uploading Made Easy: Exploring Media Features in Payload CMS
Posted on: September 23 2025
By Dave Becker
Uploading Made Easy: Exploring Media Features in Payload CMS
Hero image for Upgrading to Tailwind CSS 4: A Quick Guide
Posted on: September 23 2025
By Dave Becker
Upgrading to Tailwind CSS 4: A Quick Guide
Hero image for Build a Blog with MDX and Next.js
Posted on: May 24 2024
By Dave Becker
Build a Blog with MDX and Next.js
Hero image for A Visual Guide for Responsive Design Using Tailwind CSS?
Posted on: July 19 2024
By Dave Becker
A Visual Guide for Responsive Design Using Tailwind CSS?
Hero image for Next.js Environment Variables: Quick Start Guide
Posted on: September 01 2025
By Dave Becker
Next.js Environment Variables: Quick Start Guide
Hero image for Top Tailwind CSS Plugins and Resources
Posted on: August 01 2024
By Dave Becker
Top Tailwind CSS Plugins and Resources
Hero image for Building React Components with Variants Using Tailwind CSS
Posted on: June 11 2024
By Dave Becker
Building React Components with Variants Using Tailwind CSS
Trendy Coder Logo
Resources
  • Blog
Website
  • Home
  • About us
Subscribe

Get the latest news and articles to your inbox periodically.

We respect your email privacy

© TrendyCoder.com. All rights reserved.