Skip to content

turbopack: mark injected chunk scripts as async for React DOM dedupe#92753

Open
gurkerl83 wants to merge 1 commit intovercel:canaryfrom
gurkerl83:fix-turbopack-duplicate-chunk-script-load
Open

turbopack: mark injected chunk scripts as async for React DOM dedupe#92753
gurkerl83 wants to merge 1 commit intovercel:canaryfrom
gurkerl83:fix-turbopack-duplicate-chunk-script-load

Conversation

@gurkerl83
Copy link
Copy Markdown
Contributor

@gurkerl83 gurkerl83 commented Apr 13, 2026

What?

Set script.async = true on JS chunk scripts created by Turbopack's DOM runtime backend.

Why?

Client-side App Router navigations request the same route chunk twice when Turbopack and React DOM both try to load it.

The reproducer and investigation are here:

In particular, the investigation notes describe
"Option A: Add async Attribute to Turbopack's Script Tags", which is what this PR implements.

React DOM reuses hoistable scripts by src and looks them up in the DOM via script[async][src="..."]. Turbopack's DOM chunk loader inserted matching chunk scripts without explicitly setting async, so React DOM misses an already-present script and appends a second one for the same URL.

How?

Set async explicitly on Turbopack-inserted chunk scripts so they participate in the same DOM dedupe path that React DOM uses for hoistable scripts.

Dynamically inserted scripts already load asynchronously, so this only makes the DOM representation explicit without changing load behavior.

This change is in Turbopack's DOM runtime backend, which is used for both development and production DOM chunk loading.

Verification

Validating this change against the reproducer requires:

  1. In the local next.js checkout, run a full rebuild so the Turbopack native
    binary is rebuilt with the updated embedded DOM runtime sources:
pnpm build-all
  1. In the reproducer app, build it against that local native binding:
NEXT_TEST_NATIVE_DIR=<path-to-local-nextjs-checkout>/packages/next-swc/native pnpm next build

Without NEXT_TEST_NATIVE_DIR, the reproducer may resolve a different native binding and not include the patched runtime.

Fixes #92689

Client-side App Router navigations can request the same route chunk twice when
Turbopack and React DOM both try to load it.

React DOM reuses hoistable scripts by `src` and looks them up in the DOM via
`script[async][src="..."]`. Turbopack's DOM chunk loader inserted matching
chunk scripts without explicitly setting `async`, so React DOM could miss an
already-present script and append a second one for the same URL.

Set `script.async = true` when Turbopack appends JS chunk scripts so both code
paths converge on the same DOM identity. Dynamically inserted scripts already
load asynchronously, so this makes the DOM representation explicit without
changing load behavior.

Fixes: vercel#92689
@nextjs-bot nextjs-bot added the Turbopack Related to Turbopack with Next.js. label Apr 13, 2026
@nextjs-bot
Copy link
Copy Markdown
Collaborator

Allow CI Workflow Run

  • approve CI run for commit: ce0072e

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 14, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing gurkerl83:fix-turbopack-duplicate-chunk-script-load (ce0072e) with canary (ef3f853)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Turbopack Related to Turbopack with Next.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Twice requesting the same route-specific client chunk with different initiators during App Router production navigation (Doubles Network Traffic!!!)

2 participants