diff --git a/packages/next/src/build/webpack/loaders/next-app-loader.ts b/packages/next/src/build/webpack/loaders/next-app-loader.ts index 3ba6c72cc6acd..e0922628c7795 100644 --- a/packages/next/src/build/webpack/loaders/next-app-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-app-loader.ts @@ -75,6 +75,10 @@ export type ComponentsType = { readonly defaultPage?: ModuleReference } +function isGroupSegment(segment: string) { + return segment.startsWith('(') && segment.endsWith(')') +} + async function createAppRouteCode({ name, page, @@ -225,6 +229,7 @@ async function createTreeCodeFromPath( // Existing tree are the children of the current segment const props: Record = {} + // Root layer could be 1st layer of normal routes const isRootLayer = segments.length === 0 const isRootLayoutOrRootPage = segments.length <= 1 @@ -314,10 +319,14 @@ async function createTreeCodeFromPath( ) // Add default not found error as root not found if not present - const hasNotFound = definedFilePaths.some( + const hasRootNotFound = definedFilePaths.some( ([type]) => type === 'not-found' ) - if (isRootLayer && !hasNotFound) { + // If the first layer is a group route, we treat it as root layer + const isFirstLayerGroupRoute = + segments.length === 1 && + subSegmentPath.filter((seg) => isGroupSegment(seg)).length === 1 + if ((isRootLayer || isFirstLayerGroupRoute) && !hasRootNotFound) { definedFilePaths.push(['not-found', defaultNotFoundPath]) } diff --git a/test/e2e/app-dir/not-found-default/app/(group)/group-dynamic/[id]/page.js b/test/e2e/app-dir/not-found-default/app/(group)/group-dynamic/[id]/page.js new file mode 100644 index 0000000000000..f28071f5be5b5 --- /dev/null +++ b/test/e2e/app-dir/not-found-default/app/(group)/group-dynamic/[id]/page.js @@ -0,0 +1,9 @@ +import { notFound } from 'next/navigation' + +export default function Page({ params }) { + if (params.id === '404') { + notFound() + } + + return

{`group-dynamic [id]`}

+} diff --git a/test/e2e/app-dir/not-found-default/app/(group)/layout.js b/test/e2e/app-dir/not-found-default/app/(group)/layout.js new file mode 100644 index 0000000000000..87f98c9009427 --- /dev/null +++ b/test/e2e/app-dir/not-found-default/app/(group)/layout.js @@ -0,0 +1,7 @@ +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/not-found-default/index.test.ts b/test/e2e/app-dir/not-found-default/index.test.ts index e5affe17bfee4..1919731972098 100644 --- a/test/e2e/app-dir/not-found-default/index.test.ts +++ b/test/e2e/app-dir/not-found-default/index.test.ts @@ -72,5 +72,18 @@ createNextDescribe( 'This page could not be found.' ) }) + + it('should render default not found for group routes if not found is not defined', async () => { + const browser = await next.browser('/group-dynamic/123') + expect(await browser.elementByCss('#page').text()).toBe( + 'group-dynamic [id]' + ) + + await browser.loadPage(next.url + '/group-dynamic/404') + expect(await browser.elementByCss('.next-error-h1').text()).toBe('404') + expect(await browser.elementByCss('html').getAttribute('class')).toBe( + 'group-root-layout' + ) + }) } )