Skip to content

Commit 52e8c8b

Browse files
authored
Merge branch 'main' into support-lazy-error-component-on-before-load
2 parents 75790b1 + 8c08d87 commit 52e8c8b

File tree

199 files changed

+2310
-873
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

199 files changed

+2310
-873
lines changed

docs/router/framework/react/api/router.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ title: Router API
3232
- [`<Link>`](../router/linkComponent.md)
3333
- [`<MatchRoute>`](../router/matchRouteComponent.md)
3434
- [`<Navigate>`](../router/navigateComponent.md)
35+
- [`<NotFoundComponent>`](../router/notFoundComponentComponent.md)
3536
- [`<Outlet>`](../router/outletComponent.md)
3637
- Hooks
3738
- [`useAwaited`](../router/useAwaitedHook.md)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
id: notFoundComponentComponent
3+
title: NotFoundComponent component
4+
---
5+
6+
The `NotFoundComponent` component is a component that renders when a not-found error occurs in a route.
7+
8+
## NotFoundComponent props
9+
10+
The `NotFoundComponent` component accepts the following props:
11+
12+
### `props.data` prop
13+
14+
- Type: `unknown`
15+
- Optional
16+
- Custom data that is passed to the `notFoundComponent` when the not-found error is handled
17+
- This data comes from the `data` property of the `NotFoundError` object
18+
19+
### `props.isNotFound` prop
20+
21+
- Type: `boolean`
22+
- Required
23+
- A boolean value indicating whether the current state is a not-found error state
24+
- This value is always `true`
25+
26+
### `props.routeId` prop
27+
28+
- Type: `RouteIds<RegisteredRouter['routeTree']>`
29+
- Required
30+
- The ID of the route that is attempting to handle the not-found error
31+
- Must be one of the valid route IDs from the router's route tree
32+
33+
## NotFoundComponent returns
34+
35+
- Returns appropriate UI for not-found error situations
36+
- Typically includes a "page not found" message along with links to go home or navigate to previous pages

e2e/react-start/basic/src/routeTree.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/
3434
import { Route as RedirectTargetRouteImport } from './routes/redirect/$target'
3535
import { Route as PostsPostIdRouteImport } from './routes/posts.$postId'
3636
import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader'
37+
import { Route as NotFoundViaHeadRouteImport } from './routes/not-found/via-head'
3738
import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad'
3839
import { Route as ApiUsersRouteImport } from './routes/api.users'
3940
import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2'
@@ -169,6 +170,11 @@ const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({
169170
path: '/via-loader',
170171
getParentRoute: () => NotFoundRouteRoute,
171172
} as any)
173+
const NotFoundViaHeadRoute = NotFoundViaHeadRouteImport.update({
174+
id: '/via-head',
175+
path: '/via-head',
176+
getParentRoute: () => NotFoundRouteRoute,
177+
} as any)
172178
const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({
173179
id: '/via-beforeLoad',
174180
path: '/via-beforeLoad',
@@ -272,6 +278,7 @@ export interface FileRoutesByFullPath {
272278
'/대한민국': typeof Char45824Char54620Char48124Char44397Route
273279
'/api/users': typeof ApiUsersRouteWithChildren
274280
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
281+
'/not-found/via-head': typeof NotFoundViaHeadRoute
275282
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
276283
'/posts/$postId': typeof PostsPostIdRoute
277284
'/redirect/$target': typeof RedirectTargetRouteWithChildren
@@ -307,6 +314,7 @@ export interface FileRoutesByTo {
307314
'/대한민국': typeof Char45824Char54620Char48124Char44397Route
308315
'/api/users': typeof ApiUsersRouteWithChildren
309316
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
317+
'/not-found/via-head': typeof NotFoundViaHeadRoute
310318
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
311319
'/posts/$postId': typeof PostsPostIdRoute
312320
'/search-params/default': typeof SearchParamsDefaultRoute
@@ -347,6 +355,7 @@ export interface FileRoutesById {
347355
'/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren
348356
'/api/users': typeof ApiUsersRouteWithChildren
349357
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
358+
'/not-found/via-head': typeof NotFoundViaHeadRoute
350359
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
351360
'/posts/$postId': typeof PostsPostIdRoute
352361
'/redirect/$target': typeof RedirectTargetRouteWithChildren
@@ -389,6 +398,7 @@ export interface FileRouteTypes {
389398
| '/대한민국'
390399
| '/api/users'
391400
| '/not-found/via-beforeLoad'
401+
| '/not-found/via-head'
392402
| '/not-found/via-loader'
393403
| '/posts/$postId'
394404
| '/redirect/$target'
@@ -424,6 +434,7 @@ export interface FileRouteTypes {
424434
| '/대한민국'
425435
| '/api/users'
426436
| '/not-found/via-beforeLoad'
437+
| '/not-found/via-head'
427438
| '/not-found/via-loader'
428439
| '/posts/$postId'
429440
| '/search-params/default'
@@ -463,6 +474,7 @@ export interface FileRouteTypes {
463474
| '/_layout/_layout-2'
464475
| '/api/users'
465476
| '/not-found/via-beforeLoad'
477+
| '/not-found/via-head'
466478
| '/not-found/via-loader'
467479
| '/posts/$postId'
468480
| '/redirect/$target'
@@ -673,6 +685,13 @@ declare module '@tanstack/react-router' {
673685
preLoaderRoute: typeof NotFoundViaLoaderRouteImport
674686
parentRoute: typeof NotFoundRouteRoute
675687
}
688+
'/not-found/via-head': {
689+
id: '/not-found/via-head'
690+
path: '/via-head'
691+
fullPath: '/not-found/via-head'
692+
preLoaderRoute: typeof NotFoundViaHeadRouteImport
693+
parentRoute: typeof NotFoundRouteRoute
694+
}
676695
'/not-found/via-beforeLoad': {
677696
id: '/not-found/via-beforeLoad'
678697
path: '/via-beforeLoad'
@@ -797,12 +816,14 @@ declare module '@tanstack/react-router' {
797816

798817
interface NotFoundRouteRouteChildren {
799818
NotFoundViaBeforeLoadRoute: typeof NotFoundViaBeforeLoadRoute
819+
NotFoundViaHeadRoute: typeof NotFoundViaHeadRoute
800820
NotFoundViaLoaderRoute: typeof NotFoundViaLoaderRoute
801821
NotFoundIndexRoute: typeof NotFoundIndexRoute
802822
}
803823

804824
const NotFoundRouteRouteChildren: NotFoundRouteRouteChildren = {
805825
NotFoundViaBeforeLoadRoute: NotFoundViaBeforeLoadRoute,
826+
NotFoundViaHeadRoute: NotFoundViaHeadRoute,
806827
NotFoundViaLoaderRoute: NotFoundViaLoaderRoute,
807828
NotFoundIndexRoute: NotFoundIndexRoute,
808829
}

e2e/react-start/basic/src/routes/not-found/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ export const Route = createFileRoute('/not-found/')({
2525
via-loader
2626
</Link>
2727
</div>
28+
<div className="mb-2">
29+
<Link
30+
from={Route.fullPath}
31+
to="./via-head"
32+
preload={preload}
33+
data-testid="via-head"
34+
>
35+
via-head
36+
</Link>
37+
</div>
2838
</div>
2939
)
3040
},
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { createFileRoute, notFound } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/not-found/via-head')({
4+
head: () => {
5+
throw notFound()
6+
},
7+
component: RouteComponent,
8+
notFoundComponent: () => {
9+
return (
10+
<div data-testid="via-head-notFound-component">
11+
Not Found "/not-found/via-head"!
12+
</div>
13+
)
14+
},
15+
})
16+
17+
function RouteComponent() {
18+
return (
19+
<div data-testid="via-head-route-component" data-server={typeof window}>
20+
Hello "/not-found/via-head"!
21+
</div>
22+
)
23+
}

e2e/react-start/basic/src/routes/users.$userId.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { ErrorComponent, createFileRoute } from '@tanstack/react-router'
22
import axios from 'redaxios'
3+
import { getRouterInstance } from '@tanstack/react-start'
34
import type { ErrorComponentProps } from '@tanstack/react-router'
45

56
import type { User } from '~/utils/users'
67
import { NotFound } from '~/components/NotFound'
78

89
export const Route = createFileRoute('/users/$userId')({
910
loader: async ({ params: { userId } }) => {
11+
const router = await getRouterInstance()
1012
return await axios
11-
.get<User>('/api/users/' + userId)
13+
.get<User>('/api/users/' + userId, { baseURL: router.options.origin })
1214
.then((r) => r.data)
1315
.catch(() => {
1416
throw new Error('Failed to fetch user')

e2e/react-start/basic/src/routes/users.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
2+
import { getRouterInstance } from '@tanstack/react-start'
23
import axios from 'redaxios'
34

45
import type { User } from '~/utils/users'
56

67
export const Route = createFileRoute('/users')({
78
loader: async () => {
9+
const router = await getRouterInstance()
810
return await axios
9-
.get<Array<User>>('/api/users')
11+
.get<Array<User>>('/api/users', { baseURL: router.options.origin })
1012
.then((r) => r.data)
1113
.catch(() => {
1214
throw new Error('Failed to fetch users')

e2e/react-start/basic/tests/not-found.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const combinate = (combinateImport as any).default as typeof combinateImport
99
test.use({
1010
whitelistErrors: [
1111
/Failed to load resource: the server responded with a status of 404/,
12+
'NotFound error during hydration for routeId',
1213
],
1314
})
1415
test.describe('not-found', () => {
@@ -25,7 +26,7 @@ test.describe('not-found', () => {
2526
test.describe('throw notFound()', () => {
2627
const navigationTestMatrix = combinate({
2728
// TODO beforeLoad!
28-
thrower: [/* 'beforeLoad',*/ 'loader'] as const,
29+
thrower: [/* 'beforeLoad',*/ 'head', 'loader'] as const,
2930
preload: [false, true] as const,
3031
})
3132

@@ -57,7 +58,7 @@ test.describe('not-found', () => {
5758
const directVisitTestMatrix = combinate({
5859
// TODO beforeLoad!
5960

60-
thrower: [/* 'beforeLoad',*/ 'loader'] as const,
61+
thrower: [/* 'beforeLoad',*/ 'head', 'loader'] as const,
6162
})
6263

6364
directVisitTestMatrix.forEach(({ thrower }) => {

e2e/react-start/basic/tests/prerendering.spec.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ test.describe('Prerender Static Path Discovery', () => {
1414
// These static routes should be automatically discovered and prerendered
1515
expect(existsSync(join(distDir, 'index.html'))).toBe(true)
1616
expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true)
17-
expect(existsSync(join(distDir, 'users/index.html'))).toBe(true)
1817
expect(existsSync(join(distDir, 'deferred/index.html'))).toBe(true)
1918
expect(existsSync(join(distDir, 'scripts/index.html'))).toBe(true)
2019
expect(existsSync(join(distDir, 'inline-scripts/index.html'))).toBe(true)
@@ -40,14 +39,5 @@ test.describe('Prerender Static Path Discovery', () => {
4039
const html = readFileSync(join(distDir, 'posts/index.html'), 'utf-8')
4140
expect(html).toContain('Select a post.')
4241
})
43-
44-
test('should contain prerendered content in users.html', () => {
45-
const distDir = join(process.cwd(), 'dist', 'client')
46-
expect(existsSync(join(distDir, 'users/index.html'))).toBe(true)
47-
48-
// "Select a user." should be in the prerendered HTML
49-
const html = readFileSync(join(distDir, 'users/index.html'), 'utf-8')
50-
expect(html).toContain('Select a user.')
51-
})
5242
})
5343
})

e2e/react-start/basic/vite.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ const prerenderConfiguration = {
2020
'/redirect',
2121
'/i-do-not-exist',
2222
'/not-found/via-beforeLoad',
23+
'/not-found/via-head',
2324
'/not-found/via-loader',
25+
'/users',
2426
].some((p) => page.path.includes(p)),
2527
maxRedirects: 100,
2628
}
@@ -33,7 +35,6 @@ export default defineConfig({
3335
tsConfigPaths({
3436
projects: ['./tsconfig.json'],
3537
}),
36-
// @ts-ignore we want to keep one test with verboseFileRoutes off even though the option is hidden
3738
tanstackStart({
3839
spa: isSpaMode ? spaModeConfiguration : undefined,
3940
prerender: isPrerender ? prerenderConfiguration : undefined,

0 commit comments

Comments
 (0)