Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tanstack/router/llms.txt

Use this file to discover all available pages before exploring further.

Solid Router

TanStack Router provides first-class support for SolidJS with reactive primitives, components, and patterns designed specifically for Solid applications.

Installation

npm install @tanstack/solid-router

Core Components

RouterProvider

The top-level component that renders active route matches and provides the router to the Solid tree via context.
import { render } from 'solid-js/web'
import { RouterProvider, createRouter } from '@tanstack/solid-router'

const router = createRouter({ routeTree })

render(
  () => <RouterProvider router={router} />,
  document.getElementById('app')!
)
Reactive navigation component with preloading and active state support.
import { Link } from '@tanstack/solid-router'

function Navigation() {
  return (
    <Link
      to="/posts/$postId"
      params={{ postId: '123' }}
      activeProps={{
        class: 'font-bold',
      }}
      activeOptions={{ exact: true }}
    >
      View Post
    </Link>
  )
}
Note: Use class instead of className (Solid convention). Key Props:
  • to - Type-safe destination path
  • params - Path parameters
  • search - Search parameters
  • activeProps - Props applied when link is active (returns a function in Solid)
  • inactiveProps - Props applied when link is inactive
  • preload - Controls preloading behavior (false, 'intent', 'viewport')
  • disabled - Disables navigation

Outlet

Renders child route matches at the current level.
import { Outlet } from '@tanstack/solid-router'

function LayoutComponent() {
  return (
    <div>
      <header>Header</header>
      <main>
        <Outlet />
      </main>
    </div>
  )
}

ErrorComponent

Default error boundary component that displays error details.
import { ErrorComponent } from '@tanstack/solid-router'
import type { ErrorComponentProps } from '@tanstack/solid-router'

function CustomErrorComponent(props: ErrorComponentProps) {
  return (
    <div>
      <h1>Error</h1>
      <pre>{props.error.message}</pre>
    </div>
  )
}
Component-based navigation trigger.
import { Navigate } from '@tanstack/solid-router'

function RedirectComponent() {
  return <Navigate to="/home" />
}

Solid Primitives

useRouter

Access the router instance from any component.
import { useRouter } from '@tanstack/solid-router'

function MyComponent() {
  const router = useRouter()
  
  const handleNavigate = () => {
    router.navigate({ to: '/about' })
  }
  
  return <button onClick={handleNavigate}>Go to About</button>
}

useNavigate

Type-safe programmatic navigation.
import { useNavigate } from '@tanstack/solid-router'

function MyComponent() {
  const navigate = useNavigate({ from: '/posts' })
  
  const handleClick = () => {
    navigate({ to: '/posts/$postId', params: { postId: '123' } })
  }
  
  return <button onClick={handleClick}>View Post</button>
}

useParams

Access route path parameters reactively.
import { useParams } from '@tanstack/solid-router'
import { createEffect } from 'solid-js'

function PostComponent() {
  const params = useParams({ from: '/posts/$postId' })
  
  // params is reactive - access with params.postId
  createEffect(() => {
    console.log('Post ID changed:', params.postId)
  })
  
  return <div>Post ID: {params.postId}</div>
}
Note: In Solid, useParams returns a reactive proxy, not a signal.

useSearch

Access route search parameters reactively.
import { useSearch } from '@tanstack/solid-router'
import { createEffect } from 'solid-js'

function PostsComponent() {
  const search = useSearch({ from: '/posts' })
  
  // search is reactive - access with search.filter
  createEffect(() => {
    console.log('Filter changed:', search.filter)
  })
  
  return <div>Filter: {search.filter}</div>
}

useLoaderData

Access data loaded by the route’s loader function as a signal.
import { useLoaderData } from '@tanstack/solid-router'

function PostComponent() {
  const post = useLoaderData({ from: '/posts/$postId' })
  
  // post is a Solid signal - access with post()
  return (
    <div>
      <h1>{post().title}</h1>
      <p>{post().body}</p>
    </div>
  )
}
Key Difference: In Solid, useLoaderData returns a signal (function), so access data with post() instead of post.

useMatch

Access the current route match with reactive properties.
import { useMatch } from '@tanstack/solid-router'

function MyComponent() {
  const match = useMatch({ from: '/posts/$postId' })
  
  // match is reactive
  return (
    <div>
      <p>Status: {match().status}</p>
      <p>Post ID: {match().params.postId}</p>
    </div>
  )
}

useRouteContext

Access route context provided by parent routes reactively.
import { useRouteContext } from '@tanstack/solid-router'

function MyComponent() {
  const context = useRouteContext({ from: '/posts/$postId' })
  
  return <div>User: {context.user.name}</div>
}

useLocation

Access the current location object reactively.
import { useLocation } from '@tanstack/solid-router'
import { createEffect } from 'solid-js'

function MyComponent() {
  const location = useLocation()
  
  createEffect(() => {
    console.log('Location changed:', location.pathname)
  })
  
  return (
    <div>
      <p>Pathname: {location.pathname}</p>
      <p>Hash: {location.hash}</p>
    </div>
  )
}

useRouterState

Access router state reactively with optional selector.
import { useRouterState } from '@tanstack/solid-router'
import { Show } from 'solid-js'

function MyComponent() {
  const isLoading = useRouterState({
    select: (state) => state.isLoading,
  })
  
  return (
    <Show when={isLoading()} fallback={<div>Content</div>}>
      <div>Loading...</div>
    </Show>
  )
}
Note: Returns a signal, so access with isLoading().

useMatchRoute

Check if a specific route matches the current location.
import { useMatchRoute } from '@tanstack/solid-router'
import { Show } from 'solid-js'

function MyComponent() {
  const matchRoute = useMatchRoute()
  const isPostsRoute = () => matchRoute({ to: '/posts' })
  
  return (
    <Show when={isPostsRoute()} fallback={<div>Not on Posts Page</div>}>
      <div>On Posts Page</div>
    </Show>
  )
}

useLinkProps

Build anchor-like props for custom link components.
import { useLinkProps } from '@tanstack/solid-router'

function CustomLink(props) {
  const linkProps = useLinkProps(props)
  
  return <a {...linkProps} class="custom-link" />
}

useBlocker

Block navigation based on a condition (useful for unsaved changes).
import { useBlocker } from '@tanstack/solid-router'
import { createSignal } from 'solid-js'

function FormComponent() {
  const [hasUnsavedChanges, setHasUnsavedChanges] = createSignal(false)
  
  useBlocker({
    condition: hasUnsavedChanges,
    blockerFn: () => window.confirm('You have unsaved changes. Leave anyway?'),
  })
  
  return <form>...</form>
}

Route API

RouteApi

Route-specific API with pre-bound reactive primitives for a specific route ID.
import { getRouteApi } from '@tanstack/solid-router'

const postRoute = getRouteApi('/posts/$postId')

function PostComponent() {
  const params = postRoute.useParams()
  const post = postRoute.useLoaderData()
  const navigate = postRoute.useNavigate()
  
  return (
    <div>
      <h1>{post().title}</h1>
      <postRoute.Link to="/posts">Back to Posts</postRoute.Link>
    </div>
  )
}

Creating Routes

Code-based Routes

import { createRoute, createRootRoute, Outlet } from '@tanstack/solid-router'

const rootRoute = createRootRoute({
  component: () => (
    <>
      <nav>...</nav>
      <Outlet />
    </>
  ),
})

const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: () => <div>Welcome Home!</div>,
})

const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  loader: ({ params }) => fetchPost(params.postId),
  component: PostComponent,
})

function PostComponent() {
  const post = postRoute.useLoaderData()
  // Access as signal: post()
  return <div>{post().title}</div>
}

const routeTree = rootRoute.addChildren([indexRoute, postRoute])

File-based Routes

// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/solid-router'

export const Route = createRootRoute({
  component: () => (
    <>
      <nav>...</nav>
      <Outlet />
    </>
  ),
})

// src/routes/index.tsx
import { createFileRoute } from '@tanstack/solid-router'

export const Route = createFileRoute('/')({ 
  component: () => <div>Welcome Home!</div>,
})

// src/routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'

export const Route = createFileRoute('/posts/$postId')({
  loader: ({ params }) => fetchPost(params.postId),
  component: PostComponent,
})

function PostComponent() {
  const post = Route.useLoaderData()
  return <div>{post().title}</div>
}

SSR Support

Solid Router includes specialized SSR utilities:
// Server-side
import { renderToString } from 'solid-js/web'
import { createMemoryHistory } from '@tanstack/solid-router'

const history = createMemoryHistory({
  initialEntries: [request.url],
})

const router = createRouter({ 
  routeTree,
  history,
})

await router.load()

const html = renderToString(() => <RouterProvider router={router} />)

// Client-side
import { hydrate } from 'solid-js/web'
import { createBrowserHistory } from '@tanstack/solid-router'

const history = createBrowserHistory()
const router = createRouter({ routeTree, history })

hydrate(
  () => <RouterProvider router={router} />,
  document.getElementById('root')!
)

Solid-Specific Features

Reactive Data Flow

All router state is fully reactive and integrates with Solid’s reactivity system:
import { createEffect } from 'solid-js'
import { useParams, useSearch } from '@tanstack/solid-router'

function MyComponent() {
  const params = useParams({ from: '/posts/$postId' })
  const search = useSearch({ from: '/posts' })
  
  // Automatically tracks changes
  createEffect(() => {
    console.log('Params or search changed:', params.postId, search.filter)
  })
  
  return <div>Post: {params.postId}</div>
}

Signal-based Loader Data

Loader data is returned as Solid signals:
import { For } from 'solid-js'
import { useLoaderData } from '@tanstack/solid-router'

function PostsComponent() {
  const posts = useLoaderData({ from: '/posts' })
  
  return (
    <For each={posts()}>
      {(post) => <div>{post.title}</div>}
    </For>
  )
}

Suspense Integration

Works seamlessly with Solid’s Suspense:
import { Suspense } from 'solid-js'

const route = createRoute({
  path: '/posts',
  pendingComponent: () => <div>Loading posts...</div>,
  component: PostsComponent,
})

Show/Match Components

Use Solid’s control flow components:
import { Show, Match, Switch } from 'solid-js'
import { useRouterState } from '@tanstack/solid-router'

function MyComponent() {
  const state = useRouterState()
  
  return (
    <Switch>
      <Match when={state.isLoading}>
        <div>Loading...</div>
      </Match>
      <Match when={state.status === 'success'}>
        <div>Content loaded!</div>
      </Match>
    </Switch>
  )
}

Complete Example

import { render } from 'solid-js/web'
import { For } from 'solid-js'
import {
  RouterProvider,
  createRouter,
  createRootRoute,
  createRoute,
  Link,
  Outlet,
} from '@tanstack/solid-router'

const rootRoute = createRootRoute({
  component: () => (
    <div>
      <nav>
        <Link to="/" activeProps={{ class: 'font-bold' }}>
          Home
        </Link>
        <Link to="/posts" activeProps={{ class: 'font-bold' }}>
          Posts
        </Link>
      </nav>
      <Outlet />
    </div>
  ),
})

const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: () => <h1>Welcome Home!</h1>,
})

const postsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts',
  loader: () => fetch('/api/posts').then(r => r.json()),
  component: PostsComponent,
})

function PostsComponent() {
  const posts = postsRoute.useLoaderData()
  
  return (
    <ul>
      <For each={posts()}>
        {(post) => (
          <li>
            <Link to="/posts/$postId" params={{ postId: post.id }}>
              {post.title}
            </Link>
          </li>
        )}
      </For>
    </ul>
  )
}

const routeTree = rootRoute.addChildren([indexRoute, postsRoute])
const router = createRouter({ routeTree })

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

const rootElement = document.getElementById('root')!
render(() => <RouterProvider router={router} />, rootElement)

Best Practices

  1. Type Safety: Always register your router type:
    declare module '@tanstack/solid-router' {
      interface Register {
        router: typeof router
      }
    }
    
  2. Use Signals: Remember that useLoaderData and useRouterState return signals:
    const data = useLoaderData({ from: '/posts' })
    return <div>{data()}</div> // Call as function
    
  3. Reactive Params: useParams and useSearch return reactive proxies:
    const params = useParams({ from: '/posts/$postId' })
    return <div>{params.postId}</div> // Access directly, automatically reactive
    
  4. Control Flow: Use Solid’s control flow components:
    <Show when={isLoading()} fallback={<Content />}>
      <Spinner />
    </Show>
    
  5. Class vs ClassName: Use class (Solid convention) instead of className:
    <Link to="/" activeProps={{ class: 'active' }}>Home</Link>
    

Key Differences from React Router

  • Signals: useLoaderData and useRouterState return signals, not plain values
  • Reactive Proxies: useParams and useSearch return reactive proxies
  • Class Attribute: Use class instead of className
  • Control Flow: Use Show, For, Switch, Match instead of &&, map, ternaries
  • No Hooks Rules: Solid doesn’t have hook rules - use primitives anywhere