#Metatags in the <body>

1 messages · Page 1 of 1 (latest)

flint bluff
#

I'm having an issue with the metatags. When I use loading.tsx or template.tsx with a PageWrapper component that uses Suspense, it causes the metatags to be generated inside the <body>.

This issue occurs in pages that use generateMetadata, and within generateMetadata, there's an async fetch to retrieve data from Contentful. If I replace the fetch with a literal object, it works fine.

thorn nexusBOT
#

Hi @flint bluff. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

The behavior you're experiencing—where metatags are rendered inside the <body> instead of the <head>—is a known and intentional result of how Next.js handles streaming metadata when using async data fetching in generateMetadata.

When generateMetadata is asynchronous (e.g., it performs a fetch to Contentful), Next.js may start streaming the page's HTML before the metadata is ready. In this case, the metadata is injected into the body as the page streams, rather than blocking the initial render to wait for the metadata. This is done to improve perceived performance for users. However, if generateMetadata resolves synchronously (such as when you return a literal object), the metadata is placed in the <head> as expected.

This behavior is especially triggered when you use Suspense, loading.tsx, or template.tsx, as these components can cause the UI to stream before metadata is available. The issue is more pronounced in recent Next.js versions (15.2 and above), and is documented and discussed in several issues:

#
  • The streaming metadata behavior is intentional for non-bot visitors to improve performance, but it can cause SEO and Lighthouse issues if metadata appears in the body instead of the head. For bots and crawlers, Next.js tries to ensure metadata is in the head, but this is not always perfect in all cases and versions. Some users have reported SEO issues as a result, especially with canonical tags and social metadata not being recognized by crawlers or Google Search Console when placed in the body [Github Issue 79313][Github Issue 81361][Github Issue 83871].
  • If you want to force metadata to always be rendered in the head (for all user agents), you can set the htmlLimitedBots option in your next.config.js:
    // next.config.js
    module.exports = {
      experimental: {
        htmlLimitedBots: /.*/,
      },
    }
    
    This disables streaming metadata for all user agents, ensuring metadata is always in the head, but may impact performance [Github Issue 79313].
  • The issue is triggered by any async operation in generateMetadata, including fetches. If you remove the async fetch and return static data, the problem disappears [Github Issue 78688].

Summary:
This is expected behavior in recent Next.js versions when using async generateMetadata and streaming. If you need metadata in the head for SEO or other reasons, consider using the htmlLimitedBots workaround, or avoid async operations in generateMetadata where possible. Be aware that this may have performance trade-offs.

#

If you need more details or want to follow updates, see the official documentation on streaming metadata and the linked issues above.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@flint bluff Kapa.ai is still learning and improving, please let me know how I did by reacting below.