-
Notifications
You must be signed in to change notification settings - Fork 29.6k
fix(84750): await metadata promises to ensure proper head placement #84976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
robbchar
wants to merge
8
commits into
vercel:canary
Choose a base branch
from
robbchar:fix/metadata-head-placement-issue-84750
base: canary
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
fix(84750): await metadata promises to ensure proper head placement #84976
robbchar
wants to merge
8
commits into
vercel:canary
from
robbchar:fix/metadata-head-placement-issue-84750
+67
−97
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Fixes vercel#84750 Made Metadata() functions async and await the metadata promise before rendering. This ensures metadata is resolved during initial SSR rather than being deferred via Suspense/Flight data, allowing React's server-side hoisting to correctly place all meta tags in <head> instead of <body>. Root cause: When metadata goes through Suspense boundaries, it's sent via RSC Flight data after the initial HTML stream has already sent <head>. React cannot hoist backwards in a stream, so tags end up in <body>. This fix: - Forces metadata resolution before streaming begins - Metadata becomes part of initial SSR HTML where hoisting works - Removes need for @ts-expect-error comments (proper async/await pattern) Trade-off: Response blocks on metadata resolution (~milliseconds), but this is acceptable since metadata is invisible and correctness (SEO, social sharing) is more critical than micro-optimizations.
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
a13eea7
to
1e900de
Compare
Updates test expectations to match the fix where metadata now renders in <head> instead of <body>. Changes: - metadata-streaming.test.ts: Update all tests to expect metadata in head - metadata-streaming-static-generation.test.ts: Update dev and dynamic tests These tests were previously checking that metadata was incorrectly placed in <body>, which was the bug. Now they correctly verify that all metadata tags are in <head> for proper SEO and social sharing.
ok updated tests:
|
…etadata durting the initial SSR phase ### Changes **Core Fix:** - `packages/next/src/lib/metadata/metadata.tsx`: - Removed conditional logic based on `serveStreamingMetadata` - Always use async components that await metadata resolution - Removed `Suspense` wrapper from `MetadataOutlet` - Added explanatory comment about why metadata must be awaited **Test Updates:** Updated tests to expect the new correct behavior (metadata in `<head>`): - `test/e2e/app-dir/metadata-streaming/metadata-streaming-customized-rule.test.ts` - `test/e2e/app-dir/metadata-streaming-parallel-routes/metadata-streaming-parallel-routes.test.ts` - `test/e2e/app-dir/metadata-static-generation/metadata-static-generation.test.ts` - `test/e2e/app-dir/metadata-icons/metadata-icons.test.ts` ### Trade-offs - Response may be delayed while metadata resolves (typically <1ms for static metadata) - Metadata is invisible, so no perceived user impact - Correctness (SEO, social, HTML validity) > microsecond optimization ### Breaking Changes - The `htmlLimitedBots` configuration now has reduced effect since all requests get blocking metadata - Streaming metadata feature is effectively disabled for initial page loads ### References - Original issue: vercel#84750 - Root cause analysis in ai-docs/FINAL_ROOT_CAUSE_ANALYSIS.md
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #84750
Made Metadata() functions async and await the metadata promise before rendering. This ensures metadata is resolved during initial SSR rather than being deferred via Suspense/Flight data, allowing React's server-side hoisting to correctly place all meta tags in instead of .
Root cause: When metadata goes through Suspense boundaries, it's sent via RSC Flight data after the initial HTML stream has already sent . React cannot hoist backwards in a stream, so tags end up in .
This fix:
Trade-off: Response blocks on metadata resolution (~milliseconds), but this is acceptable since metadata is invisible and correctness (SEO, social sharing) is more critical than micro-optimizations.
(working on updating the tests....)