Skip to content

Commit 4698ad6

Browse files
committedMar 17, 2025
[metadata] remove the default segement check for metadata rendering (#77119)
### What Remove the default segment checks for metadata rendering. When the `default.js` is rendered, it should still have the metadata. It was a condition added due to possible of duplicated metadata, but it's resolved by other conditions such as only rendering in child slot, and hoisting metadata into rsc head for navigation.
1 parent 1e1ff40 commit 4698ad6

File tree

19 files changed

+125
-18
lines changed

19 files changed

+125
-18
lines changed
 

‎packages/next/src/server/app-render/create-component-tree.tsx

+4-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import type { LoadingModuleData } from '../../shared/lib/app-router-context.shar
2121
import type { Params } from '../request/params'
2222
import { workUnitAsyncStorage } from './work-unit-async-storage.external'
2323
import { OUTLET_BOUNDARY_NAME } from '../../lib/metadata/metadata-constants'
24-
import { DEFAULT_SEGMENT_KEY } from '../../shared/lib/segment'
2524
import type { UseCachePageComponentProps } from '../use-cache/use-cache-wrapper'
2625

2726
/**
@@ -394,19 +393,12 @@ async function createComponentTreeInternal({
394393

395394
// Resolve the segment param
396395
const actualSegment = segmentParam ? segmentParam.treeSegment : segment
397-
398-
// Only render metadata on the actual SSR'd segment not the `default` segment,
399-
// as it's used as a placeholder for navigation.
400-
const isNotDefaultSegment = actualSegment !== DEFAULT_SEGMENT_KEY
401-
402-
const metadata =
403-
isNotDefaultSegment && StreamingMetadata ? <StreamingMetadata /> : undefined
396+
const metadata = StreamingMetadata ? <StreamingMetadata /> : undefined
404397

405398
// Use the same condition to render metadataOutlet as metadata
406-
const metadataOutlet =
407-
isNotDefaultSegment && StreamingMetadataOutlet ? (
408-
<StreamingMetadataOutlet />
409-
) : undefined
399+
const metadataOutlet = StreamingMetadataOutlet ? (
400+
<StreamingMetadataOutlet />
401+
) : undefined
410402

411403
const notFoundElement = NotFound ? (
412404
<>

‎test/e2e/app-dir/metadata-streaming/app/dynamic-api/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async function SubComponent() {
2323

2424
export async function generateMetadata() {
2525
await connection()
26-
await new Promise((resolve) => setTimeout(resolve, 3000))
26+
await new Promise((resolve) => setTimeout(resolve, 800))
2727
return {
2828
title: `Dynamic api ${Math.random()}`,
2929
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'default @bar'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function Layout({ children }) {
2+
return (
3+
<div>
4+
<h2>@bar Layout</h2>
5+
<div id="bar-children">{children}</div>
6+
</div>
7+
)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'page @bar'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'test-page @bar'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'default @foo'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function Layout({ children }) {
2+
return (
3+
<div>
4+
<h2>@foo Layout</h2>
5+
<div id="foo-children">{children}</div>
6+
</div>
7+
)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'no-bar @foo'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'page @foo'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'test-page @foo'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'default no-children'
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { connection } from 'next/server'
2+
3+
export default function Layout({ bar, foo, children }) {
4+
return (
5+
<div>
6+
<h1>Parallel Routes Layout - No Children</h1>
7+
<div id="foo-slot">{foo}</div>
8+
<div id="bar-slot">{bar}</div>
9+
10+
{/* hitting default.js */}
11+
{children}
12+
</div>
13+
)
14+
}
15+
16+
export async function generateMetadata() {
17+
await connection()
18+
await new Promise((resolve) => setTimeout(resolve, 300))
19+
return {
20+
title: 'parallel-routes-default layout title',
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { connection } from 'next/server'
2+
3+
export default function TestPage() {
4+
return 'test page'
5+
}
6+
7+
export async function generateMetadata() {
8+
await connection()
9+
await new Promise((resolve) => setTimeout(resolve, 3000))
10+
return {
11+
title: `Dynamic api ${Math.random()}`,
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { connection } from 'next/server'
2+
3+
export default function TestPage() {
4+
return 'test page'
5+
}
6+
7+
export async function generateMetadata() {
8+
await connection()
9+
await new Promise((resolve) => setTimeout(resolve, 1000))
10+
return {
11+
title: `Dynamic api ${Math.random()}`,
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
import Link from 'next/link'
12
import { connection } from 'next/server'
23

3-
export default function TestPage() {
4-
return 'test page'
4+
export default function Page() {
5+
return (
6+
<div>
7+
<p>no bar</p>
8+
<Link href="/parallel-routes">Back to /parallel-routes</Link>
9+
</div>
10+
)
511
}
612

713
export async function generateMetadata() {
814
await connection()
9-
await new Promise((resolve) => setTimeout(resolve, 3000))
15+
await new Promise((resolve) => setTimeout(resolve, 1000))
1016
return {
1117
title: `Dynamic api ${Math.random()}`,
18+
description: 'no-bar description',
1219
}
1320
}

‎test/e2e/app-dir/metadata-streaming/app/parallel-routes/page.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ import Link from 'next/link'
33
export default function Page() {
44
return (
55
<div>
6-
Hello from Nested{' '}
6+
Hello from Nested <br />
77
<Link href="/parallel-routes/test-page">
88
To /parallel-routes/test-page
99
</Link>
10+
<br />
1011
<Link href="/parallel-routes/no-bar">To /parallel-routes/no-bar</Link>
1112
</div>
1213
)
1314
}
1415

1516
export const metadata = {
1617
title: 'parallel title',
18+
description: 'parallel description',
1719
}

‎test/e2e/app-dir/metadata-streaming/app/parallel-routes/test-page/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default function TestPage() {
66

77
export async function generateMetadata() {
88
await connection()
9-
await new Promise((resolve) => setTimeout(resolve, 3000))
9+
await new Promise((resolve) => setTimeout(resolve, 1000))
1010
return {
1111
title: `Dynamic api ${Math.random()}`,
1212
}

‎test/e2e/app-dir/metadata-streaming/metadata-streaming.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,11 @@ describe('app-dir - metadata-streaming', () => {
9393

9494
expect((await browser.elementsByCss('head title')).length).toBe(1)
9595
expect((await browser.elementsByCss('body title')).length).toBe(0)
96+
expect(await browser.elementByCss('title').text()).toBe('parallel title')
9697

9798
const $ = await next.render$('/parallel-routes')
9899
expect($('title').length).toBe(1)
100+
expect($('head title').text()).toBe('parallel title')
99101

100102
// validate behavior remains the same on client navigations
101103
await browser.elementByCss('[href="/parallel-routes/test-page"]').click()
@@ -113,13 +115,29 @@ describe('app-dir - metadata-streaming', () => {
113115
const browser = await next.browser('/parallel-routes')
114116
await browser.elementByCss('[href="/parallel-routes/no-bar"]').click()
115117

118+
// Wait for navigation is finished and metadata is updated
116119
await retry(async () => {
117120
expect(await browser.elementByCss('title').text()).toContain(
118121
'Dynamic api'
119122
)
120123
})
121124

125+
await retry(async () => {
126+
expect((await browser.elementsByCss('title')).length).toBe(1)
127+
})
128+
})
129+
130+
it('should still render metadata if children is not rendered in parallel routes layout', async () => {
131+
const browser = await next.browser('/parallel-routes-default')
132+
122133
expect((await browser.elementsByCss('title')).length).toBe(1)
134+
expect(await browser.elementByCss('body title').text()).toBe(
135+
'parallel-routes-default layout title'
136+
)
137+
138+
const $ = await next.render$('/parallel-routes-default')
139+
expect($('title').length).toBe(1)
140+
expect($('body title').text()).toBe('parallel-routes-default layout title')
123141
})
124142

125143
describe('dynamic api', () => {

0 commit comments

Comments
 (0)