Skip to content

Commit

Permalink
[metadata] disable streaming metadata for ppr deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Feb 12, 2025
1 parent 0d66343 commit 8b41f01
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 7 deletions.
6 changes: 5 additions & 1 deletion packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2771,7 +2771,11 @@ export default async function build(
},
// If it's PPR rendered non-static page, bypass the PPR cache when streaming metadata is enabled.
// This will skip the postpone data for those bots requests and instead produce a dynamic render.
...(isRoutePPREnabled && config.experimental.streamingMetadata
...(isRoutePPREnabled &&
// Disable streaming metadata for PPR on Vercel.
// TODO: enable streaming metadata in PPR mode by default once it's ready.
process.env.VERCEL === '1' &&
config.experimental.streamingMetadata
? [
{
type: 'header',
Expand Down
27 changes: 21 additions & 6 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,17 @@ function NonIndex({ ctx }: { ctx: AppRenderContext }) {
return null
}

function getServeStreamingMetadata(ctx: AppRenderContext) {
const isRoutePPREnabled = !!ctx.renderOpts.experimental.isRoutePPREnabled
const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata
// If the route is in PPR and it's on Vercel, disable the streaming metadata.
// TODO: enable streaming metadata in PPR mode by default once it's ready.
if (isRoutePPREnabled && process.env.VERCEL === '1') {
return false
}
return serveStreamingMetadata
}

/**
* This is used by server actions & client-side navigations to generate RSC data from a client-side request.
* This function is only called on "dynamic" requests (ie, there wasn't already a static response).
Expand Down Expand Up @@ -473,6 +484,8 @@ async function generateDynamicRSCPayload(
url,
} = ctx

const serveStreamingMetadata = getServeStreamingMetadata(ctx)

if (!options?.skipFlight) {
const preloadCallbacks: PreloadCallbacks = []

Expand All @@ -492,7 +505,7 @@ async function generateDynamicRSCPayload(
workStore,
MetadataBoundary,
ViewportBoundary,
serveStreamingMetadata: !!ctx.renderOpts.serveStreamingMetadata,
serveStreamingMetadata,
})

const { StreamingMetadata, StaticMetadata } =
Expand All @@ -501,7 +514,7 @@ async function generateDynamicRSCPayload(
// Adding requestId as react key to make metadata remount for each render
<MetadataTree key={requestId} />
)
}, !!ctx.renderOpts.serveStreamingMetadata)
}, serveStreamingMetadata)

flightData = (
await walkTreeWithFlightRouterState({
Expand Down Expand Up @@ -779,6 +792,7 @@ async function getRSCPayload(
getDynamicParamFromSegment,
query
)
const serveStreamingMetadata = getServeStreamingMetadata(ctx)

const searchParams = createServerSearchParamsForMetadata(query, workStore)
const { ViewportTree, MetadataTree, getViewportReady, getMetadataReady } =
Expand All @@ -797,7 +811,7 @@ async function getRSCPayload(
workStore,
MetadataBoundary,
ViewportBoundary,
serveStreamingMetadata: !!ctx.renderOpts.serveStreamingMetadata,
serveStreamingMetadata: serveStreamingMetadata,
})

const preloadCallbacks: PreloadCallbacks = []
Expand All @@ -808,7 +822,7 @@ async function getRSCPayload(
// Not add requestId as react key to ensure segment prefetch could result consistently if nothing changed
<MetadataTree />
)
}, !!ctx.renderOpts.serveStreamingMetadata)
}, serveStreamingMetadata)

const seedData = await createComponentTree({
ctx,
Expand Down Expand Up @@ -910,6 +924,7 @@ async function getErrorRSCPayload(
workStore,
} = ctx

const serveStreamingMetadata = getServeStreamingMetadata(ctx)
const searchParams = createServerSearchParamsForMetadata(query, workStore)
const { MetadataTree, ViewportTree } = createMetadataComponents({
tree,
Expand All @@ -924,7 +939,7 @@ async function getErrorRSCPayload(
workStore,
MetadataBoundary,
ViewportBoundary,
serveStreamingMetadata: !!ctx.renderOpts.serveStreamingMetadata,
serveStreamingMetadata: serveStreamingMetadata,
})

const { StreamingMetadata, StaticMetadata } =
Expand All @@ -935,7 +950,7 @@ async function getErrorRSCPayload(
<MetadataTree key={requestId} />
</React.Fragment>
),
!!ctx.renderOpts.serveStreamingMetadata
serveStreamingMetadata
)

const initialHead = (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Suspense } from 'react'

export default function Root({ children }) {
return <Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { connection } from 'next/server'

export default async function Page() {
return <p>hello world</p>
}

// Dynamic metadata
export async function generateMetadata() {
await connection()
await new Promise((resolve) => setTimeout(resolve, 1000))
return { title: 'dynamic-title' }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ReactNode } from 'react'
import Link from 'next/link'

export default function Root({ children }: { children: ReactNode }) {
return (
<html>
<body>
<nav>
<Link href="/ppr">ppr</Link>
<Link href="/dynamic">dynamic</Link>
</nav>
{children}
</body>
</html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Suspense } from 'react'

export default function Root({ children }) {
return <Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { connection } from 'next/server'

export default async function Page() {
await connection()
return <p>ppr</p>
}

export async function generateMetadata() {
await connection()
return { title: 'ppr-title' }
}

export const experimental_ppr = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
experimental: {
ppr: 'incremental',
streamingMetadata: true,
},
}

module.exports = nextConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { nextTestSetup } from 'e2e-utils'

describe('ppr-disable-streaming-metadata', () => {
const { next } = nextTestSetup({
files: __dirname,
skipDeployment: true,
env: {
// Using VERCEL=1 to simulate deployment on Vercel
VERCEL: '1',
},
})

it('should enable streaming metadata for suspenseful dynamic metadata page', async () => {
const $ = await next.render$('/dynamic')
expect($('body title').text()).toBe('dynamic-title')

const browser = await next.browser('/dynamic')
const title = await browser.eval(() => document.title)
expect(title).toBe('dynamic-title')
})

it('should not enable streaming metadata for ppr page', async () => {
const $ = await next.render$('/ppr')
// Dynamic metadata will be suspended and not rendered in default PPR mode
expect($('title').length).toBe(0)

const browser = await next.browser('/ppr')
const title = await browser.eval(() => document.title)
expect(title).toBe('ppr-title')
})
})

0 comments on commit 8b41f01

Please sign in to comment.