diff --git a/packages/next/src/lib/metadata/resolve-metadata.ts b/packages/next/src/lib/metadata/resolve-metadata.ts
index 94b692ca8fefc..25e337908040d 100644
--- a/packages/next/src/lib/metadata/resolve-metadata.ts
+++ b/packages/next/src/lib/metadata/resolve-metadata.ts
@@ -51,7 +51,7 @@ import {
import { resolveIcons } from './resolvers/resolve-icons'
import { getTracer } from '../../server/lib/trace/tracer'
import { ResolveMetadataSpan } from '../../server/lib/trace/constants'
-import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment'
+import { DEFAULT_SEGMENT_KEY, PAGE_SEGMENT_KEY } from '../../shared/lib/segment'
import * as Log from '../../build/output/log'
import type { WorkStore } from '../../server/app-render/work-async-storage.external'
import type {
@@ -443,6 +443,7 @@ async function collectMetadata({
const hasErrorConventionComponent = Boolean(
errorConvention && tree[2][errorConvention]
)
+
if (errorConvention) {
mod = await getComponentTypeModule(tree, 'layout')
modType = errorConvention
@@ -550,6 +551,15 @@ async function resolveMetadataItemsImpl(
}
}
+ const pageKey = tree[0]
+
+ // If it's default.js, ignore the metadata
+ // webpack loader: pageKey of the default.js is DEFAULT_SEGMENT_KEY ('__DEFAULT__')
+ // turbopack loader: pageKey of the parallel route @bar is 'page$'
+ const isDefaultPage = pageKey === DEFAULT_SEGMENT_KEY || pageKey === 'page$'
+ if (isDefaultPage) {
+ return metadataItems
+ }
await collectMetadata({
tree,
metadataItems,
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/layout.tsx
new file mode 100644
index 0000000000000..621aea648e1a0
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/layout.tsx
@@ -0,0 +1,19 @@
+import Link from 'next/link'
+import React from 'react'
+
+export default function Root({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+ to nested
+
+
+ to nested subroute
+
+ Root Layout
+ {children}
+
+
+ )
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/default.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/default.tsx
new file mode 100644
index 0000000000000..014c2d9b53246
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/default.tsx
@@ -0,0 +1,11 @@
+export default function Default() {
+ console.log(
+ '@bar default page',
+ typeof window !== 'undefined' ? 'client' : 'server'
+ )
+ return '@bar default'
+}
+
+export const metadata = {
+ title: '@bar - page',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/layout.tsx
new file mode 100644
index 0000000000000..2e09ca74cc17d
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/layout.tsx
@@ -0,0 +1,8 @@
+export default function Layout({ children }) {
+ return (
+
+
@bar Layout
+
{children}
+
+ )
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/page.tsx
new file mode 100644
index 0000000000000..dd9ee3b726d00
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/page.tsx
@@ -0,0 +1,7 @@
+export default function Page() {
+ return Bar Slot
+}
+
+export const metadata = {
+ title: '@bar - page',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/subroute/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/subroute/page.tsx
new file mode 100644
index 0000000000000..d351cc540fa7e
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/subroute/page.tsx
@@ -0,0 +1,7 @@
+export default function Page() {
+ return Subroute
+}
+
+export const metadata = {
+ title: 'subroute - page',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/default.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/default.tsx
new file mode 100644
index 0000000000000..7adcff2087d8c
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/default.tsx
@@ -0,0 +1,11 @@
+export default function Default() {
+ console.log(
+ 'foo default default page',
+ typeof window !== 'undefined' ? 'client' : 'server'
+ )
+ return '@foo default'
+}
+
+export const metadata = {
+ title: '@foo - default',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/layout.tsx
new file mode 100644
index 0000000000000..ab0a56dffff47
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/layout.tsx
@@ -0,0 +1,8 @@
+export default function Layout({ children }) {
+ return (
+
+
@foo Layout
+
{children}
+
+ )
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/page.tsx
new file mode 100644
index 0000000000000..07028d5f5da9c
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/page.tsx
@@ -0,0 +1,7 @@
+export default function Page() {
+ return Foo Slot
+}
+
+export const metadata = {
+ title: '@foo - page',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/default.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/default.tsx
new file mode 100644
index 0000000000000..bef9e8b7075ad
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/default.tsx
@@ -0,0 +1,11 @@
+export default function Page() {
+ console.log(
+ 'nested default page',
+ typeof window !== 'undefined' ? 'client' : 'server'
+ )
+ return 'nested default page'
+}
+
+export const metadata = {
+ title: 'nested - default',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/layout.tsx
new file mode 100644
index 0000000000000..467f9ca246a53
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/layout.tsx
@@ -0,0 +1,10 @@
+export default function Layout({ children, bar, foo }) {
+ return (
+
+
Nested Layout
+
{children}
+
{foo}
+
{bar}
+
+ )
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/page.tsx
new file mode 100644
index 0000000000000..5c6924d018e43
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/page.tsx
@@ -0,0 +1,7 @@
+export default function Page() {
+ return Hello from Nested
+}
+
+export const metadata = {
+ title: 'nested - page',
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/page.tsx
new file mode 100644
index 0000000000000..c9f9a60006193
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/app/page.tsx
@@ -0,0 +1,3 @@
+export default async function Home() {
+ return Hello World
+}
diff --git a/test/e2e/app-dir/metadata-parallel-routes/metadata-parallel-routes.test.ts b/test/e2e/app-dir/metadata-parallel-routes/metadata-parallel-routes.test.ts
new file mode 100644
index 0000000000000..2f35e83e25c81
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/metadata-parallel-routes.test.ts
@@ -0,0 +1,22 @@
+import { nextTestSetup } from 'e2e-utils'
+
+describe('metadata-parallel-routes', () => {
+ const { next } = nextTestSetup({
+ files: __dirname,
+ })
+
+ it('should ignore metadata from default.js in normal route', async () => {
+ const $ = await next.render$('/')
+ // When there's no title, parallel routes should not affect it
+ expect($('title').length).toBe(0)
+
+ // When there's defined title, parallel routes should not affect it
+ const $nested = await next.render$('/nested')
+ expect($nested('title').text()).toBe('nested - page')
+ })
+
+ it('should ignore metadata from default.js in parallel routes', async () => {
+ const $ = await next.render$('/nested/subroute')
+ expect($('title').text()).toBe('subroute - page')
+ })
+})
diff --git a/test/e2e/app-dir/metadata-parallel-routes/next.config.js b/test/e2e/app-dir/metadata-parallel-routes/next.config.js
new file mode 100644
index 0000000000000..807126e4cf0bf
--- /dev/null
+++ b/test/e2e/app-dir/metadata-parallel-routes/next.config.js
@@ -0,0 +1,6 @@
+/**
+ * @type {import('next').NextConfig}
+ */
+const nextConfig = {}
+
+module.exports = nextConfig
diff --git a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx
index 86d9ad310e688..24edb5a8595ec 100644
--- a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx
+++ b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx
@@ -1,3 +1,7 @@
export default function Page() {
return Bar Slot
}
+
+export const metadata = {
+ title: 'nested - page',
+}
diff --git a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx
index 83be0d5a350c7..d351cc540fa7e 100644
--- a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx
+++ b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx
@@ -1,3 +1,7 @@
export default function Page() {
return Subroute
}
+
+export const metadata = {
+ title: 'subroute - page',
+}