Skip to content

fix(app-router): emit streamed not-found/redirect meta tags in Next.js byte-exact format (#1491)#1651

Merged
james-elicx merged 1 commit into
mainfrom
fix/issue-1491-streaming-meta-tags
May 28, 2026
Merged

fix(app-router): emit streamed not-found/redirect meta tags in Next.js byte-exact format (#1491)#1651
james-elicx merged 1 commit into
mainfrom
fix/issue-1491-streaming-meta-tags

Conversation

@james-elicx
Copy link
Copy Markdown
Member

Summary

  • When notFound() or redirect() is called from inside a Suspense boundary the error fires after the shell has streamed; the not-found / redirect intent is communicated via inline <meta> tags injected into <head> (<meta name="robots" content="noindex"/>, <meta id="__next-page-redirect" http-equiv="refresh" content="...">).
  • vinext was emitting these as <meta ... content="..." /> (a space before />), but Next.js serializes them via React's HTML renderer which produces <meta ... content="..."/> (no space). The Next.js navigation deploy suite asserts on that exact substring, so the mismatch caused ~5 failures.
  • Drop the extra space in app-ssr-error-meta.ts and update the focused unit tests / stream-injection test to match. Added two ported regression tests (tests/nextjs-compat/navigation.test.ts) that assert the exact streamed substrings against the existing suspense-notfound-test / suspense-redirect-test fixtures.

Closes #1491

Test plan

  • pnpm test tests/app-ssr-error-meta.test.ts — unit tests for the renderer
  • pnpm test tests/rsc-streaming.test.ts — covers the streaming injection ordering test
  • pnpm test tests/nextjs-compat/navigation.test.ts — new exact-substring suspense assertions
  • pnpm test tests/nextjs-compat/not-found.test.ts tests/nextjs-compat/metadata.test.ts tests/app-page-boundary-render.test.ts — adjacent paths unaffected
  • pnpm run check — format + lint + types green

References

  • Next.js source: packages/next/src/server/app-render/make-get-server-inserted-html.tsx
  • Next.js tests: test/e2e/app-dir/navigation/navigation.test.ts (SEO describe block, lines 732-772)

)

When notFound() or redirect() is called from inside a Suspense boundary
the error surfaces after the shell has already streamed, so the not-found
/ redirect intent is communicated to the client via inline <meta> tags
injected into the head section of the stream.

vinext was emitting these as `<meta name="robots" content="noindex" />`
(with a space before the self-closing slash), but Next.js serializes them
via React's HTML renderer, which produces `<meta ... content="..."/>`
without the space. The Next.js navigation tests assert on that exact
substring, so this mismatch caused ~5 deploy-suite failures.

Drop the extra space so the streamed meta tags match Next.js byte-for-byte,
and add ported regression tests against the suspense fixtures.

Ref: packages/next/src/server/app-render/make-get-server-inserted-html.tsx
Ref: test/e2e/app-dir/navigation/navigation.test.ts (SEO describe block)
@james-elicx
Copy link
Copy Markdown
Member Author

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 28, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1651

commit: 0e98e2e

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified against the upstream Next.js test file (test/e2e/app-dir/navigation/navigation.test.ts, SEO describe block, lines ~732-772). Next.js asserts the exact substrings:

'<meta name="robots" content="noindex"/>'
'<meta id="__next-page-redirect" http-equiv="refresh" content="1;url=/redirect/result"/>'
'<meta id="__next-page-redirect" http-equiv="refresh" content="0;url=/redirect/result"/>'

The fix is correct — React's void-element serialization uses no space before />, and this PR matches that format across all three emission sites (noindex, dev next-error, and redirect refresh).

Code changes look clean:

  • app-ssr-error-meta.ts: All three meta tag templates updated consistently. The comment explaining why the format matters and linking to the Next.js source is a good addition.
  • app-ssr-error-meta.test.ts: All 12 assertion strings updated to match — mechanically correct, nothing missed.
  • rsc-streaming.test.ts: The one redirect meta assertion in the stream-ordering test updated to match.
  • navigation.test.ts: Two new ported tests with good block comment explaining the streaming meta tag scenario. Removing the "N/A: Redirect refresh meta tag" entry makes sense since it's now covered.

Fixtures (suspense-notfound-test/page.tsx, suspense-redirect-test/page.tsx) already exist and correctly exercise the Suspense-boundary streaming path.

No concerns.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 28, 2026

Reviewed and approved PR #1651. The fix is correct — I verified against the upstream Next.js test file (test/e2e/app-dir/navigation/navigation.test.ts, SEO describe block) which asserts on the exact "/> format (no space before />) produced by React's void-element serialization. All three meta tag emission sites are updated consistently, all test assertions match, and the two new ported regression tests cover the streaming Suspense path that was causing the failures.

github run

@james-elicx james-elicx marked this pull request as ready for review May 28, 2026 13:40
@james-elicx
Copy link
Copy Markdown
Member Author

The 'Deploy benchmarks' check failure is unrelated: wrangler asset upload returned 401 Unauthorized from the Cloudflare API (Received a malformed response from the API / POST /accounts/***/workers/assets/upload -> 401). This is an infrastructure/credentials hiccup in the deploy preview workflow, not a code regression in this PR. Other CI is green.

@github-actions
Copy link
Copy Markdown

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
web preview production

@james-elicx james-elicx merged commit b5da92e into main May 28, 2026
47 of 48 checks passed
@james-elicx james-elicx deleted the fix/issue-1491-streaming-meta-tags branch May 28, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App Router: streaming not-found / redirect responses missing robots / refresh meta tags

1 participant