diff --git a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts
index aec39673abbac..8f0879241e62f 100644
--- a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts
+++ b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts
@@ -125,12 +125,19 @@ function getAppPathRequiredChunks(
// - app/foo/page -> app/foo
// - app/(group)/@named/foo/page -> app/foo
// - app/(.)foo/(..)bar/loading -> app/bar
+// - app/[...catchAll]/page -> app
+// - app/foo/@slot/[...catchAll]/page -> app/foo
function entryNameToGroupName(entryName: string) {
let groupName = entryName
.slice(0, entryName.lastIndexOf('/'))
+ // Remove slots
.replace(/\/@[^/]+/g, '')
// Remove the group with lookahead to make sure it's not interception route
.replace(/\/\([^/]+\)(?=(\/|$))/g, '')
+ // Remove catch-all routes since they should be part of the parent group that the catch-all would apply to.
+ // This is necessary to support parallel routes since multiple page components can be rendered on the same page.
+ // In order to do that, we need to ensure that the manifests are merged together by putting them in the same group.
+ .replace(/\/\[?\[\.\.\.[^\]]*\]\]?/g, '')
// Interception routes
groupName = groupName
diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/client-component.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/client-component.tsx
new file mode 100644
index 0000000000000..07d98879aa600
--- /dev/null
+++ b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/client-component.tsx
@@ -0,0 +1,5 @@
+'use client'
+
+export function ClientComponent() {
+ return
catchall page client component
+}
diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx
index a305e6e15809b..69be5f30859b8 100644
--- a/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx
+++ b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx
@@ -1,3 +1,9 @@
+import { ClientComponent } from './client-component'
+
export default function Page() {
- return 'main catchall'
+ return (
+
+ main catchall
+
+ )
}
diff --git a/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts b/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts
index 0c4fc9d01a170..fdd66c166ca5c 100644
--- a/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts
+++ b/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts
@@ -330,7 +330,11 @@ createNextDescribe(
await browser.elementByCss('[href="/parallel-catchall/baz"]').click()
await check(
() => browser.waitForElementByCss('#main').text(),
- 'main catchall'
+ /main catchall/
+ )
+ await check(
+ () => browser.waitForElementByCss('#main').text(),
+ /catchall page client component/
)
await check(
() => browser.waitForElementByCss('#slot-content').text(),
diff --git a/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/client-component.tsx b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/client-component.tsx
new file mode 100644
index 0000000000000..494aaeacfad03
--- /dev/null
+++ b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/client-component.tsx
@@ -0,0 +1,5 @@
+'use client'
+
+export function ClientComponent() {
+ return catchall slot client component
+}
diff --git a/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx
index c53a5bb486dbe..6a63e87121129 100644
--- a/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx
+++ b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx
@@ -1,3 +1,9 @@
+import { ClientComponent } from './client-component'
+
export default function Page() {
- return 'slot catchall'
+ return (
+
+ slot catchall
+
+ )
}
diff --git a/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts b/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts
index 38952ae3ffffc..5a5aa0ee80383 100644
--- a/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts
+++ b/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts
@@ -29,11 +29,19 @@ createNextDescribe(
// so we'd expect to see the catch-all slot & the page content
await check(() => browser.elementById('children').text(), /bar/)
await check(() => browser.elementById('slot').text(), /slot catchall/)
+ await check(
+ () => browser.elementById('slot').text(),
+ /catchall slot client component/
+ )
})
it('should match correctly when defining an explicit slot but no page', async () => {
const browser = await next.browser('/')
await check(() => browser.elementById('slot').text(), /slot catchall/)
+ await check(
+ () => browser.elementById('slot').text(),
+ /catchall slot client component/
+ )
await browser.elementByCss('[href="/baz"]').click()
@@ -46,12 +54,19 @@ createNextDescribe(
it('should match both the catch-all page & slot', async () => {
const browser = await next.browser('/')
await check(() => browser.elementById('slot').text(), /slot catchall/)
-
+ await check(
+ () => browser.elementById('slot').text(),
+ /catchall slot client component/
+ )
await browser.elementByCss('[href="/quux"]').click()
// quux doesn't have a page or slot defined. It should use the catch-all for both
await check(() => browser.elementById('children').text(), /main catchall/)
await check(() => browser.elementById('slot').text(), /slot catchall/)
+ await check(
+ () => browser.elementById('slot').text(),
+ /catchall slot client component/
+ )
})
}
)