@tanstack/vue-router 1.167.3 → 1.167.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/intent.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ // Auto-generated by @tanstack/intent setup
3
+ // Exposes the intent end-user CLI for consumers of this library.
4
+ // Commit this file, then add to your package.json:
5
+ // "bin": { "intent": "./bin/intent.js" }
6
+ try {
7
+ await import('@tanstack/intent/intent-library')
8
+ } catch (e) {
9
+ const isModuleNotFound =
10
+ e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND'
11
+ const missingIntentLibrary =
12
+ typeof e?.message === 'string' && e.message.includes('@tanstack/intent')
13
+
14
+ if (isModuleNotFound && missingIntentLibrary) {
15
+ console.error('@tanstack/intent is not installed.')
16
+ console.error('')
17
+ console.error('Install it as a dev dependency:')
18
+ console.error(' npm add -D @tanstack/intent')
19
+ console.error('')
20
+ console.error('Or run directly:')
21
+ console.error(' npx @tanstack/intent@latest list')
22
+ process.exit(1)
23
+ }
24
+ throw e
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/vue-router",
3
- "version": "1.167.3",
3
+ "version": "1.167.4",
4
4
  "description": "Modern and scalable routing for Vue applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -51,7 +51,10 @@
51
51
  "sideEffects": false,
52
52
  "files": [
53
53
  "dist",
54
- "src"
54
+ "src",
55
+ "skills",
56
+ "bin",
57
+ "!skills/_artifacts"
55
58
  ],
56
59
  "engines": {
57
60
  "node": ">=20.19"
@@ -64,9 +67,10 @@
64
67
  "tiny-invariant": "^1.3.3",
65
68
  "tiny-warning": "^1.0.3",
66
69
  "@tanstack/history": "1.161.6",
67
- "@tanstack/router-core": "1.167.3"
70
+ "@tanstack/router-core": "1.167.4"
68
71
  },
69
72
  "devDependencies": {
73
+ "@tanstack/intent": "^0.0.14",
70
74
  "@testing-library/jest-dom": "^6.6.3",
71
75
  "@testing-library/vue": "^8.1.0",
72
76
  "@types/jsesc": "^3.0.3",
@@ -80,6 +84,9 @@
80
84
  "peerDependencies": {
81
85
  "vue": "^3.3.0"
82
86
  },
87
+ "bin": {
88
+ "intent": "./bin/intent.js"
89
+ },
83
90
  "scripts": {
84
91
  "clean": "rimraf ./dist && rimraf ./coverage",
85
92
  "test": "pnpm run test:unit",
@@ -0,0 +1,389 @@
1
+ ---
2
+ name: vue-router
3
+ description: >-
4
+ Vue bindings for TanStack Router: RouterProvider, useRouter,
5
+ useRouterState, useMatch, useMatches, useLocation, useSearch,
6
+ useParams, useNavigate, useLoaderData, useLoaderDeps,
7
+ useRouteContext, useBlocker, useCanGoBack, Link, Navigate,
8
+ Outlet, CatchBoundary, ErrorComponent, Html, Body.
9
+ Vue-specific patterns with Ref<T> returns, defineComponent,
10
+ h() render functions, provide/inject, and computed refs.
11
+ type: framework
12
+ library: tanstack-router
13
+ library_version: '1.166.2'
14
+ framework: vue
15
+ requires:
16
+ - router-core
17
+ sources:
18
+ - TanStack/router:packages/vue-router/src
19
+ ---
20
+
21
+ # Vue Router (`@tanstack/vue-router`)
22
+
23
+ This skill builds on router-core. Read [router-core](../../../router-core/skills/router-core/SKILL.md) first for foundational concepts.
24
+
25
+ This skill covers the Vue-specific bindings, components, composables, and setup for TanStack Router.
26
+
27
+ > **CRITICAL**: TanStack Router types are FULLY INFERRED. Never cast, never annotate inferred values.
28
+
29
+ > **CRITICAL**: TanStack Router is CLIENT-FIRST. Loaders run on the client by default, not on the server.
30
+
31
+ > **CRITICAL**: Most composables return `Ref<T>` — access via `.value` in script, auto-unwrapped in templates. This is the #1 difference from the React version.
32
+
33
+ > **CRITICAL**: Do not confuse `@tanstack/vue-router` with `vue-router` (the official Vue router). They are completely different libraries with different APIs.
34
+
35
+ ## Full Setup: File-Based Routing with Vite
36
+
37
+ ### 1. Install Dependencies
38
+
39
+ ```bash
40
+ npm install @tanstack/vue-router
41
+ npm install -D @tanstack/router-plugin @vitejs/plugin-vue-jsx
42
+ ```
43
+
44
+ ### 2. Configure Vite Plugin
45
+
46
+ ```ts
47
+ // vite.config.ts
48
+ import { defineConfig } from 'vite'
49
+ import vue from '@vitejs/plugin-vue'
50
+ import vueJsx from '@vitejs/plugin-vue-jsx'
51
+ import { tanstackRouter } from '@tanstack/router-plugin/vite'
52
+
53
+ export default defineConfig({
54
+ plugins: [
55
+ // MUST come before vue()
56
+ tanstackRouter({
57
+ target: 'vue',
58
+ autoCodeSplitting: true,
59
+ }),
60
+ vue(),
61
+ vueJsx(), // Required for JSX/TSX route files
62
+ ],
63
+ })
64
+ ```
65
+
66
+ ### 3. Create Root Route
67
+
68
+ ```tsx
69
+ // src/routes/__root.tsx
70
+ import { createRootRoute, Link, Outlet } from '@tanstack/vue-router'
71
+
72
+ export const Route = createRootRoute({
73
+ component: RootLayout,
74
+ })
75
+
76
+ function RootLayout() {
77
+ return (
78
+ <>
79
+ <nav>
80
+ <Link to="/" activeProps={{ class: 'font-bold' }}>
81
+ Home
82
+ </Link>
83
+ <Link to="/about" activeProps={{ class: 'font-bold' }}>
84
+ About
85
+ </Link>
86
+ </nav>
87
+ <hr />
88
+ <Outlet />
89
+ </>
90
+ )
91
+ }
92
+ ```
93
+
94
+ ### 4. Create Route Files
95
+
96
+ ```tsx
97
+ // src/routes/index.tsx
98
+ import { createFileRoute } from '@tanstack/vue-router'
99
+
100
+ export const Route = createFileRoute('/')({
101
+ component: HomePage,
102
+ })
103
+
104
+ function HomePage() {
105
+ return <h1>Welcome Home</h1>
106
+ }
107
+ ```
108
+
109
+ ### 5. Create Router Instance and Register Types
110
+
111
+ ```tsx
112
+ // src/main.tsx
113
+ import { createApp } from 'vue'
114
+ import { RouterProvider, createRouter } from '@tanstack/vue-router'
115
+ import { routeTree } from './routeTree.gen'
116
+
117
+ const router = createRouter({ routeTree })
118
+
119
+ // REQUIRED — without this, Link/useNavigate/useSearch have no type safety
120
+ declare module '@tanstack/vue-router' {
121
+ interface Register {
122
+ router: typeof router
123
+ }
124
+ }
125
+
126
+ const app = createApp(RouterProvider, { router })
127
+ app.mount('#root')
128
+ ```
129
+
130
+ ## Composables Reference
131
+
132
+ All composables imported from `@tanstack/vue-router`. Most return `Ref<T>` — access via `.value` in script or auto-unwrap in templates.
133
+
134
+ ### `useRouter()` — returns `TRouter` (NOT a Ref)
135
+
136
+ ```tsx
137
+ import { useRouter } from '@tanstack/vue-router'
138
+
139
+ const router = useRouter()
140
+ router.invalidate()
141
+ ```
142
+
143
+ ### `useRouterState()` — returns `Ref<T>`
144
+
145
+ ```tsx
146
+ import { useRouterState } from '@tanstack/vue-router'
147
+
148
+ const isLoading = useRouterState({ select: (s) => s.isLoading })
149
+ // Access: isLoading.value
150
+ ```
151
+
152
+ ### `useNavigate()` — returns a function (NOT a Ref)
153
+
154
+ ```tsx
155
+ import { useNavigate } from '@tanstack/vue-router'
156
+
157
+ const navigate = useNavigate()
158
+
159
+ async function handleSubmit() {
160
+ await saveData()
161
+ navigate({ to: '/posts/$postId', params: { postId: '123' } })
162
+ }
163
+ ```
164
+
165
+ ### `useSearch({ from })` — returns `Ref<T>`
166
+
167
+ ```tsx
168
+ import { useSearch } from '@tanstack/vue-router'
169
+
170
+ const search = useSearch({ from: '/products' })
171
+ // Access: search.value.page
172
+ ```
173
+
174
+ ### `useParams({ from })` — returns `Ref<T>`
175
+
176
+ ```tsx
177
+ import { useParams } from '@tanstack/vue-router'
178
+
179
+ const params = useParams({ from: '/posts/$postId' })
180
+ // Access: params.value.postId
181
+ ```
182
+
183
+ ### `useLoaderData({ from })` — returns `Ref<T>`
184
+
185
+ ```tsx
186
+ import { useLoaderData } from '@tanstack/vue-router'
187
+
188
+ const data = useLoaderData({ from: '/posts/$postId' })
189
+ // Access: data.value.post.content
190
+ ```
191
+
192
+ ### `useMatch({ from })` — returns `Ref<T>`
193
+
194
+ ```tsx
195
+ import { useMatch } from '@tanstack/vue-router'
196
+
197
+ const match = useMatch({ from: '/posts/$postId' })
198
+ // Access: match.value.loaderData.post.title
199
+ ```
200
+
201
+ ### Other Composables
202
+
203
+ - **`useMatches()`** — `Ref<Array<Match>>`, all active route matches
204
+ - **`useRouteContext({ from })`** — `Ref<T>`, context from `beforeLoad`
205
+ - **`useBlocker({ shouldBlockFn })`** — blocks navigation for unsaved changes
206
+ - **`useCanGoBack()`** — `Ref<boolean>`
207
+ - **`useLocation()`** — `Ref<ParsedLocation>`
208
+ - **`useLoaderDeps({ from })`** — `Ref<T>`, loader dependency values
209
+ - **`useLinkProps()`** — returns `LinkHTMLAttributes`
210
+ - **`useMatchRoute()`** — returns a function; calling it returns `Ref<false | Params>`
211
+
212
+ ## Components Reference
213
+
214
+ ### `RouterProvider`
215
+
216
+ ```tsx
217
+ import { RouterProvider } from '@tanstack/vue-router'
218
+ // In createApp or template
219
+ <RouterProvider :router="router" />
220
+ ```
221
+
222
+ ### `Link`
223
+
224
+ Type-safe navigation link with scoped slot for active state:
225
+
226
+ ```vue
227
+ <Link to="/posts/$postId" :params="{ postId: '42' }">
228
+ View Post
229
+ </Link>
230
+
231
+ <!-- Scoped slot for active state -->
232
+ <Link to="/about">
233
+ <template #default="{ isActive }">
234
+ <span :class="{ active: isActive }">About</span>
235
+ </template>
236
+ </Link>
237
+ ```
238
+
239
+ ### `Outlet`
240
+
241
+ Renders the matched child route component.
242
+
243
+ ### `Navigate`
244
+
245
+ Declarative redirect (triggers navigation in `onMounted`).
246
+
247
+ ### `Await`
248
+
249
+ Async setup component for deferred data — use with Vue's `<Suspense>`.
250
+
251
+ ### `CatchBoundary`
252
+
253
+ Error boundary using Vue's `onErrorCaptured`.
254
+
255
+ ### `Html` and `Body`
256
+
257
+ Vue-specific SSR shell components:
258
+
259
+ ```tsx
260
+ function RootComponent() {
261
+ return (
262
+ <Html>
263
+ <head>
264
+ <HeadContent />
265
+ </head>
266
+ <Body>
267
+ <Outlet />
268
+ <Scripts />
269
+ </Body>
270
+ </Html>
271
+ )
272
+ }
273
+ ```
274
+
275
+ ### `ClientOnly`
276
+
277
+ Renders children only after `onMounted` (hydration complete):
278
+
279
+ ```tsx
280
+ <ClientOnly fallback={<div>Loading...</div>}>
281
+ <BrowserOnlyWidget />
282
+ </ClientOnly>
283
+ ```
284
+
285
+ ## Vue-Specific Patterns
286
+
287
+ ### Custom Link Component with `createLink`
288
+
289
+ ```tsx
290
+ import { createLink } from '@tanstack/vue-router'
291
+ import { defineComponent, h } from 'vue'
292
+
293
+ const StyledLinkComponent = defineComponent({
294
+ setup(props, { slots, attrs }) {
295
+ return () => h('a', { ...attrs, class: 'styled-link' }, slots.default?.())
296
+ },
297
+ })
298
+
299
+ const StyledLink = createLink(StyledLinkComponent)
300
+ ```
301
+
302
+ ### Render Functions (h())
303
+
304
+ All components in `@tanstack/vue-router` use `h()` render functions internally. Route components can use either SFC templates or render functions:
305
+
306
+ SFC template (most common for user code) in `MyRoute.component.vue`:
307
+
308
+ ```vue
309
+ <template>
310
+ <div>{{ data.title }}</div>
311
+ </template>
312
+
313
+ <script setup>
314
+ import { useLoaderData } from '@tanstack/vue-router'
315
+ const data = useLoaderData({ from: '/posts/$postId' })
316
+ </script>
317
+ ```
318
+
319
+ ### Auth with Router Context
320
+
321
+ ```tsx
322
+ import { createRootRouteWithContext } from '@tanstack/vue-router'
323
+
324
+ const rootRoute = createRootRouteWithContext<{ auth: AuthState }>()({
325
+ component: RootComponent,
326
+ })
327
+
328
+ const router = createRouter({
329
+ routeTree,
330
+ context: { auth: authState },
331
+ })
332
+
333
+ // In a route — access via beforeLoad
334
+ beforeLoad: ({ context }) => {
335
+ if (!context.auth.isAuthenticated) {
336
+ throw redirect({ to: '/login' })
337
+ }
338
+ }
339
+ ```
340
+
341
+ ### Vue File Conventions for Code Splitting
342
+
343
+ With `autoCodeSplitting`, Vue routes can optionally use split-file conventions. These are NOT required — single-file `.tsx` routes work fine. Split files are useful for separating route config from components:
344
+
345
+ - `myRoute.ts` — route configuration (search params, loader, beforeLoad)
346
+ - `myRoute.component.vue` — route component (lazy-loaded)
347
+ - `myRoute.errorComponent.vue` — error component (lazy-loaded)
348
+ - `myRoute.notFoundComponent.vue` — not-found component (lazy-loaded)
349
+ - `myRoute.lazy.ts` — lazy-loaded route options
350
+
351
+ ## Common Mistakes
352
+
353
+ ### 1. CRITICAL: Forgetting .value in script blocks
354
+
355
+ Composables return `Ref<T>` — access via `.value` in `<script>`. Templates auto-unwrap.
356
+
357
+ ```tsx
358
+ // WRONG — accessing Ref without .value in script
359
+ const params = useParams({ from: '/posts/$postId' })
360
+ console.log(params.postId) // undefined!
361
+
362
+ // CORRECT — use .value
363
+ const params = useParams({ from: '/posts/$postId' })
364
+ console.log(params.value.postId)
365
+ ```
366
+
367
+ ### 2. HIGH: Confusing with vue-router (official)
368
+
369
+ `@tanstack/vue-router` is NOT `vue-router`. Do not use `<router-view>`, `<router-link>`, `useRoute()`, `useRouter()` from `vue-router`.
370
+
371
+ ```ts
372
+ // WRONG — official vue-router imports
373
+ import { useRoute, useRouter } from 'vue-router'
374
+
375
+ // CORRECT — TanStack Vue Router imports
376
+ import { useMatch, useRouter } from '@tanstack/vue-router'
377
+ ```
378
+
379
+ ### 3. HIGH: Using Vue hooks in beforeLoad or loader
380
+
381
+ `beforeLoad` and `loader` are NOT component setup functions — they are plain async functions. Vue composables cannot be used in them. Pass state via router context instead.
382
+
383
+ ### 4. MEDIUM: Wrong plugin target
384
+
385
+ Must set `target: 'vue'` in the router plugin config. Default is `'react'`.
386
+
387
+ ## Cross-References
388
+
389
+ - [router-core/SKILL.md](../../../router-core/skills/router-core/SKILL.md) — all sub-skills for domain-specific patterns (search params, data loading, navigation, auth, SSR, etc.)