diff --git a/packages/next/src/export/index.ts b/packages/next/src/export/index.ts index 06088024258fe..e341d1359a4e9 100644 --- a/packages/next/src/export/index.ts +++ b/packages/next/src/export/index.ts @@ -387,11 +387,7 @@ async function exportAppImpl( strictNextHead: nextConfig.experimental.strictNextHead ?? true, deploymentId: nextConfig.deploymentId, htmlLimitedBots: nextConfig.htmlLimitedBots.source, - streamingMetadata: - // Disable streaming metadata when dynamic IO is enabled. - // FIXME: remove dynamic IO guard once we fixed the dynamic indicator case. - // test/e2e/app-dir/dynamic-io/dynamic-io.test.ts - should not have static indicator on not-found route - !nextConfig.experimental.dynamicIO, + streamingMetadata: true, experimental: { clientTraceMetadata: nextConfig.experimental.clientTraceMetadata, expireTime: nextConfig.expireTime, diff --git a/packages/next/src/export/worker.ts b/packages/next/src/export/worker.ts index 8e28db7a4d635..72d619fcd8af0 100644 --- a/packages/next/src/export/worker.ts +++ b/packages/next/src/export/worker.ts @@ -425,11 +425,7 @@ export async function exportPages( enableExperimentalReact: needsExperimentalReact(nextConfig), sriEnabled: Boolean(nextConfig.experimental.sri?.algorithm), buildId: input.buildId, - streamingMetadata: - // Disable streaming metadata when dynamic IO is enabled. - // FIXME: remove dynamic IO guard once we fixed the dynamic indicator case. - // test/e2e/app-dir/dynamic-io/dynamic-io.test.ts - should not have static indicator on not-found route - !nextConfig.experimental.dynamicIO, + streamingMetadata: true, }), // If exporting the page takes longer than the timeout, reject the promise. new Promise((_, reject) => { diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 9881709dc6a10..82a599d5324a1 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -2446,6 +2446,7 @@ async function spawnDynamicValidationInDev( } ) + let rootDidError = false const serverPhasedStream = serverPrerenderStreamResult.asPhasedStream() try { const prerender = require('react-dom/static.edge') @@ -2476,7 +2477,12 @@ async function spawnDynamicValidationInDev( isPrerenderInterruptedError(err) || finalClientController.signal.aborted ) { - requestStore.usedDynamic = true + if (!rootDidError) { + // If the root errored before we observe this error then it wasn't caused by something dynamic. + // If the root did not error or is erroring because of a sync dynamic API or a prerender interrupt error + // then we are a dynamic route. + requestStore.usedDynamic = true + } const componentStack = errorInfo.componentStack if (typeof componentStack === 'string') { @@ -2501,6 +2507,7 @@ async function spawnDynamicValidationInDev( } ) } catch (err) { + rootDidError = true if ( isPrerenderInterruptedError(err) || finalClientController.signal.aborted diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index c87ecf85ca254..63329722af0ed 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -600,11 +600,7 @@ export default abstract class Server< isExperimentalCompile: this.nextConfig.experimental.isExperimentalCompile, // `htmlLimitedBots` is passed to server as serialized config in string format htmlLimitedBots: this.nextConfig.htmlLimitedBots, - streamingMetadata: - // Disable streaming metadata when dynamic IO is enabled. - // FIXME: remove dynamic IO guard once we fixed the dynamic indicator case. - // test/e2e/app-dir/dynamic-io/dynamic-io.test.ts - should not have static indicator on not-found route - !this.nextConfig.experimental.dynamicIO, + streamingMetadata: true, experimental: { expireTime: this.nextConfig.expireTime, clientTraceMetadata: this.nextConfig.experimental.clientTraceMetadata, diff --git a/test/e2e/app-dir/dynamic-io-errors/dynamic-io-errors.test.ts b/test/e2e/app-dir/dynamic-io-errors/dynamic-io-errors.test.ts index 970be876b8867..605d9f4cadadb 100644 --- a/test/e2e/app-dir/dynamic-io-errors/dynamic-io-errors.test.ts +++ b/test/e2e/app-dir/dynamic-io-errors/dynamic-io-errors.test.ts @@ -159,10 +159,16 @@ function runTests(options: { withMinification: boolean }) { throw new Error('expected build not to fail for fully static project') } - expect(next.cliOutput).toContain('ƒ / ') - const $ = await next.render$('/') - expect($('#dynamic').text()).toBe('Dynamic') - expect($('[data-fallback]').length).toBe(0) + if (WITH_PPR) { + expect(next.cliOutput).toContain('◐ / ') + const $ = await next.render$('/') + expect($('#dynamic').text()).toBe('Dynamic') + expect($('[data-fallback]').length).toBe(1) + } else { + expect(next.cliOutput).toContain('ƒ / ') + const $ = await next.render$('/') + expect($('#dynamic').text()).toBe('Dynamic') + } }) })