diff --git a/packages/next/src/server/app-render/create-component-tree.tsx b/packages/next/src/server/app-render/create-component-tree.tsx index 209c237b5e047..e28dfeda953dd 100644 --- a/packages/next/src/server/app-render/create-component-tree.tsx +++ b/packages/next/src/server/app-render/create-component-tree.tsx @@ -21,7 +21,6 @@ import type { LoadingModuleData } from '../../shared/lib/app-router-context.shar import type { Params } from '../request/params' import { workUnitAsyncStorage } from './work-unit-async-storage.external' import { OUTLET_BOUNDARY_NAME } from '../../lib/metadata/metadata-constants' -import { DEFAULT_SEGMENT_KEY } from '../../shared/lib/segment' import type { UseCachePageComponentProps } from '../use-cache/use-cache-wrapper' /** @@ -394,19 +393,12 @@ async function createComponentTreeInternal({ // Resolve the segment param const actualSegment = segmentParam ? segmentParam.treeSegment : segment - - // Only render metadata on the actual SSR'd segment not the `default` segment, - // as it's used as a placeholder for navigation. - const isNotDefaultSegment = actualSegment !== DEFAULT_SEGMENT_KEY - - const metadata = - isNotDefaultSegment && StreamingMetadata ? : undefined + const metadata = StreamingMetadata ? : undefined // Use the same condition to render metadataOutlet as metadata - const metadataOutlet = - isNotDefaultSegment && StreamingMetadataOutlet ? ( - - ) : undefined + const metadataOutlet = StreamingMetadataOutlet ? ( + + ) : undefined const notFoundElement = NotFound ? ( <> diff --git a/test/e2e/app-dir/metadata-streaming/app/dynamic-api/page.tsx b/test/e2e/app-dir/metadata-streaming/app/dynamic-api/page.tsx index 6a521e8de1f33..af601aced5059 100644 --- a/test/e2e/app-dir/metadata-streaming/app/dynamic-api/page.tsx +++ b/test/e2e/app-dir/metadata-streaming/app/dynamic-api/page.tsx @@ -23,7 +23,7 @@ async function SubComponent() { export async function generateMetadata() { await connection() - await new Promise((resolve) => setTimeout(resolve, 3000)) + await new Promise((resolve) => setTimeout(resolve, 800)) return { title: `Dynamic api ${Math.random()}`, } diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/default.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/default.tsx new file mode 100644 index 0000000000000..e355998a4433c --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/default.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'default @bar' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/layout.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/layout.tsx new file mode 100644 index 0000000000000..1e18e748cf81e --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/layout.tsx @@ -0,0 +1,8 @@ +export default function Layout({ children }) { + return ( +
+

@bar Layout

+
{children}
+
+ ) +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/page.tsx new file mode 100644 index 0000000000000..eb4a3e2e9df71 --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'page @bar' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/test-page/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/test-page/page.tsx new file mode 100644 index 0000000000000..bff087a3a98f9 --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@bar/test-page/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'test-page @bar' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/default.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/default.tsx new file mode 100644 index 0000000000000..408576f1876b5 --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/default.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'default @foo' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/layout.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/layout.tsx new file mode 100644 index 0000000000000..05a3bda4f7be8 --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/layout.tsx @@ -0,0 +1,8 @@ +export default function Layout({ children }) { + return ( +
+

@foo Layout

+
{children}
+
+ ) +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/no-bar/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/no-bar/page.tsx new file mode 100644 index 0000000000000..44ab40e2ddfce --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/no-bar/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'no-bar @foo' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/page.tsx new file mode 100644 index 0000000000000..3536981cea3ff --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'page @foo' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/test-page/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/test-page/page.tsx new file mode 100644 index 0000000000000..8eff01dbe6dec --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/@foo/test-page/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'test-page @foo' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/default.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/default.tsx new file mode 100644 index 0000000000000..2b713383cfcdb --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/default.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return 'default no-children' +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/layout.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/layout.tsx new file mode 100644 index 0000000000000..907190e027c57 --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/layout.tsx @@ -0,0 +1,22 @@ +import { connection } from 'next/server' + +export default function Layout({ bar, foo, children }) { + return ( +
+

Parallel Routes Layout - No Children

+
{foo}
+
{bar}
+ + {/* hitting default.js */} + {children} +
+ ) +} + +export async function generateMetadata() { + await connection() + await new Promise((resolve) => setTimeout(resolve, 300)) + return { + title: 'parallel-routes-default layout title', + } +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/no-bar/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/no-bar/page.tsx new file mode 100644 index 0000000000000..640e2e61603f7 --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/no-bar/page.tsx @@ -0,0 +1,13 @@ +import { connection } from 'next/server' + +export default function TestPage() { + return 'test page' +} + +export async function generateMetadata() { + await connection() + await new Promise((resolve) => setTimeout(resolve, 3000)) + return { + title: `Dynamic api ${Math.random()}`, + } +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/test-page/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/test-page/page.tsx new file mode 100644 index 0000000000000..4fee1a94819fb --- /dev/null +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes-default/test-page/page.tsx @@ -0,0 +1,13 @@ +import { connection } from 'next/server' + +export default function TestPage() { + return 'test page' +} + +export async function generateMetadata() { + await connection() + await new Promise((resolve) => setTimeout(resolve, 1000)) + return { + title: `Dynamic api ${Math.random()}`, + } +} diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes/no-bar/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes/no-bar/page.tsx index 640e2e61603f7..773d8bbaa1719 100644 --- a/test/e2e/app-dir/metadata-streaming/app/parallel-routes/no-bar/page.tsx +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes/no-bar/page.tsx @@ -1,13 +1,20 @@ +import Link from 'next/link' import { connection } from 'next/server' -export default function TestPage() { - return 'test page' +export default function Page() { + return ( +
+

no bar

+ Back to /parallel-routes +
+ ) } export async function generateMetadata() { await connection() - await new Promise((resolve) => setTimeout(resolve, 3000)) + await new Promise((resolve) => setTimeout(resolve, 1000)) return { title: `Dynamic api ${Math.random()}`, + description: 'no-bar description', } } diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes/page.tsx index 5739a4c02e37b..4ce809e3634d1 100644 --- a/test/e2e/app-dir/metadata-streaming/app/parallel-routes/page.tsx +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes/page.tsx @@ -3,10 +3,11 @@ import Link from 'next/link' export default function Page() { return (
- Hello from Nested{' '} + Hello from Nested
To /parallel-routes/test-page +
To /parallel-routes/no-bar
) @@ -14,4 +15,5 @@ export default function Page() { export const metadata = { title: 'parallel title', + description: 'parallel description', } diff --git a/test/e2e/app-dir/metadata-streaming/app/parallel-routes/test-page/page.tsx b/test/e2e/app-dir/metadata-streaming/app/parallel-routes/test-page/page.tsx index 640e2e61603f7..4fee1a94819fb 100644 --- a/test/e2e/app-dir/metadata-streaming/app/parallel-routes/test-page/page.tsx +++ b/test/e2e/app-dir/metadata-streaming/app/parallel-routes/test-page/page.tsx @@ -6,7 +6,7 @@ export default function TestPage() { export async function generateMetadata() { await connection() - await new Promise((resolve) => setTimeout(resolve, 3000)) + await new Promise((resolve) => setTimeout(resolve, 1000)) return { title: `Dynamic api ${Math.random()}`, } diff --git a/test/e2e/app-dir/metadata-streaming/metadata-streaming.test.ts b/test/e2e/app-dir/metadata-streaming/metadata-streaming.test.ts index eae22f99affe4..121df695128ae 100644 --- a/test/e2e/app-dir/metadata-streaming/metadata-streaming.test.ts +++ b/test/e2e/app-dir/metadata-streaming/metadata-streaming.test.ts @@ -93,9 +93,11 @@ describe('app-dir - metadata-streaming', () => { expect((await browser.elementsByCss('head title')).length).toBe(1) expect((await browser.elementsByCss('body title')).length).toBe(0) + expect(await browser.elementByCss('title').text()).toBe('parallel title') const $ = await next.render$('/parallel-routes') expect($('title').length).toBe(1) + expect($('head title').text()).toBe('parallel title') // validate behavior remains the same on client navigations await browser.elementByCss('[href="/parallel-routes/test-page"]').click() @@ -113,13 +115,29 @@ describe('app-dir - metadata-streaming', () => { const browser = await next.browser('/parallel-routes') await browser.elementByCss('[href="/parallel-routes/no-bar"]').click() + // Wait for navigation is finished and metadata is updated await retry(async () => { expect(await browser.elementByCss('title').text()).toContain( 'Dynamic api' ) }) + await retry(async () => { + expect((await browser.elementsByCss('title')).length).toBe(1) + }) + }) + + it('should still render metadata if children is not rendered in parallel routes layout', async () => { + const browser = await next.browser('/parallel-routes-default') + expect((await browser.elementsByCss('title')).length).toBe(1) + expect(await browser.elementByCss('body title').text()).toBe( + 'parallel-routes-default layout title' + ) + + const $ = await next.render$('/parallel-routes-default') + expect($('title').length).toBe(1) + expect($('body title').text()).toBe('parallel-routes-default layout title') }) describe('dynamic api', () => {