tanstack-vue-router-skilld

v2026.09.25

Use when writing, debugging, or refactoring code that imports @tanstack/vue-router (TanStack Router for Vue). Covers setup, file-based and code-based routes, composables returning Ref, Link, loaders, search params, SSR, and v1.170.x deprecations.

GitHub
Install command
npx skhub add skilld-dev/tanstack-vue-router-skilld
Markdown
SKILL.md

@tanstack/vue-router

Type-safe router for Vue. Prepared source: version 1.170.35 (package.json:3).

  • Requires vue >= 3.3.0 (peer), Node >= 20.19 (package.json:59-88)
  • Runtime deps: @tanstack/router-core@1.171.32, @tanstack/history@1.162.4, @tanstack/vue-store@^0.11.0
  • ESM-only ("type": "module"), no main/CJS entry
  • Entry points: @tanstack/vue-router, @tanstack/vue-router/ssr/server, @tanstack/vue-router/ssr/client (package.json:29-50)

Docs: https://tanstack.com/router (framework pages verified at /router/latest/docs/framework/vue/...)

Hard rules

  1. Most composables return Ref<T> — use .value in script; templates auto-unwrap. useRouter(), useNavigate(), useLinkProps(), useAwaited() do NOT return refs. Full table in references/composables.md.
  2. Never cast or annotate inferred route/router types. Types come from the route tree and the Register declaration.
  3. Register the router for type safety — without this, Link/useNavigate/useSearch accept any string:
declare module '@tanstack/vue-router' {
  interface Register {
    router: typeof router
  }
}
  1. Not vue-router. Never import useRoute/useRouter from vue-router, never use <router-view>/<router-link>.
  2. beforeLoad/loader are plain async functions. Vue composables (ref, computed, lifecycle hooks) cannot run inside them. Pass state through router context.
  3. Route options accept Vue SFCs: component, errorComponent, notFoundComponent, pendingComponent and the default*Component router options take .vue files or h()-style function components (src/route.ts:54-58, src/router.ts:26-65).

Setup with Vite (file-based routing)

npm install @tanstack/vue-router
npm install -D @tanstack/router-plugin @vitejs/plugin-vue @vitejs/plugin-vue-jsx
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({ target: 'vue', autoCodeSplitting: true }), // before vue()
    vue(),
    vueJsx(), // needed for .tsx route files
  ],
})

Source: official Vue example, https://github.com/TanStack/router/blob/main/examples/vue/basic-file-based-sfc/vite.config.ts

// src/main.ts
import { createApp, h } from 'vue'
import { RouterProvider, createRouter } from '@tanstack/vue-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  scrollRestoration: true,
})

declare module '@tanstack/vue-router' {
  interface Register {
    router: typeof router
  }
}

createApp({ setup: () => () => h(RouterProvider, { router }) }).mount('#app')

File naming, split-file conventions (.route.ts, .component.vue, .lazy.ts), and the generated routeTree.gen.ts: references/file-based-routing.md.

Code-based routing

import { h } from 'vue'
import { createRootRouteWithContext, createRoute, createRouter, Outlet } from '@tanstack/vue-router'

interface RouterContext { auth: { isAuthenticated: boolean } }

const rootRoute = createRootRouteWithContext<RouterContext>()({ component: () => h(Outlet) })

const postsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'posts',
  loader: ({ context }) => fetchPosts(context.auth),
})

const routeTree = rootRoute.addChildren([postsRoute])
const router = createRouter({
  routeTree,
  context: { auth: { isAuthenticated: false } },
})

Route options, hooks on route objects, and property-order rules: references/routes.md.

Composables

ComposableReturnsNotes
useRouter()router instancenot a ref
useRouterState({ select })Ref<T>pass select to avoid re-renders
useNavigate({ from? })functionnot a ref
useSearch({ from })Ref<T>throws unless from matches
useParams({ from })Ref<T>
useMatch({ from })Ref<T>
useLoaderData({ from })Ref<T>
useLoaderDeps({ from })Ref<T>
useRouteContext({ from })Ref<T>
useLocation()Ref<ParsedLocation>
useMatches() / useParentMatches() / useChildMatches()Ref<Array<Match>>accepts select
useMatchRoute()functioncalling it returns Ref<false | params> (src/Matches.tsx:148-172)
useLinkProps(options)link props objectfor custom anchors
useBlocker({ shouldBlockFn })void or Ref<BlockerResolver>resolver only with withResolver: true
useCanGoBack()Ref<boolean>location.state.__TSR_index !== 0 (src/useCanGoBack.ts:4-10)
useAwaited({ promise })[data, promise] tuplefor deferred data

Route objects and getRouteApi('/path') expose the same hooks pre-scoped: Route.useSearch(), Route.useLoaderData(), Route.useNavigate(), Route.Link (src/route.ts:72-80).

Details and select/strict semantics: references/composables.md.

Components

ComponentPurpose
<RouterProvider :router="router" />mounts the router; extra attrs update router options (src/RouterProvider.tsx:60-95)
<Link to="..." :params="..." :search="...">type-safe anchor; active state sets data-status="active" + aria-current="page" (src/link.tsx:522-525); default slot receives { isActive } (src/link.tsx:918)
<Outlet />renders matched child route (src/Match.tsx:275)
<Navigate to="..." />declarative redirect, fires in onMounted (src/useNavigate.tsx:24-40)
<MatchRoute to="..." :fuzzy="true">renders slot when matched; scoped slot receives params
<Await :promise="p">deferred data with <Suspense> (src/awaited.tsx:26-43)
<Block :should-block-fn="fn">navigation blocking; scoped slot receives resolver (src/useBlocker.tsx:470-491)
<CatchBoundary> / <ErrorComponent>error boundary via onErrorCaptured
<ClientOnly>renders children only after mount
<HeadContent>, <Scripts>, <Html>, <Body>, <Asset>, <ScriptOnce>SSR document shell

Link options, activeProps (defaults to { class: 'active' }, src/link.tsx:477), preloading (intent/viewport/render), linkOptions(), and createLink(): references/navigation-links.md.

Data loading and redirects

const route = createFileRoute('/posts/$postId')({
  beforeLoad: async ({ context, params }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({ to: '/login', search: { from: params.postId } })
    }
  },
  loaderDeps: ({ search: { page } }) => ({ page }),
  loader: ({ deps, params }) => fetchPost(params.postId, deps.page),
  pendingComponent: PendingSpinner, // .vue file works too
})

throw redirect(), isRedirect(), deferred streaming with defer(), and context typing: references/data-loading.md.

Search params

import { z } from 'zod' // or plain validators
const route = createFileRoute('/posts')({
  validateSearch: z.object({ page: z.number().default(1) }),
  search: {
    middlewares: [stripSearchParams({ page: 1 })],
  },
})

Validators, input/output schemas, retainSearchParams/stripSearchParams, and custom serialization: references/search-params.md.

Deprecations in 1.170.x (use instead)

DeprecatedReplacementProof
Route, RootRoute, FileRoute, RouteApi classescreateRoute, createRootRoute, createFileRoute, getRouteApisrc/route.ts:95, src/route.ts:220, src/route.ts:460, src/fileRoute.ts:57
rootRouteWithContextcreateRootRouteWithContextsrc/route.ts:414
NotFoundRoute / routerOptions.notFoundRoutenotFoundComponent route option / defaultNotFoundComponent router optionhttps://tanstack.com/router/latest/docs/framework/vue/guide/not-found-errors
<ScrollRestoration />scrollRestoration: true in createRoutersrc/ScrollRestoration.tsx:18
opts.navigate in beforeLoad/loaderthrow redirect({ to })https://tanstack.com/router/latest/docs/framework/vue/api/router/RouteOptionsType
parseParams/stringifyParamsparams.parse/params.stringifysame RouteOptionsType doc
preSearchFilters/postSearchFilterssearch.middlewaressame RouteOptionsType doc
FileRouteLoader / separate .lazy.ts loader fileskeep the loader in the route filesrc/fileRoute.ts:153
useBlocker(fn, condition) legacy signatures{ shouldBlockFn } objectsrc/useBlocker.tsx:139-151

Best practices

References

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.25

Published

Sep 25, 2026

Category

Uncategorized

License

MIT

Source path

skills/tanstack-vue-router-skilld

Default branch

main

Latest commit

c8994af

Tree SHA

78224cb