|
1 | 1 | --- |
2 | 2 | title: TanStack Router Example |
3 | | -description: A simple example of using TanStack Router with OpenAPI React Query Codegen. |
| 3 | +description: Using TanStack Router with OpenAPI React Query Codegen for data loading and prefetching. |
4 | 4 | --- |
5 | 5 |
|
6 | | -Example of using Next.js can be found in the [`examples/tanstack-router-app`](https://github.com/7nohe/openapi-react-query-codegen/tree/main/examples/tanstack-router-app) directory of the repository. |
| 6 | +Example of using TanStack Router can be found in the [`examples/tanstack-router-app`](https://github.com/7nohe/openapi-react-query-codegen/tree/main/examples/tanstack-router-app) directory of the repository. |
| 7 | + |
| 8 | +## Generated Files |
| 9 | + |
| 10 | +The codegen generates a `router.ts` file that provides: |
| 11 | + |
| 12 | +- **Loader factories** (`loaderUse*`) for route data loading |
| 13 | +- **`withQueryPrefetch`** helper for hover/touch prefetching |
| 14 | + |
| 15 | +## Using Loader Factories |
| 16 | + |
| 17 | +Use `loaderUse*` functions in your route definitions to prefetch data before the route renders: |
| 18 | + |
| 19 | +```tsx |
| 20 | +// routes/pets.$petId.tsx |
| 21 | +import { createFileRoute } from "@tanstack/react-router"; |
| 22 | +import { loaderUseFindPetById } from "../openapi/queries/router"; |
| 23 | +import { queryClient } from "../queryClient"; |
| 24 | + |
| 25 | +export const Route = createFileRoute("/pets/$petId")({ |
| 26 | + loader: ({ params }) => |
| 27 | + loaderUseFindPetById({ queryClient })({ |
| 28 | + params: { petId: Number(params.petId) }, |
| 29 | + }), |
| 30 | + component: PetDetail, |
| 31 | +}); |
| 32 | + |
| 33 | +function PetDetail() { |
| 34 | + const { petId } = Route.useParams(); |
| 35 | + const { data } = useFindPetById({ path: { petId: Number(petId) } }); |
| 36 | + |
| 37 | + return <div>{data?.name}</div>; |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +### For SSR / TanStack Start |
| 42 | + |
| 43 | +When using SSR or TanStack Start, pass `queryClient` from the router context instead of importing it directly: |
| 44 | + |
| 45 | +```tsx |
| 46 | +export const Route = createFileRoute("/pets/$petId")({ |
| 47 | + loader: ({ context, params }) => |
| 48 | + loaderUseFindPetById({ queryClient: context.queryClient })({ |
| 49 | + params: { petId: Number(params.petId) }, |
| 50 | + }), |
| 51 | + component: PetDetail, |
| 52 | +}); |
| 53 | +``` |
| 54 | + |
| 55 | +### Operations Without Path Parameters |
| 56 | + |
| 57 | +For operations without path parameters, the loader is simpler: |
| 58 | + |
| 59 | +```tsx |
| 60 | +import { loaderUseFindPets } from "../openapi/queries/router"; |
| 61 | + |
| 62 | +export const Route = createFileRoute("/pets")({ |
| 63 | + loader: () => loaderUseFindPets({ queryClient })(), |
| 64 | + component: PetList, |
| 65 | +}); |
| 66 | +``` |
| 67 | + |
| 68 | +### Passing Additional Options |
| 69 | + |
| 70 | +You can pass additional client options through the `clientOptions` parameter: |
| 71 | + |
| 72 | +```tsx |
| 73 | +loader: ({ params }) => |
| 74 | + loaderUseFindPetById({ |
| 75 | + queryClient, |
| 76 | + clientOptions: { |
| 77 | + headers: { "X-Custom-Header": "value" }, |
| 78 | + }, |
| 79 | + })({ |
| 80 | + params: { petId: Number(params.petId) }, |
| 81 | + }), |
| 82 | +``` |
| 83 | + |
| 84 | +## Using withQueryPrefetch |
| 85 | + |
| 86 | +The `withQueryPrefetch` helper enables prefetching on hover or touch events. This is useful for custom prefetch triggers outside of TanStack Router's built-in `<Link>` preloading: |
| 87 | + |
| 88 | +```tsx |
| 89 | +import { withQueryPrefetch } from "../openapi/queries/router"; |
| 90 | +import { prefetchUseFindPetById } from "../openapi/queries/prefetch"; |
| 91 | +import { queryClient } from "../queryClient"; |
| 92 | + |
| 93 | +function PetLink({ petId }: { petId: number }) { |
| 94 | + return ( |
| 95 | + <a |
| 96 | + href={`/pets/${petId}`} |
| 97 | + {...withQueryPrefetch(() => |
| 98 | + prefetchUseFindPetById(queryClient, { path: { petId } }) |
| 99 | + )} |
| 100 | + > |
| 101 | + View Pet |
| 102 | + </a> |
| 103 | + ); |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +This spreads `onMouseEnter` and `onTouchStart` handlers that trigger the prefetch. |
| 108 | + |
| 109 | +## Router Configuration |
| 110 | + |
| 111 | +### External Cache Settings |
| 112 | + |
| 113 | +When using TanStack Query as an external cache, configure the router to delegate cache freshness to React Query: |
| 114 | + |
| 115 | +```tsx |
| 116 | +import { createRouter } from "@tanstack/react-router"; |
| 117 | +import { routeTree } from "./routeTree.gen"; |
| 118 | + |
| 119 | +const router = createRouter({ |
| 120 | + routeTree, |
| 121 | + defaultPreloadStaleTime: 0, // Let React Query handle cache freshness |
| 122 | +}); |
| 123 | +``` |
| 124 | + |
| 125 | +### Link Preloading |
| 126 | + |
| 127 | +TanStack Router's `<Link>` component supports intent-based preloading: |
| 128 | + |
| 129 | +```tsx |
| 130 | +<Link to="/pets/$petId" params={{ petId: "1" }} preload="intent"> |
| 131 | + View Pet |
| 132 | +</Link> |
| 133 | +``` |
| 134 | + |
| 135 | +Or set it globally: |
| 136 | + |
| 137 | +```tsx |
| 138 | +const router = createRouter({ |
| 139 | + routeTree, |
| 140 | + defaultPreload: "intent", |
| 141 | + defaultPreloadStaleTime: 0, |
| 142 | +}); |
| 143 | +``` |
| 144 | + |
| 145 | +When using `preload="intent"`, the router automatically calls the route's `loader` on hover/touch. If your loader uses `ensureUse*Data` (which the generated loaders do), prefetching happens automatically. |
| 146 | + |
| 147 | +## Important Notes |
| 148 | + |
| 149 | +### Router Params Are Strings |
| 150 | + |
| 151 | +TanStack Router params are always strings. You must parse them to the correct type before passing to the loader: |
| 152 | + |
| 153 | +```tsx |
| 154 | +// Router params: { petId: string } |
| 155 | +// API expects: { petId: number } |
| 156 | +loader: ({ params }) => |
| 157 | + loaderUseFindPetById({ queryClient })({ |
| 158 | + params: { petId: Number(params.petId) }, // Convert string to number |
| 159 | + }), |
| 160 | +``` |
| 161 | + |
| 162 | +For type-safe parsing, consider using TanStack Router's `parseParams`: |
| 163 | + |
| 164 | +```tsx |
| 165 | +export const Route = createFileRoute("/pets/$petId")({ |
| 166 | + parseParams: (params) => ({ |
| 167 | + petId: Number(params.petId), |
| 168 | + }), |
| 169 | + loader: ({ params }) => |
| 170 | + loaderUseFindPetById({ queryClient })({ |
| 171 | + params: { petId: params.petId }, // Already a number |
| 172 | + }), |
| 173 | +}); |
| 174 | +``` |
0 commit comments