first commit

This commit is contained in:
2026-03-02 21:16:26 +01:00
commit 841f43285e
179 changed files with 33193 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
# ctx-root-context: Define Context at Root Route
## Priority: LOW
## Explanation
Use `createRootRouteWithContext` to define typed context that flows through your entire route tree. This enables dependency injection for things like query clients, auth state, and services.
## Bad Example
```tsx
// No context - importing globals directly
// routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router'
import { queryClient } from '@/lib/query-client' // Global import
export const Route = createRootRoute({
component: RootComponent,
})
// routes/posts.tsx
import { queryClient } from '@/lib/query-client' // Import again
export const Route = createFileRoute('/posts')({
loader: async () => {
// Using global - harder to test, couples to implementation
return queryClient.ensureQueryData(postQueries.list())
},
})
```
## Good Example
```tsx
// routes/__root.tsx
import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'
import { QueryClient } from '@tanstack/react-query'
// Define the context interface
interface RouterContext {
queryClient: QueryClient
auth: {
user: User | null
isAuthenticated: boolean
}
}
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootComponent,
})
function RootComponent() {
return (
<>
<Header />
<main>
<Outlet />
</main>
<Footer />
</>
)
}
// router.tsx - Provide context when creating router
import { createRouter } from '@tanstack/react-router'
import { QueryClient } from '@tanstack/react-query'
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
import { routeTree } from './routeTree.gen'
export function getRouter(auth: RouterContext['auth'] = { user: null, isAuthenticated: false }) {
const queryClient = new QueryClient()
const router = createRouter({
routeTree,
context: {
queryClient,
auth,
},
defaultPreload: 'intent',
defaultPreloadStaleTime: 0,
scrollRestoration: true,
})
setupRouterSsrQueryIntegration({ router, queryClient })
return router
}
// routes/posts.tsx - Use context in loaders
export const Route = createFileRoute('/posts')({
loader: async ({ context: { queryClient } }) => {
// Context is typed and injected
return queryClient.ensureQueryData(postQueries.list())
},
})
```
## Good Example: Auth-Protected Routes
```tsx
// routes/__root.tsx
interface RouterContext {
queryClient: QueryClient
auth: AuthState
}
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootComponent,
})
// routes/_authenticated.tsx - Layout route for protected pages
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ context, location }) => {
if (!context.auth.isAuthenticated) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
},
component: AuthenticatedLayout,
})
// routes/_authenticated/dashboard.tsx
export const Route = createFileRoute('/_authenticated/dashboard')({
loader: async ({ context: { queryClient, auth } }) => {
// We know user is authenticated from parent beforeLoad
return queryClient.ensureQueryData(
dashboardQueries.forUser(auth.user!.id)
)
},
})
```
## Extending Context with beforeLoad
```tsx
// routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
beforeLoad: async ({ context, params }) => {
// Extend context with route-specific data
const post = await fetchPost(params.postId)
return {
post, // Available to this route and children
}
},
loader: async ({ context }) => {
// context now includes 'post' from beforeLoad
const comments = await fetchComments(context.post.id)
return { comments }
},
})
```
## Context vs. Loader Data
| Context | Loader Data |
|---------|-------------|
| Available in beforeLoad, loader, and component | Only available in component |
| Set at router creation or in beforeLoad | Returned from loader |
| Good for services, clients, auth | Good for route-specific data |
| Flows down to all children | Specific to route |
## Context
- Type the context interface in `createRootRouteWithContext<T>()`
- Provide context when calling `createRouter({ context: {...} })`
- Context flows from root to all nested routes
- Use `beforeLoad` to extend context for specific subtrees
- Enables testability - inject mocks in tests
- Avoids global imports and singletons