Skip to content

Commit 963198c

Browse files
committed
fix(react-router): walk parent route chain for notFoundComponent lookup
1 parent 0592d49 commit 963198c

File tree

2 files changed

+89
-12
lines changed

2 files changed

+89
-12
lines changed

packages/react-router/src/renderRouteNotFound.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,25 @@ export function renderRouteNotFound(
1616
route: AnyRoute,
1717
data: any,
1818
) {
19-
if (!route.options.notFoundComponent) {
20-
if (router.options.defaultNotFoundComponent) {
21-
return <router.options.defaultNotFoundComponent {...data} />
22-
}
19+
let routeWithNotFound: AnyRoute | undefined = route
20+
while (routeWithNotFound && !routeWithNotFound.options.notFoundComponent) {
21+
routeWithNotFound = routeWithNotFound.parentRoute
22+
}
23+
24+
if (routeWithNotFound?.options.notFoundComponent) {
25+
return <routeWithNotFound.options.notFoundComponent {...data} />
26+
}
2327

24-
if (process.env.NODE_ENV === 'development') {
25-
warning(
26-
route.options.notFoundComponent,
27-
`A notFoundError was encountered on the route with ID "${route.id}", but a notFoundComponent option was not configured, nor was a router level defaultNotFoundComponent configured. Consider configuring at least one of these to avoid TanStack Router's overly generic defaultNotFoundComponent (<p>Not Found</p>)`,
28-
)
29-
}
28+
if (router.options.defaultNotFoundComponent) {
29+
return <router.options.defaultNotFoundComponent {...data} />
30+
}
3031

31-
return <DefaultGlobalNotFound />
32+
if (process.env.NODE_ENV === 'development') {
33+
warning(
34+
false,
35+
`A notFoundError was encountered on the route with ID "${route.id}", but a notFoundComponent option was not configured, nor was a router level defaultNotFoundComponent configured. Consider configuring at least one of these to avoid TanStack Router's overly generic defaultNotFoundComponent (<p>Not Found</p>)`,
36+
)
3237
}
3338

34-
return <route.options.notFoundComponent {...data} />
39+
return <DefaultGlobalNotFound />
3540
}

packages/react-router/tests/not-found.test.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,75 @@ test('defaultNotFoundComponent and notFoundComponent receives data props via spr
245245
const errorMessageComponent = await screen.findByTestId('message')
246246
expect(errorMessageComponent).toHaveTextContent(customData.message)
247247
})
248+
249+
test('pathless layout route notFoundComponent is used when child has none (issue #6351)', async () => {
250+
const rootRoute = createRootRoute({
251+
component: () => (
252+
<div data-testid="root">
253+
<Outlet />
254+
</div>
255+
),
256+
})
257+
258+
const authenticatedRoute = createRoute({
259+
getParentRoute: () => rootRoute,
260+
id: '_authenticated',
261+
component: () => (
262+
<div data-testid="authenticated-layout">
263+
<Outlet />
264+
</div>
265+
),
266+
notFoundComponent: () => (
267+
<div data-testid="authenticated-not-found">
268+
Authenticated Not Found
269+
</div>
270+
),
271+
})
272+
273+
const agentsRoute = createRoute({
274+
getParentRoute: () => authenticatedRoute,
275+
path: 'agents',
276+
component: () => (
277+
<div data-testid="agents-layout">
278+
<Outlet />
279+
</div>
280+
),
281+
})
282+
283+
const agentsIndexRoute = createRoute({
284+
getParentRoute: () => agentsRoute,
285+
path: '/',
286+
component: () => <div data-testid="agents-index">Agents Index</div>,
287+
})
288+
289+
const skillAgentRoute = createRoute({
290+
getParentRoute: () => agentsRoute,
291+
path: 'skill-agent',
292+
component: () => <div data-testid="skill-agent">Skill Agent</div>,
293+
})
294+
295+
const router = createRouter({
296+
routeTree: rootRoute.addChildren([
297+
authenticatedRoute.addChildren([
298+
agentsRoute.addChildren([agentsIndexRoute, skillAgentRoute]),
299+
]),
300+
]),
301+
history,
302+
defaultNotFoundComponent: () => (
303+
<div data-testid="default-not-found">Default Not Found</div>
304+
),
305+
})
306+
307+
window.history.replaceState(null, '', '/agents/non-existent')
308+
309+
render(<RouterProvider router={router} />)
310+
await router.load()
311+
312+
const notFound = await screen.findByTestId(
313+
'authenticated-not-found',
314+
{},
315+
{ timeout: 1000 },
316+
)
317+
expect(notFound).toBeInTheDocument()
318+
expect(screen.queryByTestId('default-not-found')).not.toBeInTheDocument()
319+
})

0 commit comments

Comments
 (0)