Skip to content

fix(pages): align React runtime behavior#2571

Open
james-elicx wants to merge 6 commits into
mainfrom
codex/fix-pages-react-runtime-28938793088
Open

fix(pages): align React runtime behavior#2571
james-elicx wants to merge 6 commits into
mainfrom
codex/fix-pages-react-runtime-28938793088

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

Fix the Pages Router functional failures from test/e2e/react-current-version/react-current-version.test.ts in Actions run 28938793088 against Next.js v16.3.0-canary.80.

Owned non-styled failures:

  • Basics > should only render once in SSR (expected 1 render, received 2)
  • Basics > should contain dynamicIds in next data for dynamic imports
  • Concurrent mode in the experimental-edge runtime > <RouteAnnouncer /> > should not have the initial route announced
  • Concurrent mode in the nodejs runtime > <RouteAnnouncer /> > should not have the initial route announced

This change:

  • records request-scoped Pages next/dynamic module usage and serializes rendered module IDs in __NEXT_DATA__.dynamicIds
  • adds the Pages Router route-announcer DOM contract and soft-navigation announcements
  • seeds and reuses prerendered Pages HTML on the first production request so auto-exported pages are not rendered twice
  • covers the behavior with focused unit and Pages Router Playwright tests

Scope

The two flushes styled-jsx styles as the page renders assertions remain intentionally excluded because styled-jsx/CSS behavior is owned by #2220. This PR does not modify styled-jsx or CSS handling.

No overlapping open PR from james-elicx was found for these functional failures.

Validation

Pinned upstream suite:

REPO="$(pwd)" \
NEXTJS_DIR="/Users/jamesanderson/Developer/vinext/.nextjs-ref-v16.3.0-canary.80" \
./scripts/run-targeted-nextjs-e2e.sh test/e2e/react-current-version/react-current-version.test.ts

Result: 9/9 non-styled functional assertions pass. The command exits nonzero only for the 2/2 explicitly excluded styled-jsx assertions.

Focused vinext tests:

vp test run tests/pages-page-data.test.ts tests/pages-page-response.test.ts tests/seed-cache.test.ts

Result: 136/136 passed.

Additional validation:

  • Pages Router route-announcer Playwright regression: 1/1 passed
  • vp check: passed
  • vp run vinext#build: passed

@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2571
npm i https://pkg.pr.new/create-vinext-app@2571
npm i https://pkg.pr.new/vinext@2571

commit: 1c6eb40

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
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
workers-cache preview production
web preview production

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 1c6eb40 against base 617bffa using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 1 regressed · 5 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 126.6 KB 126.6 KB ⚫ +0.0%
Client entry size (gzip) vinext 120.6 KB 120.6 KB ⚫ 0.0%
Dev server cold start vinext 2.68 s 2.67 s ⚫ -0.2%
Production build time vinext 2.83 s 2.87 s 🔴 +1.6%
RSC entry closure size (gzip) vinext 98.5 KB 98.6 KB ⚫ +0.1%
Server bundle size (gzip) vinext 165.1 KB 165.4 KB ⚫ +0.1%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — PR #2571 (fix(pages): align React runtime behavior)

Reviewed the full branch (2812400 + b18676c) against main. Ran tests/dynamic.test.ts, tests/pages-page-data.test.ts, tests/pages-page-response.test.ts, tests/seed-cache.test.ts, tests/entry-templates.test.ts, tests/pages-router.test.ts (all green) and tsc --noEmit (clean).

The overall approach is sound: request-scoped dynamicIds recording, imperative route announcer that produces no SSR markup (so no hydration mismatch), and prerender seeding to avoid the double auto-export render. Below are the issues worth addressing.

1. App-load-failure fallback path skips dynamic preload (parity gap)

packages/vinext/src/entries/pages-client-entry.ts (~L253-267):

let appModule;
try {
  appModule = await appLoader();
  await preloadPagesDynamicModules(nextData.dynamicIds);
  ...
} catch {
  element = React.createElement(PageComponent, pageProps);  // no preload here
}

If appLoader() throws, the catch hydrates PageComponent without calling preloadPagesDynamicModules(...). That reintroduces the exact hydration-timing bug this PR fixes, but only on the _app load-failure fallback. preloadPagesDynamicModules swallows loader errors internally, so it's safe to call it before building the fallback element. Suggest moving the preload out of the try (or calling it in the catch) so both branches preload.

2. Dev/prod parity: dev client template has no _app failure fallback

packages/vinext/src/server/dev-server.ts (~L1820-1834) generates the dev hydration script with a bare const appModule = await import(...) and no try/catch, whereas the prod template (pages-client-entry.ts) wraps _app loading in try/catch with a PageComponent fallback. This is a pre-existing divergence, but issue #1's fix should be mirrored to whichever template ends up with the fallback so the two stay in sync per AGENTS.md's dev/prod parity rule.

3. preloadPagesDynamicModules clears all registered initializers, not just requested ones

packages/vinext/src/shims/pages-dynamic.ts (L50-51): clientInitializers.clear() wipes the entire map each loop iteration, but only initializers whose id is in requestedIds are actually invoked (L54). Any initializer registered for a module not in nextData.dynamicIds is silently discarded, and since clientPreloadFinished is then latched true (L61), registerPagesDynamicInitializer becomes a permanent no-op (L31). In practice this is fine because post-hydration components load lazily via React.lazy/Suspense, but the "clear everything, run a subset" shape is a footgun. Consider deleting only the entries you actually ran, and add a comment documenting that the latch is intentional (initial-hydration-only preload).

4. Dead defensive branch in preloadPagesDynamicModules

The inner IIFE (L48-62) can never reject — every pending promise is .catch(() => undefined)'d — so clientPreloadFinished is always true by the time the finally (L66-69) runs, making the clientPreloadPromise = undefined reset unreachable. Not a bug, but the try/finally implies a rejection path that doesn't exist; either drop it or make the intent explicit.

Non-blocking notes

  • Shared module-level retry state (sharedSnapshot/retrySharedSnapshot in dynamic.ts) means all instances of the same dynamic() share one retry/loadable state, and a successful load persists across unmount/remount. The new preserves a successful dynamic retry across remounts test asserts exactly this (gateRequests === 2). This matches react-loadable's shared-subscription model, so it's consistent with Next.js — just calling it out as intentional since it changes prior per-instance useState behavior.
  • Route announcer text priority is document.title || h1 || asPath, evaluated in a useEffect on asPath change — correct ordering and it reads the post-navigation title. The initial-render guard (previousPath.current === asPath) correctly suppresses the initial announcement, satisfying the "should not have the initial route announced" case.
  • Seed-cache: seeding reads full prerendered HTML from disk (which already contains __NEXT_DATA__ incl. dynamicIds), and the auto-export HIT in pages-page-data.ts serves it verbatim, so dynamicIds are preserved on the seeded path. Key derivation (routeUrl.split("?")[0]) dropping the query is correct for auto-export/static pages.

Items 1–2 are the ones I'd want addressed before merge (hydration correctness + dev/prod parity); 3–4 are cleanup.

@ask-bonk

ask-bonk Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2571. Summary of findings:

Review Summary

I reviewed the full branch (both 2812400 and the follow-up b18676c) against main, ran the relevant unit/integration tests (all pass) and type-checked the package (clean). The approach is sound. I flagged 4 issues + 3 non-blocking notes:

Blocking-ish (hydration correctness + parity):

  1. App-load-failure fallback skips dynamic preload — in pages-client-entry.ts, if appLoader() throws, the catch hydrates PageComponent without calling preloadPagesDynamicModules(...), reintroducing the hydration-timing bug this PR fixes. preloadPagesDynamicModules swallows errors internally, so it can safely move out of the try.
  2. Dev/prod parity — the dev-server client template has no _app failure fallback that the prod template has; the fix for Virtual module imports break esbuild dependency optimization when vinext is installed from npm #1 should be mirrored to keep them in sync (per AGENTS.md dev/prod parity rule).

Cleanup:
3. preloadPagesDynamicModules clears the entire initializer map but only runs a requested subset, then permanently latches clientPreloadFinished = true. Works in practice (post-hydration loads are lazy) but is a footgun worth documenting/tightening.
4. Dead defensive try/finally branch — the inner IIFE can never reject, so the clientPreloadPromise = undefined reset is unreachable.

Non-blocking notes: confirmed the shared module-level retry state matches react-loadable's design (intentional), the route announcer text priority and initial-announcement suppression are correct, and the seed-cache path correctly preserves dynamicIds in the served HTML.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — PR #2571 (fix(pages): align React runtime behavior)

Re-reviewed the full branch at 9f183c7 (includes the follow-up d1e64d2 fix(pages): preserve preload on app fallback) against main. Ran tests/dynamic.test.ts, tests/pages-page-data.test.ts, tests/pages-page-response.test.ts, tests/seed-cache.test.ts, tests/entry-templates.test.ts (196/196 pass) and vp check on the changed source files (clean).

Status of prior review items

  • #1 (app-load-failure skips dynamic preload):Fixed. preloadPagesDynamicModules(nextData.dynamicIds) now runs after the try/catch in pages-client-entry.ts:272, so both the success and _app-failure fallback branches preload before hydration.
  • #2 (dev/prod parity):Fixed. The dev hydration template in dev-server.ts now wraps _app loading in try/catch with a PageComponent fallback and calls preloadPagesDynamicModules(nextData.dynamicIds), matching the prod template.
  • #3 (clear-all initializers footgun): ⚠️ Still present (see below).
  • #4 (dead defensive branch):Fixed. The try/finally was removed; clientPreloadFinished = true now runs inline in the IIFE.

New / remaining findings

1. Cloudflare runtime parity gap — auto-export seed never fires on Workers (parity, non-blocking for stated scope)

The double-render fix has two halves: (a) seed prerendered Pages HTML into the cache at startup, and (b) short-circuit auto-export pages on a cache HIT in pages-page-data.ts:745-763. The seed half only runs in the Node production server — startPagesRouterServer calls seedMemoryCacheFromPrerender(...) at prod-server.ts:1634.

On Cloudflare Workers there is no equivalent:

  • packages/cloudflare/src/prerender-kv-populate.ts (buildPrerenderKVPairs) only walks getRenderedAppRoutes(...) and writes kind: "APP_PAGE" entries — it never seeds Pages Router (kind: "PAGES") artifacts.
  • The default MemoryCacheHandler is empty on isolate cold start, and the auto-export path in pages-page-data.ts only reads the cache (unlike the getStaticProps path, it never isrSets after render).

Net effect: on Workers the auto-export HIT never fires, so the behavior the upstream should only render once in SSR test verifies won't hold there. This is consistent with AGENTS.md's "MemoryCacheHandler is empty per-isolate" note, so it's arguably out of scope for a PR targeting the Node-based upstream e2e suite — but it's a deliberate divergence worth either documenting in the PR or filing as a follow-up (mirror the Pages seed into buildPrerenderKVPairs).

2. Route announcer uses textContent where Next.js uses innerText first (minor parity)

shims/router.ts PagesRouteAnnouncer computes document.title || heading?.textContent || asPath. Next.js's route announcer prefers h1?.innerText before falling back to textContent (document.title || innerText || textContent || pathname). textContent includes visually-hidden text and doesn't collapse whitespace, so for pages that rely on the <h1> (no document.title) the announced string can differ from Next.js. Suggest heading?.innerText || heading?.textContent to match.

3. Auto-export cache HIT omits bot ETag / 304 handling (consistency)

The new auto-export HIT at pages-page-data.ts:749-762 returns buildPagesCacheResponse(..., "HIT", ...) directly, but the analogous getStaticProps HIT path (pages-page-data.ts:886-904) additionally runs applyBotETagAndCheck(...) so bot UAs get a stable ETag and can receive a 304 on If-None-Match. Auto-export HITs skip that, so a crawler hitting an auto-export page gets no ETag/304 while a crawler hitting an SSG page does. Not a correctness bug for the target failures, but an inconsistency between two very similar HIT branches.

4. preloadPagesDynamicModules still clears all registered initializers (from prior #3)

shims/pages-dynamic.ts:50-56: each loop iteration snapshots clientInitializers, then clientInitializers.clear() wipes the whole set, but only initializers whose registered ids intersect requestedIds actually run (registerPagesDynamicInitializer returns undefined otherwise). Any initializer for a module not in nextData.dynamicIds is discarded, and once clientPreloadFinished = true latches, registerPagesDynamicInitializer is a permanent no-op. This is fine in practice (post-hydration components load lazily via React.lazy/Suspense) but the "clear everything, run a subset" shape remains a footgun. Consider deleting only the entries you ran and adding a one-line comment that the latch is intentionally initial-hydration-only.

Non-blocking confirmations

  • Shared module-level dynamic retry state (sharedSnapshot/retrySharedSnapshot + useSyncExternalStore in dynamic.ts) intentionally shares one loadable/retry state across instances and persists a successful load across remount — matching react-loadable. The preserves a successful dynamic retry across remounts test (gateRequests === 2) asserts exactly this. The added key: retryKey on the error boundary correctly forces a remount on retry.
  • Seed-cache path reads full prerendered HTML from disk (which already embeds __NEXT_DATA__ incl. dynamicIds) and serves it verbatim on the auto-export HIT, so dynamicIds are preserved. Cache-key derivation (routeUrl.split("?")[0] + buildId via the request-scoped isrCacheKey) matches the seeder's isrCacheKey("pages", cachePathname, buildId).
  • dynamicIds serialization into __NEXT_DATA__ is consistently gated on getPagesDynamicModuleIds() returning a non-empty array in both pages-page-response.ts and the dev buildAllScripts() closure — no empty-array noise.

Items 1–2 are the ones I'd want addressed or explicitly deferred (Cloudflare parity + announcer text parity); 3–4 are consistency/cleanup.

@ask-bonk

ask-bonk Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2571.

Summary

Re-reviewed the full branch at 9f183c7 (including the follow-up commit d1e64d2 since the last review) against main. Ran the relevant unit/integration tests (196/196 pass) and vp check on changed source (clean).

Prior review items resolved:

New findings flagged:

  1. Cloudflare runtime parity gap (main new issue) — the auto-export double-render fix relies on cache seeding that only runs in the Node prod server (prod-server.ts:1634). Cloudflare's buildPrerenderKVPairs only seeds App Router (kind: "APP_PAGE"), and the auto-export path only reads (never writes) the cache, so the fix never applies on Workers. Recommend documenting or filing a follow-up.
  2. Route announcer text parity — vinext uses heading?.textContent; Next.js prefers innerText first.
  3. Auto-export HIT omits bot ETag/304 handling that the analogous getStaticProps HIT path has.
  4. preloadPagesDynamicModules still clears all initializers (prior fix: exclude vinext from optimizeDeps to prevent virtual module resolution errors #3 footgun).

Plus non-blocking confirmations that the shared retry state, seed-cache dynamicIds preservation, cache-key derivation, and __NEXT_DATA__ serialization are all correct.

The overall approach is sound and the two blocking items from the prior review are properly fixed. Items 1–2 are worth addressing or explicitly deferring; 3–4 are cleanup.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — PR #2571 (fix(pages): align React runtime behavior)

Re-reviewed the full branch at 1c6eb40 (includes the new follow-up 1c6eb40 fix(pages): preserve prerender runtime parity, on top of the previously reviewed 9f183c7) against main. Ran tests/pages-page-data.test.ts, tests/seed-cache.test.ts, tests/prerender-kv-populate.test.ts (97/97), tests/dynamic.test.ts (19/19), and tests/entry-templates.test.ts + tests/pages-router.test.ts (393/393). Ran vp check on the changed source files (clean).

Status of prior review items — all three flagged items are now fixed

  • Prior #1 (Cloudflare runtime parity — auto-export seed never fires on Workers):Fixed. buildPrerenderKVPairs now has a second loop (prerender-kv-populate.ts:176-219) that walks route.router === "pages" rendered routes, skips fallback shells and zero-revalidate entries, and seeds kind: "PAGES" KV entries under isrCacheKey("pages", cachePathname, buildId) — the same key the auto-export HIT reads. Error-doc status (/404→404, /500//_error→500) is preserved, matching the Node seed path. Covered by new tests/prerender-kv-populate.test.ts cases (pages seeding, fallback/zero-revalidate skipping, error-doc status).
  • Prior #2 (route announcer textContent vs Next.js innerText):Fixed. PagesRouteAnnouncer now uses heading?.innerText ?? heading?.textContent (router.ts:1437), exactly matching Next.js's route-announcer.tsx (pageHeader?.innerText ?? pageHeader?.textContent). Verified against upstream canary source. New Playwright test route announcer uses visible heading text when the title is empty covers the <h1> fallback path.
  • Prior #3 (auto-export HIT omits bot ETag / 304 handling):Fixed. The auto-export HIT at pages-page-data.ts:749-764 now calls applyBotETagAndCheck(hitResponse, cachedValue.html, options) before returning, identical to the getStaticProps HIT path (pages-page-data.ts:902). Crawlers now get a stable ETag / 304 on both HIT branches.

Prior blocking items (from the review before) — confirmed still fixed

  • App-load-failure skips dynamic preload:preloadPagesDynamicModules(nextData.dynamicIds) runs after the try/catch in both prod (pages-client-entry.ts:272) and dev (dev-server.ts) hydration templates.
  • Dev/prod parity: ✅ dev template mirrors prod (try/catch _app load + PageComponent fallback + preload).
  • Dead defensive branch:try/finally removed from preloadPagesDynamicModules.

Remaining findings

1. preloadPagesDynamicModules still clears all registered initializers (cleanup, non-blocking; carried over from prior #3/#4)

shims/pages-dynamic.ts:50-51: each loop iteration snapshots clientInitializers then clientInitializers.clear() wipes the whole set, but only initializers whose ids intersect requestedIds actually run (registerPagesDynamicInitializer returns undefined otherwise). Initializers for modules not in nextData.dynamicIds are discarded, and once clientPreloadFinished = true latches, registerPagesDynamicInitializer becomes a permanent no-op. This is fine in practice (post-hydration components load lazily via React.lazy/Suspense) but the "clear everything, run a subset" shape remains a footgun. Consider deleting only the entries you ran and adding a one-line comment that the latch is intentionally initial-hydration-only.

2. Auto-export HIT does not bypass on-demand revalidation (minor, likely intended)

The auto-export HIT guard (pages-page-data.ts:745) is isAutoExport && !options.isDataReq && !options.scriptNonce && previewData === false, and since previewData is forced to false when isOnDemandRevalidate is true (pages-page-data.ts:722), an on-demand res.revalidate() request against an auto-export page still serves the cached HIT rather than falling through (unlike the getStaticProps HIT, which explicitly checks !options.isOnDemandRevalidate). Because auto-export pages have no data-fetching to regenerate, serving the cached HTML is arguably correct — flagging only so the divergence is a conscious choice, not accidental.

Non-blocking confirmations

  • dynamicIds render/serialize ordering is correct in all three paths: in prod (pages-page-response.ts:560) and dev (buildAllScripts() at dev-server.ts) the module ids are read after renderToReadableStream resolves the shell — ServerDynamic records preloadModuleIds synchronously during shell render, so the ids are populated before getPagesDynamicModuleIds() runs. The dev scripts callback is invoked at dev-server.ts:458/468, after renderToReadableStream at line 407. Serialization is gated on a non-empty array in both paths (no empty-array noise). Verified by the new serializes only next/dynamic modules used during the request render unit test.
  • Node/Cloudflare seed parity for revalidate: static pages (revalidate: false) resolve to 31_536_000 on both paths (Cloudflare inlines the default at prerender-kv-populate.ts:193; Node's isrSetPrerenderedPagesPage defaults revalidateSeconds ?? 31_536_000 at isr-cache.ts:242), so cacheControl.revalidate matches. Error-doc status derivation is identical between seed-cache.ts:172-177 and prerender-kv-populate.ts:204-209.
  • Shared module-level dynamic retry state (sharedSnapshot/retrySharedSnapshot + useSyncExternalStore) intentionally shares one loadable/retry state across instances and persists a successful load across remount — matching react-loadable. On the server this correctly caches the module load once per isolate; retry only fires from client error boundaries. The added key: retryKey on the error boundary forces a remount on retry.
  • Route announcer matches Next.js's title→h1→asPath priority and suppresses the initial announcement via previousPath.current === asPath.

Verdict: The three items I flagged in the previous review are all properly resolved and covered by tests, and the two earlier blocking items remain fixed. Remaining findings (#1 cleanup, #2 minor divergence) are non-blocking. The approach is sound and the branch looks ready to merge.

Reviewed at 1c6eb40.

@ask-bonk

ask-bonk Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2571.

Summary

I re-reviewed the full branch at the latest commit 1c6eb40 (which includes the new fix(pages): preserve prerender runtime parity follow-up, on top of the previously reviewed 9f183c7). Ran all relevant unit/integration tests (97 + 19 + 393 = clean) and vp check on changed source (clean).

The key outcome: all three items flagged in the prior review are now fixed and tested:

  1. Cloudflare runtime parity gapbuildPrerenderKVPairs now seeds kind: "PAGES" KV entries in a second loop, so the auto-export double-render fix actually works on Workers. Covered by new tests.
  2. Route announcer text parity — now uses innerText ?? textContent, matching Next.js's route-announcer.tsx exactly (verified against upstream canary source).
  3. Auto-export HIT bot ETag/304 — now calls applyBotETagAndCheck, consistent with the getStaticProps HIT path.

The earlier blocking items (app-load-failure preload, dev/prod parity, dead defensive branch) also remain fixed.

Remaining (non-blocking):

  • preloadPagesDynamicModules still clears all initializers instead of only the ones it ran (cleanup footgun).
  • Auto-export HIT doesn't bypass on-demand revalidation, but that's arguably correct since auto-export pages have nothing to regenerate.

Verdict: sound approach, prior findings resolved and covered by tests, ready to merge.

github run

@james-elicx
james-elicx marked this pull request as ready for review July 9, 2026 08:59
@james-elicx

Copy link
Copy Markdown
Member Author

Independent Codex review — NO FINDINGS

Independently reviewed exact head 1c6eb403c14ed4d53cd99f1af9cb52f83c340619 against main. This is separate from Bonk.

Scope reviewed:

  • Cloudflare Pages prerender KV seeding, cache keys, revalidate/expiry behavior, and preserved 404/500 status
  • auto-export cache HIT bot ETag / 304 handling
  • Pages route-announcer initial suppression and title → visible h1 → path fallback behavior
  • Pages dynamic-module recording/preload ordering, app-load fallback behavior, shared retry state, and retry remounting

Targeted oracle evidence:

  • Next.js checkout: v16.3.0-canary.80 (9dd6d677b63b249584c8ff0bccffcc6a65288683)
  • Command: REPO="$(pwd)" NEXTJS_DIR="/Users/jamesanderson/Developer/vinext/.nextjs-ref-v16.3.0-canary.80" ./scripts/run-targeted-nextjs-e2e.sh test/e2e/react-current-version/react-current-version.test.ts
  • Result: 9/9 scoped functional assertions passed (single SSR render, hydration/useId, dynamicIds, both route-announcer runtimes, and invalid-config checks)
  • The only failures were the 2 explicitly excluded styled-jsx assertions (experimental-edge and nodejs style flushing), outside this PR's scope
  • Regular PR CI is 64/64 green

Verdict: NO FINDINGS. No blocking correctness, parity, or regression concerns in the reviewed scope.

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.

1 participant