diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx
index e36addc60376b..38a15539ce813 100644
--- a/packages/next/src/client/components/app-router.tsx
+++ b/packages/next/src/client/components/app-router.tsx
@@ -293,6 +293,7 @@ function Head({
function Router({
buildId,
initialHead,
+ initialLayerAssets,
initialTree,
initialCanonicalUrl,
initialSeedData,
@@ -310,7 +311,7 @@ function Router({
initialParallelRoutes,
location: !isServer ? window.location : null,
initialHead,
- initialLayerAssets: null,
+ initialLayerAssets,
couldBeIntercepted,
}),
[
@@ -319,6 +320,7 @@ function Router({
initialCanonicalUrl,
initialTree,
initialHead,
+ initialLayerAssets,
couldBeIntercepted,
]
)
@@ -657,10 +659,27 @@ function Router({
head = null
}
+ // We use `useDeferredValue` to handle switching between the prefetched and
+ // final values. The second argument is returned on initial render, then it
+ // re-renders with the first argument. We only use the prefetched layer assets
+ // if they are available. Otherwise, we use the non-prefetched version.
+ const resolvedPrefetchLayerAssets =
+ cache.prefetchLayerAssets !== null
+ ? cache.prefetchLayerAssets
+ : cache.layerAssets
+
+ const layerAssets = useDeferredValue(
+ cache.layerAssets,
+ // @ts-expect-error The second argument to `useDeferredValue` is only
+ // available in the experimental builds. When its disabled, it will always
+ // return `cache.layerAssets`.
+ resolvedPrefetchLayerAssets
+ )
+
let content = (
{head}
- {/* {cache.layerAssets} */}
+ {layerAssets}
{cache.rsc}
diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx
index eb7b7824effea..a6ea8659406fb 100644
--- a/packages/next/src/client/components/layout-router.tsx
+++ b/packages/next/src/client/components/layout-router.tsx
@@ -456,6 +456,23 @@ function InnerLayoutRouter({
}
}
+ // We use `useDeferredValue` to handle switching between the prefetched and
+ // final values. The second argument is returned on initial render, then it
+ // re-renders with the first argument. We only use the prefetched layer assets
+ // if they are available. Otherwise, we use the non-prefetched version.
+ const resolvedPrefetchLayerAssets =
+ childNode.prefetchLayerAssets !== null
+ ? childNode.prefetchLayerAssets
+ : childNode.layerAssets
+
+ const layerAssets = useDeferredValue(
+ childNode.layerAssets,
+ // @ts-expect-error The second argument to `useDeferredValue` is only
+ // available in the experimental builds. When its disabled, it will always
+ // return `cache.layerAssets`.
+ resolvedPrefetchLayerAssets
+ )
+
// If we get to this point, then we know we have something we can render.
const subtree = (
// The layout router context narrows down tree and childNodes at each level.
@@ -468,7 +485,7 @@ function InnerLayoutRouter({
loading: childNode.loading,
}}
>
- {/* {childNode.layerAssets} */}
+ {layerAssets}
{resolvedRsc}
)
diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx
index bbe34eed82437..3a50b406686b8 100644
--- a/packages/next/src/server/app-render/app-render.tsx
+++ b/packages/next/src/server/app-render/app-render.tsx
@@ -482,34 +482,31 @@ async function ReactServerApp({ tree, ctx, asNotFound }: ReactServerAppProps) {
typeof varyHeader === 'string' && varyHeader.includes(NEXT_URL)
return (
- <>
- {styles}
-
- {typeof ctx.res.statusCode === 'number' &&
- ctx.res.statusCode > 400 && (
-
- )}
- {/* Adding requestId as react key to make metadata remount for each render */}
-
- >
- }
- initialLayerAssets={null}
- globalErrorComponent={GlobalError}
- // This is used to provide debug information (when in development mode)
- // about which slots were not filled by page components while creating the component tree.
- missingSlots={missingSlots}
- />
- >
+
+ {typeof ctx.res.statusCode === 'number' &&
+ ctx.res.statusCode > 400 && (
+
+ )}
+ {/* Adding requestId as react key to make metadata remount for each render */}
+
+ >
+ }
+ initialLayerAssets={styles}
+ globalErrorComponent={GlobalError}
+ // This is used to provide debug information (when in development mode)
+ // about which slots were not filled by page components while creating the component tree.
+ missingSlots={missingSlots}
+ />
)
}
diff --git a/test/development/acceptance-app/hydration-error.test.ts b/test/development/acceptance-app/hydration-error.test.ts
index ee51218152876..1602329ad03e3 100644
--- a/test/development/acceptance-app/hydration-error.test.ts
+++ b/test/development/acceptance-app/hydration-error.test.ts
@@ -604,40 +604,38 @@ describe('Error overlay for hydration errors', () => {
if (isTurbopack) {
expect(fullPseudoHtml).toMatchInlineSnapshot(`
"...
-
-
-
-
-
-
+
+
+
+
+
+