**Solid Router** brings fine-grained reactivity to route navigation, enabling your single-page application to become multi-paged without full page reloads. Fully integrated into the SolidJS ecosystem, Solid Router provides declarative syntax with features like universal rendering and parallel data fetching for best performance.
Explore the official [documentation](https://docs.solidjs.com/solid-router) for detailed guides and examples.
## Core Features
- **All Routing Modes**:
- [History-Based](https://docs.solidjs.com/solid-router/reference/components/router#router) for standard browser navigation
- [Hash-Based](https://docs.solidjs.com/solid-router/reference/components/hash-router#hashrouter) for navigation based on URL hash
- [Static Routing](https://docs.solidjs.com/solid-router/rendering-modes/ssr#server-side-rendering) for server-side rendering (_SSR_)
- [Memory-Based](https://docs.solidjs.com/solid-router/reference/components/memory-router#memoryrouter) for testing in non-browser environments
- **TypeScript**: Full integration for robust, type-safe development
- **Universal Rendering**: Seamless rendering on both client and server environments
- **Declarative**: Define routes as components or as an object
- **Preload Functions**: Parallel data fetching, following the render-as-you-fetch pattern
- **Dynamic Route Parameters**: Flexible URL patterns with parameters, optional segments, and wildcards
- **Data APIs with Caching**: Reactive data fetching with deduplication and revalidation
## Table of contents
- [Getting Started](#getting-started)
- [Set Up the Router](#set-up-the-router)
- [Configure Your Routes](#configure-your-routes)
- [Create Links to Your Routes](#create-links-to-your-routes)
- [Dynamic Routes](#dynamic-routes)
- [Nested Routes](#nested-routes)
- [Hash Mode Router](#hash-mode-router)
- [Memory Mode Router](#memory-mode-router)
- [Data APIs](#data-apis)
- [Config Based Routing](#config-based-routing)
- [Components](#components)
- [Router Primitives](#router-primitives)
- [useParams](#useparams)
- [useNavigate](#usenavigate)
- [useLocation](#uselocation)
- [useSearchParams](#usesearchparams)
- [useIsRouting](#useisrouting)
- [useMatch](#usematch)
- [useCurrentMatches](#useCurrentMatches)
- [useBeforeLeave](#usebeforeleave)
- [SPAs in Deployed Environments](#spas-in-deployed-environments)
## Getting Started
### Set Up the Router
```bash
# use preferred package manager
npm add @solidjs/router
```
Install `@solidjs/router`, then start your application by rendering the router component
```jsx
import { render } from "solid-js/web";
import { Router } from "@solidjs/router";
render(() => , document.getElementById("app"));
```
This sets up a Router that will match on the url to display the desired page
### Configure Your Routes
Solid Router allows you to configure your routes using JSX:
1. Add each route to a `` using the `Route` component, specifying a path and a component to render when the user navigates to that path.
```jsx
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import Home from "./pages/Home";
import Users from "./pages/Users";
render(
() => (
),
document.getElementById("app")
);
```
2. Provide a root level layout
This will always be there and won't update on page change. It is the ideal place to put top level navigation and Context Providers
```jsx
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import Home from "./pages/Home";
import Users from "./pages/Users";
const App = (props) => (
<>
My Site with lots of pages
{props.children}
>
);
render(
() => (
),
document.getElementById("app")
);
```
3. Create a catch-all route (404 page)
We can create catch-all routes for pages not found at any nested level of the router. We use `*` and optionally the name of a parameter to retrieve the rest of the path.
```jsx
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import Home from "./pages/Home";
import Users from "./pages/Users";
import NotFound from "./pages/404";
const App = (props) => (
<>
My Site with lots of pages
{props.children}
>
);
render(
() => (
),
document.getElementById("app")
);
```
4. Lazy-load route components
This way, the `Users` and `Home` components will only be loaded if you're navigating to `/users` or `/`, respectively.
```jsx
import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
const Users = lazy(() => import("./pages/Users"));
const Home = lazy(() => import("./pages/Home"));
const App = (props) => (
<>
My Site with lots of pages
{props.children}
>
);
render(
() => (
),
document.getElementById("app")
);
```
### Create Links to Your Routes
Use an anchor tag that takes you to a route:
```jsx
import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
const Users = lazy(() => import("./pages/Users"));
const Home = lazy(() => import("./pages/Home"));
const App = (props) => (
<>
My Site with lots of pages
{props.children}
>
);
render(
() => (
),
document.getElementById("app")
);
```
## Dynamic Routes
If you don't know the path ahead of time, you might want to treat part of the path as a flexible parameter that is passed on to the component.
```jsx
import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
const Users = lazy(() => import("./pages/Users"));
const User = lazy(() => import("./pages/User"));
const Home = lazy(() => import("./pages/Home"));
render(
() => (
),
document.getElementById("app")
);
```
The colon indicates that `id` can be any string, and as long as the URL fits that pattern, the `User` component will show.
You can then access that `id` from within a route component with `useParams`.
**Note on Animation/Transitions**:
Routes that share the same path match will be treated as the same route. If you want to force re-render you can wrap your component in a keyed `` like:
```jsx
```
---
Each path parameter can be validated using a `MatchFilter`.
This allows for more complex routing descriptions than just checking the presence of a parameter.
```jsx
import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import type { MatchFilters } from "@solidjs/router";
const User = lazy(() => import("./pages/User"));
const filters: MatchFilters = {
parent: ["mom", "dad"], // allow enum values
id: /^\d+$/, // only allow numbers
withHtmlExtension: (v: string) => v.length > 5 && v.endsWith(".html"), // we want an `*.html` extension
};
render(
() => (
),
document.getElementById("app")
);
```
Here, we have added the `matchFilters` prop. This allows us to validate the `parent`, `id` and `withHtmlExtension` parameters against the filters defined in `filters`.
If the validation fails, the route will not match.
So in this example:
- `/users/mom/123/contact.html` would match,
- `/users/dad/123/about.html` would match,
- `/users/aunt/123/contact.html` would not match as `:parent` is not 'mom' or 'dad',
- `/users/mom/me/contact.html` would not match as `:id` is not a number,
- `/users/dad/123/contact` would not match as `:withHtmlExtension` is missing `.html`.
---
### Optional Parameters
Parameters can be specified as optional by adding a question mark to the end of the parameter name:
```jsx
// Matches stories and stories/123 but not stories/123/comments
```
### Wildcard Routes
`:param` lets you match an arbitrary name at that point in the path. You can use `*` to match any end of the path:
```jsx
// Matches any path that begins with foo, including foo/, foo/a/, foo/a/b/c
```
If you want to expose the wild part of the path to the component as a parameter, you can name it:
```jsx
```
Note that the wildcard token must be the last part of the path; `foo/*any/bar` won't create any routes.
### Multiple Paths
Routes also support defining multiple paths using an array. This allows a route to remain mounted and not rerender when switching between two or more locations that it matches:
```jsx
// Navigating from login to register does not cause the Login component to re-render
```
## Nested Routes
The following two route definitions have the same result:
```jsx
```
```jsx
```
`/users/:id` renders the `` component, and `/users/` is an empty route.
Only leaf Route nodes (innermost `Route` components) are given a route. If you want to make the parent its own route, you have to specify it separately:
```jsx
//This won't work the way you'd expect
// This works
// This also works
```
You can also take advantage of nesting by using `props.children` passed to the route component.
```jsx
function PageWrapper(props) {
return (
);
}
;
```
The routes are still configured the same, but now the route elements will appear inside the parent element where the `props.children` was declared.
You can nest indefinitely - just remember that only leaf nodes will become their own routes. In this example, the only route created is `/layer1/layer2`, and it appears as three nested divs.
```jsx
Onion starts here {props.children}
}
>
Another layer {props.children}
}
>
Innermost layer
} />
```
## Preload Functions
Even with smart caches it is possible that we have waterfalls both with view logic and with lazy loaded code. With preload functions, we can instead start fetching the data parallel to loading the route, so we can use the data as soon as possible. The preload function is called when the Route is loaded or eagerly when links are hovered.
As its only argument, the preload function is passed an object that you can use to access route information:
```js
import { lazy } from "solid-js";
import { Route } from "@solidjs/router";
const User = lazy(() => import("./pages/users/[id].js"));
// preload function
function preloadUser({ params, location }) {
// do preloading
}
// Pass it in the route definition
;
```
| key | type | description |
| -------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| params | object | The route parameters (same value as calling `useParams()` inside the route component) |
| location | `{ pathname, search, hash, query, state, key}` | An object that you can use to get more information about the path (corresponds to [`useLocation()`](#uselocation)) |
| intent | `"initial", "navigate", "native", "preload"` | Indicates why this function is being called.
"initial" - the route is being initially shown (ie page load)
"native" - navigate originated from the browser (eg back/forward)
"navigate" - navigate originated from the router (eg call to navigate or anchor clicked)
"preload" - not navigating, just preloading (eg link hover)
|
A common pattern is to export the preload function and data wrappers that corresponds to a route in a dedicated `route.data.js` file. This way, the data function can be imported without loading anything else.
```js
import { lazy } from "solid-js";
import { Route } from "@solidjs/router";
import preloadUser from "./pages/users/[id].data.js";
const User = lazy(() => import("/pages/users/[id].js"));
// In the Route definition
;
```
The `preload` function's return value is passed to the page component for any intent other than `"preload"`, allowing you to initialize data or alternatively use our new Data APIs:
## Data APIs
Keep in mind that these are entirely optional, but they demonstrate the power of our preload mechanism.
### `query`
To prevent duplicate fetching and to handle refetching triggers, we provide a query API that accepts a function and returns the same function.
```jsx
const getUser = query(async (id) => {
return (await fetch(`/api/users/${id}`)).json();
}, "users"); // used as the query key + serialized arguments
```
It is expected that the arguments to the query function are serializable.
This query accomplishes the following:
1. It does deduping on the server for the lifetime of the request.
2. It fills a preload cache in the browser which lasts 5 seconds. When a route is preloaded on hover or when preload is called when entering a route it will make sure to dedupe calls.
3. We have a reactive refetch mechanism based on key. So we can tell routes that aren't new to retrigger on action revalidation.
4. It will serve as a back/forward cache for browser navigation up to 5 mins. Any user based navigation or link click bypasses this cache. Revalidation or new fetch updates the cache.
Using it with preload function might look like:
```js
import { lazy } from "solid-js";
import { Route } from "@solidjs/router";
import { getUser } from ... // the query function
const User = lazy(() => import("./pages/users/[id].js"));
// preload function
function preloadUser({params, location}) {
void getUser(params.id)
}
// Pass it in the route definition
;
```
Inside your page component you:
```jsx
// pages/users/[id].js
import { getUser } from ... // the query function
export default function User(props) {
const user = createAsync(() => getUser(props.params.id));
return
{user().name}
;
}
```
Cached function has a few useful methods for getting the key that are useful for invalidation.
```ts
let id = 5;
getUser.key; // returns "users"
getUser.keyFor(id); // returns "users[5]"
```
You can revalidate the query using the `revalidate` method or you can set `revalidate` keys on your response from your actions. If you pass the whole key it will invalidate all the entries for the query (ie "users" in the example above). You can also invalidate a single entry by using `keyFor`.
`query` can be defined anywhere and then used inside your components with:
### `createAsync`
This is light wrapper over `createResource` that aims to serve as stand-in for a future primitive we intend to bring to Solid core in 2.0. It is a simpler async primitive where the function tracks like `createMemo` and it expects a promise back that it turns into a Signal. Reading it before it is ready causes Suspense/Transitions to trigger.
```jsx
const user = createAsync((currentValue) => getUser(params.id));
```
It also preserves `latest` field from `createResource`. Note that it will be removed in the future.
```jsx
const user = createAsync((currentValue) => getUser(params.id));
return
{user.latest.name}
;
```
Using `query` in `createResource` directly won't work properly as the fetcher is not reactive and it won't invalidate properly.
### `createAsyncStore`
Similar to `createAsync` except it uses a deeply reactive store. Perfect for applying fine-grained changes to large model data that updates.
It also supports `latest` field which will be removed in the future.
```jsx
const todos = createAsyncStore(() => getTodos());
```
### `action`
Actions are data mutations that can trigger invalidations and further routing. A list of prebuilt response helpers can be found below.
```jsx
import { action, revalidate, redirect } from "@solidjs/router"
// anywhere
const myAction = action(async (data) => {
await doMutation(data);
throw redirect("/", { revalidate: getUser.keyFor(data.id) }); // throw a response to do a redirect
});
// in component
//or
```
Actions only work with post requests, so make sure to put `method="post"` on your form.
Sometimes it might be easier to deal with typed data instead of `FormData` and adding additional hidden fields. For that reason Actions have a with method. That works similar to `bind` which applies the arguments in order.
Picture an action that deletes Todo Item:
```js
const deleteTodo = action(async (formData: FormData) => {
const id = Number(formData.get("id"))
await api.deleteTodo(id)
})
```
Instead with `with` you can write this:
```js
const deleteTodo = action(api.deleteTodo)
```
Actions also take a second argument which can be the name or an option object with `name` and `onComplete`. `name` is used to identify SSR actions that aren't server functions (see note below). `onComplete` allows you to configure behavior when `action`s complete. Keep in mind `onComplete` does not work when JavaScript is disabled.
#### Notes on `