diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx
index 26e4bcf54cb25..39f1654977e3c 100644
--- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx
+++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx
@@ -10,7 +10,7 @@ const getFlightData = (): NormalizedFlightData[] => {
segmentPath: ['children', 'linking', 'children', 'about'],
segment: 'about',
tree: ['about', { children: ['', {}] }],
- seedData: ['about',
SubTreeData Injected!
, {}, null],
+ seedData: ['about', SubTreeData Injected!
, {}, null, false],
head: 'Head Injected!',
isRootRender: false,
},
diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx
index f55e0d28746eb..e7be100bb5c84 100644
--- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx
+++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx
@@ -11,7 +11,7 @@ const getFlightData = (): NormalizedFlightData[] => {
segmentPath: ['children', 'linking', 'children', 'about'],
segment: 'about',
tree: ['about', { children: ['', {}] }],
- seedData: ['about', About Page!
, {}, null],
+ seedData: ['about', About Page!
, {}, null, false],
head: 'About page!',
isRootRender: false,
},
diff --git a/packages/next/src/client/components/segment-cache/cache.ts b/packages/next/src/client/components/segment-cache/cache.ts
index 5f7316f425324..8be3535929fb1 100644
--- a/packages/next/src/client/components/segment-cache/cache.ts
+++ b/packages/next/src/client/components/segment-cache/cache.ts
@@ -117,6 +117,7 @@ type PendingSegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Pending
rsc: null
loading: null
+ isPartial: true
promise: null | PromiseWithResolvers
}
@@ -124,6 +125,7 @@ type RejectedSegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Rejected
rsc: null
loading: null
+ isPartial: true
promise: null
}
@@ -131,6 +133,7 @@ type FulfilledSegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Fulfilled
rsc: React.ReactNode | null
loading: LoadingModuleData | Promise
+ isPartial: boolean
promise: null
}
@@ -328,6 +331,7 @@ export function requestSegmentEntryFromCache(
rsc: null,
loading: null,
staleAt: route.staleAt,
+ isPartial: true,
promise: null,
// LRU-related fields
@@ -435,13 +439,15 @@ function fulfillSegmentCacheEntry(
segmentCacheEntry: PendingSegmentCacheEntry,
rsc: React.ReactNode,
loading: LoadingModuleData | Promise,
- staleAt: number
+ staleAt: number,
+ isPartial: boolean
) {
const fulfilledEntry: FulfilledSegmentCacheEntry = segmentCacheEntry as any
fulfilledEntry.status = EntryStatus.Fulfilled
fulfilledEntry.rsc = rsc
fulfilledEntry.loading = loading
fulfilledEntry.staleAt = staleAt
+ fulfilledEntry.isPartial = isPartial
// Resolve any listeners that were waiting for this data.
if (segmentCacheEntry.promise !== null) {
segmentCacheEntry.promise.resolve(fulfilledEntry)
@@ -612,7 +618,8 @@ async function fetchSegmentEntryOnCacheMiss(
serverData.loading,
// TODO: The server does not currently provide per-segment stale time.
// So we use the stale time of the route.
- route.staleAt
+ route.staleAt,
+ serverData.isPartial
)
} catch (error) {
// Either the connection itself failed, or something bad happened while
diff --git a/packages/next/src/client/components/segment-cache/navigation.ts b/packages/next/src/client/components/segment-cache/navigation.ts
index 48d203a8e542c..39c2b2021df70 100644
--- a/packages/next/src/client/components/segment-cache/navigation.ts
+++ b/packages/next/src/client/components/segment-cache/navigation.ts
@@ -181,6 +181,7 @@ function readRenderSnapshotFromCache(
let rsc: React.ReactNode | null = null
let loading: LoadingModuleData | Promise = null
+ let isPartial: boolean = true
const segmentEntry = readSegmentCacheEntry(now, tree.path)
if (segmentEntry !== null) {
@@ -189,6 +190,7 @@ function readRenderSnapshotFromCache(
// Happy path: a cache hit
rsc = segmentEntry.rsc
loading = segmentEntry.loading
+ isPartial = segmentEntry.isPartial
break
}
case EntryStatus.Pending: {
@@ -202,6 +204,10 @@ function readRenderSnapshotFromCache(
loading = promiseForFulfilledEntry.then((entry) =>
entry !== null ? entry.loading : null
)
+ // Since we don't know yet whether the segment is partial or fully
+ // static, we must assume it's partial; we can't skip the
+ // dynamic request.
+ isPartial = true
break
}
case EntryStatus.Rejected:
@@ -225,7 +231,13 @@ function readRenderSnapshotFromCache(
null,
isRootLayout,
],
- seedData: [flightRouterStateSegment, rsc, childSeedDatas, loading],
+ seedData: [
+ flightRouterStateSegment,
+ rsc,
+ childSeedDatas,
+ loading,
+ isPartial,
+ ],
}
}
diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx
index 0a70918d33604..e829eb6efabcf 100644
--- a/packages/next/src/server/app-render/app-render.tsx
+++ b/packages/next/src/server/app-render/app-render.tsx
@@ -872,6 +872,7 @@ async function getErrorRSCPayload(