Fixing Duplicate Meta Tag Issue in Next.js
Adam C. |

Introduction

As a Next.js developer, optimizing your web pages' SEO to ensure they rank well in search engine results is essential. However, you may encounter an issue where Next.js renders duplicate meta tags, causing potential SEO problems. In this blog post, we will explore the problem and discuss a solution to fix it when using NextFeathers, a popular Next.js setup.

Photo by Stefan Cosma on Unsplash

The Issue 

Recently, I discovered a situation where the page meta tags in the HTML header were duplicated in my Next.js project, which was set up using NextFeathers. For instance, I noticed two <meta charset="utf-8"> tags on the page header. To make matters worse, when the page was rendered on the client side after a router change, the <meta description> tag was appended to the header instead of replacing the existing one, resulting in duplicated page description headers. 

This issue may not have a significant impact on SEO, as the meta tags render correctly when the page is initially loaded from the server. This might explain why I had not encountered this issue while using NextFeathers for many years. However, it is still crucial to address this problem to ensure consistent rendering and maintain best practices in Next.js development.

Research

To resolve the problem, I conducted thorough research and discovered a crucial aspect I had overlooked. In the Next.js documentation, it states that adding a key to the <Meta> tag created in the next/head component is essential to avoid duplication. The key property ensures that the tag is only rendered once. 

But this did not resolve the duplication problem in my specific case. The issue arose when transitioning from one page to another, where the previous page was unmounted before the new page was mounted. Despite adding a key, the meta tag from the previous page persisted, and the new meta tag did not replace it consistently. This inconsistency was observed across different browsers, such as Brave and Chrome.

Uncovering the Root Cause

After thorough analysis, I have discovered a previously undocumented constraint that sheds light on the root cause of the issue: "tags in pages cannot be in child components." This constraint implies that when rendering meta tags within Next.js pages, they should not be placed within child components. However, this limitation is not explicitly mentioned in the official Next.js documentation, which has led to confusion and difficulty in resolving the duplicate meta tag problem.

To gain a better understanding, let's review the structure I had previously. In this structure, each page (page.js) renders its content within the Layout.js component. The Layout.js component, in turn, includes the Meta.js component, which is responsible for setting up the page header.

Example code for page.js:

const seoData = {
  title,
  desc,
  summary,
  canonical,
  image,
};

return (
  <Layout seoData={seoData}>
    <Container text>
      {/* page content */}
    </Container>
  </Layout>
);

Example code for Layout.js:

return (
  <div id="deniApps">
    <Meta
      title={title}
      desc={desc}
      canonical={canonical}
      pageURL={pageURL}
      image={image}
      css={css}
      js={js}
      noAd={props.noAd || router.query.vip}
    />
    <Header />
    {children}
    <Footer />
  </div>
);

Example code for Meta.js:

import Head from "next/head";

<Head>
  <title>{props.title || defaultTitle}</title>
  <meta name="description" content={props.desc || defaultDesc} />
  <meta property="og:type" content="website" />
  <meta
    name="og:title"
    property="og:title"
    content={props.title || defaultTitle}
  />
  <meta
    name="og:description"
    property="og:description"
    content={props.desc || defaultDesc}
  />
  {/* other meta tags */}
</Head>

Based on this structure, it appears that the Meta.js component is rendered within the Layout.js component, which is included on every page. However, as mentioned earlier, the issue of duplicate meta tags can arise due to the constraint that "tags in pages cannot be in child components."

To address this issue, you can modify the code structure by removing the Meta.js component from the Layout.js component and placing it directly within each individual page (page.js). This change ensures that the meta tags are not rendered within a child component, allowing for consistent rendering and preventing duplication.

Here's an updated code structure for your reference:

Example code for page.js:(updated)

const seoData = {
  title,
  desc,
  summary,
  canonical,
  image,
};

return (
  <Layout>
    <Meta
      title={seoData.title}
      desc={seoData.desc}
      canonical={seoData.canonical}
      pageURL={pageURL}
      image={seoData.image}
    />
    <Container text>
      {/* page content */}
    </Container>
  </Layout>
);

By making this adjustment, we ensure that the Meta.js component is placed directly within each page, avoiding the constraint of tags in pages not being allowed in child components. This modification resolved the issue of duplicate meta tags and ensure consistent rendering.

I would like to express my sincere gratitude to Sean W for providing crucial insight regarding the constraint in Next.js: "Tags in pages cannot be in child components."