
solid-router
Solid bindings for TanStack Router: RouterProvider, useRouter, useRouterState, useMatch, useMatches, useLocation, useSearch, useParams, useNavigate, useLoaderData, useLoaderDeps, useRouteContext, useBlocker, useCanGoBack, Link, Navigate, Outlet, CatchBoundary, ErrorComponent. Solid-specific patterns with Accessor returns, createSignal/createMemo/createEffect, Show/Switch/Match/Dynamic, and @solidjs/meta for head management.
- Installs
- 58
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/tanstack/router --skill solid-router- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads solid-router automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Solid Router (@tanstack/solid-router)
This skill builds on router-core. Read @tanstack/router-core#router-core first for foundational concepts.
CRITICAL: TanStack Router types are FULLY INFERRED. Never cast, never annotate inferred values. CRITICAL: TanStack Router is CLIENT-FIRST. Loaders run on the client by default, not on the server. CRITICAL: Most hooks return
Accessor<T>— you MUST call the accessor (value()) to read the reactive value. This is the #1 difference from the React version. CRITICAL: Do not confuse@tanstack/solid-routerwith@solidjs/router. They are completely different libraries with different APIs.
Full Setup: File-Based Routing with Vite
1. Install Dependencies
npm install @tanstack/solid-router
npm install -D @tanstack/router-plugin @tanstack/solid-router-devtools
2. Configure Vite Plugin
// vite.config.ts
import { defineConfig } from 'vite'
import solidPlugin from 'vite-plugin-solid'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
// MUST come before solid plugin
tanstackRouter({
target: 'solid',
autoCodeSplitting: true,
}),
solidPlugin(),
],
})
3. Create Root Route
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/solid-router'
export const Route = createRootRoute({
component: RootLayout,
})
function RootLayout() {
return (
<>
<nav>
<Link to="/" activeClass="font-bold">
Home
</Link>
<Link to="/about" activeClass="font-bold">
About
</Link>
</nav>
<hr />
<Outlet />
</>
)
}
4. Create Route Files
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/')({
component: HomePage,
})
function HomePage() {
return <h1>Welcome Home</h1>
}
5. Create Router Instance and Register Types
// src/main.tsx
import { render } from 'solid-js/web'
import { RouterProvider, createRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
const router = createRouter({ routeTree })
// REQUIRED — without this, Link/useNavigate/useSearch have no type safety
declare module '@tanstack/solid-router' {
interface Register {
router: typeof router
}
}
render(
() => <RouterProvider router={router} />,
document.getElementById('root')!,
)
Hooks Reference
All hooks imported from @tanstack/solid-router. Most return Accessor<T> — call the result to read the value.
useRouter() — returns TRouter (NOT an Accessor)
import { useRouter } from '@tanstack/solid-router'
function InvalidateButton() {
const router = useRouter()
return <button onClick={() => router.invalidate()}>Refresh data</button>
}
useRouterState() — returns Accessor<T>
Exposes the entire state and thus incurs a performance cost. For matches or location favor useMatches and useLocation.
import { useRouterState } from '@tanstack/solid-router'
function LoadingIndicator() {
const isLoading = useRouterState({ select: (s) => s.isLoading })
return (
<Show when={isLoading()}>
<div>Loading...</div>
</Show>
)
}
useNavigate() — returns a function (NOT an Accessor)
import { useNavigate } from '@tanstack/solid-router'
function AfterSubmit() {
const navigate = useNavigate()
const handleSubmit = async () => {
await saveData()
navigate({ to: '/posts/$postId', params: { postId: '123' } })
}
return <button onClick={handleSubmit}>Save</button>
}
useSearch({ from }) — returns Accessor<T>
import { useSearch } from '@tanstack/solid-router'
function Pagination() {
const search = useSearch({ from: '/products' })
return <span>Page {search().page}</span>
}
useParams({ from }) — returns Accessor<T>
import { useParams } from '@tanstack/solid-router'
function PostHeader() {
const params = useParams({ from: '/posts/$postId' })
return <h2>Post {params().postId}</h2>
}
useLoaderData({ from }) — returns Accessor<T>
import { useLoaderData } from '@tanstack/solid-router'
function PostContent() {
const data = useLoaderData({ from: '/posts/$postId' })
return <article>{data().post.content}</article>
}
useMatch({ from }) — returns Accessor<T>
import { useMatch } from '@tanstack/solid-router'
function PostDetails() {
const match = useMatch({ from: '/posts/$postId' })
return <div>{match().loaderData.post.title}</div>
}
Other Hooks
All imported from @tanstack/solid-router:
useMatches()—Accessor<Array<Match>>, all active route matchesuseParentMatches()—Accessor<Array<Match>>, parent route matchesuseChildMatches()—Accessor<Array<Match>>, child route matchesuseRouteContext({ from })—Accessor<T>, context frombeforeLoaduseLoaderDeps({ from })—Accessor<T>, loader dependency valuesuseBlocker({ shouldBlockFn })— blocks navigation for unsaved changesuseCanGoBack()—Accessor<boolean>useLocation()—Accessor<ParsedLocation>useLinkProps({ to, params?, search? })— returnsComponentProps<'a'>(NOT an Accessor)useMatchRoute()— returns a function; calling it returnsAccessor<false | Params>useHydrated()—Accessor<boolean>
Components Reference
RouterProvider
<RouterProvider router={router} />
Link
Type-safe navigation link. Children can be a function for active state:
;<Link to="/posts/$postId" params={{ postId: '42' }}>
View Post
</Link>
{
/* Function children for active state */
}
;<Link to="/about">
{(state) => <span classList={{ active: state.isActive }}>About</span>}
</Link>
Outlet
Renders the matched child route component:
function Layout() {
return (
<div>
<Sidebar />
<main>
<Outlet />
</main>
</div>
)
}
Navigate
Declarative redirect (triggers navigation in onMount):
import { Navigate } from '@tanstack/solid-router'
function OldPage() {
return <Navigate to="/new-page" />
}
Await
Renders deferred data with Solid's Suspense:
import { Await } from '@tanstack/solid-router'
import { Suspense } from 'solid-js'
function PostWithComments() {
const data = Route.useLoaderData()
return (
<Suspense fallback={<div>Loading...</div>}>
<Await promise={data().deferredComments}>
{(comments) => <For each={comments}>{(c) => <li>{c.text}</li>}</For>}
</Await>
</Suspense>
)
}
CatchBoundary
Error boundary wrapping Solid.ErrorBoundary:
import { CatchBoundary } from '@tanstack/solid-router'
;<CatchBoundary
getResetKey={() => 'widget'}
errorComponent={({ error }) => <div>Error: {error.message}</div>}
>
<RiskyWidget />
</CatchBoundary>
Other Components
CatchNotFound— catchesnotFound()errors in children;fallbackreceives the error dataBlock— declarative navigation blocker; useshouldBlockFnandwithResolverfor custom UIScrollRestoration— deprecated; usecreateRouter'sscrollRestoration: trueoption insteadClientOnly— renders children only after hydration; acceptsfallbackprop
Block
Declarative navigation blocker component:
import { Block } from '@tanstack/solid-router'
;<Block shouldBlockFn={() => formIsDirty()} withResolver>
{({ status, proceed, reset }) => (
<Show when={status === 'blocked'}>
<div>
<p>Are you sure?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
</Show>
)}
</Block>
ScrollRestoration
Restores scroll position on navigation:
import { ScrollRestoration } from '@tanstack/solid-router'
// In root route component
;<ScrollRestoration />
ClientOnly
Renders children only after hydration:
import { ClientOnly } from '@tanstack/solid-router'
;<ClientOnly fallback={<div>Loading...</div>}>
<BrowserOnlyWidget />
</ClientOnly>
Head Management
Uses @solidjs/meta under the hood:
import { HeadContent, Scripts } from '@tanstack/solid-router'
function RootDocument(props) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{props.children}
<Scripts />
</body>
</html>
)
}
Solid-Specific Patterns
Custom Link Component with createLink
import { createLink } from '@tanstack/solid-router'
const StyledLinkComponent = (props) => (
<a {...props} class={`styled-link ${props.class ?? ''}`} />
)
const StyledLink = createLink(StyledLinkComponent)
function Nav() {
return (
<StyledLink to="/posts/$postId" params={{ postId: '42' }}>
Post
</StyledLink>
)
}
Using Solid Primitives with Router State
import { createMemo, Show, For } from 'solid-js'
import { useRouterState } from '@tanstack/solid-router'
function Breadcrumbs() {
const matches = useRouterState({ select: (s) => s.matches })
const crumbs = createMemo(() =>
matches().filter((m) => m.context?.breadcrumb),
)
return (
<nav>
<For each={crumbs()}>
{(match) => <span>{match.context.breadcrumb}</span>}
</For>
</nav>
)
}
Auth with Router Context
import { createRootRouteWithContext } from '@tanstack/solid-router'
const rootRoute = createRootRouteWithContext<{ auth: AuthState }>()({
component: RootComponent,
})
// In main.tsx — provide context at router creation
const router = createRouter({
routeTree,
context: { auth: authState },
})
// In a route — access via beforeLoad (NOT hooks)
beforeLoad: ({ context }) => {
if (!context.auth.isAuthenticated) {
throw redirect({ to: '/login' })
}
}
Common Mistakes
1. CRITICAL: Forgetting to call Accessor
Hooks return Accessor<T> — you must call them to read the value. This is the #1 migration issue from React.
// WRONG — comparing the accessor function, not its value
const params = useParams({ from: '/posts/$postId' })
if (params.postId === '42') { ... } // params is a function!
// CORRECT — call the accessor
const params = useParams({ from: '/posts/$postId' })
if (params().postId === '42') { ... }
2. HIGH: Destructuring reactive values
Destructuring breaks Solid's reactivity tracking.
// WRONG — loses reactivity
const { page } = useSearch({ from: '/products' })()
// CORRECT — access through accessor
const search = useSearch({ from: '/products' })
<span>Page {search().page}</span>
3. HIGH: Using React hooks in beforeLoad or loader
beforeLoad and loader are NOT components — they are plain async functions. No hooks (React or Solid) can be used in them. Pass state via router context instead.
4. MEDIUM: Wrong plugin target
Must set target: 'solid' in the router plugin config. Default is 'react'.
Cross-References
@tanstack/router-core#router-core— all sub-skills for domain-specific patterns (search params, data loading, navigation, auth, SSR, etc.)
FAQ
What does the solid-router skill do?
Solid bindings for TanStack Router: RouterProvider, useRouter, useRouterState, useMatch, useMatches, useLocation, useSearch, useParams, useNavigate, useLoaderData, useLoaderDeps, useRouteContext, useBlocker, useCanGoBack, Link, Navigate, Outlet, CatchBoundary, ErrorComponent. Solid-specific patterns with Accessor returns, createSignal/createMemo/createEffect, Show/Switch/Match/Dynamic, and @solidjs/meta for head management.
How do I install solid-router?
Run npx skills add https://github.com/tanstack/router --skill solid-router in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/solid-router/ (personal) or .claude/skills/solid-router/ (project).
Which AI agents can use solid-router?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is solid-router safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains solid-router?
It is published by tanstack in tanstack/router. It has 58 recorded installs.
More from tanstack/router
router-core
tanstack/router
Framework-agnostic core concepts for TanStack Router: route trees, createRouter, createRoute, createRootRoute, createRootRouteWithContext, addChildren, Register type declaration, route matching, route sorting, file naming conventions. Entry point for all router skills.
react-router
tanstack/router
React bindings for TanStack Router: RouterProvider, useRouter, useRouterState, useMatch, useMatches, useLocation, useSearch, useParams, useNavigate, useLoaderData, useLoaderDeps, useRouteContext, useBlocker, useCanGoBack, Link, Navigate, Outlet, CatchBoundary, ErrorComponent. React-specific patterns for hooks, providers, SSR hydration, and createLink with forwardRef.
react-start
tanstack/router
React bindings for TanStack Start: createStart, StartClient, StartServer, React-specific imports, re-exports from @tanstack/react-router, full project setup with React, useServerFn hook.
start-core
tanstack/router
Core overview for TanStack Start: tanstackStart() Vite plugin, getRouter() factory, root route document shell (HeadContent, Scripts, Outlet), client/server entry points, routeTree.gen.ts, tsconfig configuration. Entry point for all Start skills.
router-query
tanstack/router
Integrating TanStack Router with TanStack Query: queryClient in router context, static queries and fire-and-forget queries in loaders, useSuspenseQuery in components, defaultPreloadStaleTime: 0, setupRouterSsrQueryIntegration for SSR dehydration/hydration and streaming, per-request QueryClient isolation.
start-server-core
tanstack/router
Server-side runtime for TanStack Start: createStartHandler, request/response utilities (getRequest, setResponseHeader, setCookie, getCookie, appendResponseHeader), three-phase request handling, AsyncLocalStorage context.
Popular other skills
hyperframes-cli
heygen-com/hyperframes
The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.
triage
mattpocock/skills
Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.
ask-matt
mattpocock/skills
Ask which skill or flow fits your situation. A router over the skills in this repo.
to-questionnaire
mattpocock/skills
Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
loop-me
mattpocock/skills
Grill me about specs for the workflows I want to build, within this workspace.