Skip to content

next/script beforeInteractive: hydration duplicate, src bypasses server registry, attr-map gaps #2016

Description

@SuperManfred

vinext: 0.1.2

Summary. Three related defects in the next/script shim around beforeInteractive:

  1. Hydration duplication. SSR splices the beforeInteractive script into the pre-head, but the emitted node carries no marker. On the client, <Script> re-renders the element because it can't tell the server already handled it (the nav runtime may not be registered yet — dev loads it as an async ESM module). Result: a duplicate script element and a hydration mismatch on essentially every page.
  2. src scripts bypass the server registry. Only inline beforeInteractive content is registered into the pre-head splice path. A beforeInteractive script with src falls through to the normal React element path, so it does not get the same pre-head ordering, marker, or client dedupe treatment as inline scripts.
  3. Attribute mapping gaps. REACT_TO_HTML_ATTR maps className/htmlFor/httpEquiv but not crossOrigincrossorigin or referrerPolicyreferrerpolicy, so server-spliced script attrs are not normalized to the same HTML spelling Next uses.

Reproduce.

  1. Add <Script id="x" strategy="beforeInteractive">…</Script> (and separately one with src=…).
  2. Load/hydrate the page.
  3. Observe: two copies of the inline script + a hydration warning (defect 1); the src script is not emitted through the same server splice/marker/dedupe path as inline beforeInteractive scripts (defect 2); crossOrigin/referrerPolicy are not normalized in the server-spliced string attrs (defect 3).

Where (0.1.2 dist).

  • dist/server/app-ssr-entry.js:80renderBeforeInteractiveInlineScripts emits scripts with no identifying marker.
  • dist/shims/script.js:113REACT_TO_HTML_ATTR missing crossOrigin/referrerPolicy.
  • dist/shims/script.js beforeInteractive branch (~L269 / ~L353) — only handles inline content (inlineContent !== null) and dedups only via hasAppNavigationRuntimeBootstrap().

Suggested fix.

  • Emit a marker on SSR scripts: data-vinext-before-interactive="" (in renderBeforeInteractiveInlineScripts).
  • On the client, also treat a matching spliced DOM node (by marker + id/src) as "server already handled it" → return null instead of rendering a duplicate.
  • Register src beforeInteractive scripts too (inlineContent !== null || src), passing src through the registered attributes.
  • Add crossOrigin: "crossorigin" and referrerPolicy: "referrerpolicy" to REACT_TO_HTML_ATTR.

Related existing issues: #1518 and #1557 cover earlier beforeInteractive head-placement/order failures; #1517 covers nonce/stylesheets/bootstrap preinit. This report is narrower: server/client duplicate-dedupe marker, src parity with the beforeInteractive server registry, and the remaining attr-map gaps. Defect 3 is independent and tiny enough to split if maintainers prefer, but keeping it here is reasonable because the fix is in the same next/script attr collection/emission path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions