All skills
skilld-dev avatar

/tanstack-vue-router-skilld

@b9c39cb
by skilldskilld-dev/vue-ecosystem-skills180 stars
8

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.

Use this Skill: https://skilld.dev/gh/skilld-dev/vue-ecosystem-skills/tanstack-vue-router-skilld

This session only. Nothing lands on disk.

SKILL.md

โ‰ˆ69 tokens always: the name and description. โ‰ˆ2.8k when used: this file. โ‰ˆ7.2k more on demand in 8 files.

@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

Composable Returns Notes
useRouter() router instance not a ref
useRouterState({ select }) Ref<T> pass select to avoid re-renders
useNavigate({ from? }) function not 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() function calling it returns Ref<false | params> (src/Matches.tsx:148-172)
useLinkProps(options) link props object for 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] tuple for 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

Component Purpose
<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)

Deprecated Replacement Proof
Route, RootRoute, FileRoute, RouteApi classes createRoute, createRootRoute, createFileRoute, getRouteApi src/route.ts:95, src/route.ts:220, src/route.ts:460, src/fileRoute.ts:57
rootRouteWithContext createRootRouteWithContext src/route.ts:414
NotFoundRoute / routerOptions.notFoundRoute notFoundComponent route option / defaultNotFoundComponent router option https://tanstack.com/router/latest/docs/framework/vue/guide/not-found-errors
<ScrollRestoration /> scrollRestoration: true in createRouter src/ScrollRestoration.tsx:18
opts.navigate in beforeLoad/loader throw redirect({ to }) https://tanstack.com/router/latest/docs/framework/vue/api/router/RouteOptionsType
parseParams/stringifyParams params.parse/params.stringify same RouteOptionsType doc
preSearchFilters/postSearchFilters search.middlewares same RouteOptionsType doc
FileRouteLoader / separate .lazy.ts loader files keep the loader in the route file src/fileRoute.ts:153
useBlocker(fn, condition) legacy signatures { shouldBlockFn } object src/useBlocker.tsx:139-151

Best practices

References

Source: SKILL.md on GitHub

1 warning8d3 checks ยท Risk SAFE
  • Gen Agent Trust Hub8d

    The skill is a comprehensive collection of documentation, API references, and guides for the TanStack Router library. It provides technical information and best practices for implementing routing in web applications. No malicious code, prompt injections, or data exfiltration patterns were detected.

  • Socket8d

    No alerts

  • Snyk8d

    Risk: MEDIUM ยท 1 issue

Signed by skilld at b9c39cb. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last week.

Activeupdated last week

README badge

README badge for skilld-dev/vue-ecosystem-skills/tanstack-vue-router-skilld