Guides

Routing & navigation

Edit this page

Solid Router simplifies routing in Solid applications to help developers manage navigation and rendering by defining routes using JSX or objects passed via props.


Getting started

1. Install the router

This package is not included by default.

2. Setup the <Router> component

Start your application by rendering the Router component. This component will match the URL to display the desired page.

import { render } from "solid-js/web";
import { Router } from "@solidjs/router";
render(() => <Router />, document.getElementById("root"));

3. Provide a root level layout

This layout will not update on page change and is the ideal place for top-level navigation and Context Providers.

import { render } from "solid-js/web";
import { Router } from "@solidjs/router";
const App = (props) => (
<>
<h1>Site Title</h1>
{props.children}
</>
);
render(() => <Router root={App} />, document.getElementById("root"));

4. Add routes

Each route is added to the Router using the Route component. Here, you specify a path and a component to render once the user navigates to that path.

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) => (
<>
<h1>Site Title</h1>
{props.children}
</>
);
render(
() => (
<Router root={App}>
<Route path="/" component={Home} />
<Route path="/users" component={Users} />
</Router>
),
document.getElementById("root")
);

5. Create a CatchAll route (404 page)

A catchall route can be used for pages not found at any nested level of the router. Using * will retrieve the rest of the path. Optionally, you can also add a parameter name.

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/NotFound";
const App = (props) => (
<>
<h1>Site Title</h1>
{props.children}
</>
);
render(
() => (
<Router root={App}>
<Route path="/" component={Home} />
<Route path="/users" component={Users} />
<Route path="*paramName" component={NotFound} />
</Router>
),
document.getElementById("root")
);

6. Create links to your routes

The <A> component provides navigation to an application's routes. Alternatively, you can use the native anchor tag. However, the <A> component provides additional functionality including properties for CSS, inactiveClass and activeClass.

import { render } from "solid-js/web";
import { Router, Route, A } from "@solidjs/router";
import Home from "./pages/Home";
import Users from "./pages/Users";
import NotFound from "./pages/NotFound";
const App = (props) => (
<>
<nav>
<A href="/">Home</A>
<A href="/users">Users</A>
</nav>
<h1>Site Title</h1>
{props.children}
</>
);
render(
() => (
<Router root={App}>
<Route path="/" component={Home} />
<Route path="/users" component={Users} />
<Route path="*paramName" component={NotFound} />
</Router>
),
document.getElementById("root")
);

Lazy-loading route components

The lazy function postpones the loading of a component until it is navigated to.

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) => (
<>
<h1>Site Title</h1>
{props.children}
</>
);
render(
() => (
<Router root={App}>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
</Router>
),
document.getElementById("root")
);

Dynamic routes

If a path is unknown ahead of time, you can treat part of the path as a flexible parameter.

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(
() => (
<Router>
<Route path="/users" component={Users} />
<Route path="/users/:id" component={User} />
<Route path="/" component={Home} />
</Router>
),
document.getElementById("root")
);

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 will be treated as the same route. If you want to force re-render, you can wrap your component in a keyed <Show>:

<Show when={params.something} keyed>
<MyComponent>
</Show>

Accessing parameters

In cases where you may need to access a dynamic route's parameters within your components, the useParams primitive is available. Once the parameters have been accessed using useParams, they can be used within your component:

import { useParams } from "@solidjs/router";
const User = () => {
const params = useParams(); // Retrieve the dynamic route parameters
// Now you can access the id parameter as params.id
return (
<p>
This is the user with the id of <code>{params.id}</code>
</p>
);
};

useParams can be especially useful with other Solid primitives, such as createResource and createSignal, which can create dynamic behaviors based on the route parameters.

import { createResource } from "solid-js";
import { useParams } from "@solidjs/router";
async function fetchUser(id) {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${id}`
);
return response.json();
}
const User = () => {
const params = useParams();
const [data] = createResource(params.id, fetchUser); // Pass the id parameter to createResource
return (
<div>
<Show when={!data.loading} fallback={<p>Loading...</p>}>
<div>
<p>Name: {data().name}</p>
<p>Email: {data().email}</p>
<p>Phone: {data().phone}</p>
</div>
</Show>
</div>
);
};

Every time the id parameter changes in this example, the fetchUser function is called to fetch the new user data.

Validating routes

Each path parameter can be validated using a MatchFilter. Instead of checking for the presence of a parameter, this allows for more complex routing descriptions:

import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import type { SegmentValidators } from "./types";
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"), // only `*.html` extensions wanted
};
render(() => (
<Router>
<Route
path="/users/:parent/:id/:withHtmlExtension"
component={User}
matchFilters={filters}
/>
</Router>
), document.getElementById("root"));

In this example, the matchFilters prop provides a way to validate the parent, id and withHtmlExtension parameters against the filters defined in filters. If the validation fails, the route will not match.

In this example:

  • /users/mom/123/contact.html will match,
  • /users/dad/123/about.html will match,
  • /users/aunt/123/contact.html will not match as :parent is not 'mom' or 'dad',
  • /users/mom/me/contact.html will not match as :id is not a number,
  • /users/dad/123/contact will 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:

// Matches stories and stories/123 but not stories/123/comments
<Route path="/stories/:id?" component={Stories} />

Wildcard routes

To match any descendent routes within a given path, you can use the wildcard token (*). This can be used to represent any value in that segment of the path.

// Will match any path beginning with foo (eg. foo/, foo/a/, foo/a/b/c)
<Route path="foo/*" component={Foo} />

To expose the wildcard portion to the component as a parameter, you can name it:

<Route path="foo/*any" component={Foo} />

Wildcard tokens must be the last part of the path; foo/*any/bar will not create any routes.

Multiple paths

The Routes component also supports defining multiple paths using an array. This allows avoids a route rerendering when switching between two or more locations that it matches:

// Navigating from "/login" to "/register" will not cause the component to re-render
<Route path={["login", "register"]} component={Login} />

Nested routes

Only leaf <Route> nodes (the innermost <Route> components) are given a route.

<Route path="/users" component={Users}>
<Route path="/:id" component={User} />
</Route>

The following two route definitions both match the same URL /users/:id and render the same component:

<Route path="/users/:id" component={User} />
<Route path="/users">
<Route path="/:id" component={User} />
</Route>

If you want to make the parent its own route, you have to specify it separately:

<Route path="/users" component={Users} />
<Route path="/users/:id" component={User} />
// or
<Route path="/users">
<Route path="/" component={Users} />
<Route path="/:id" component={User} />
</Route>

You can also take advantage of nesting by using props.children passed to the route component.

function PageWrapper(props) {
return (
<div>
<h1> We love our users! </h1>
{props.children}
<A href="/">Back Home</A>
</div>
);
}
<Route path="/users" component={PageWrapper}>
<Route path="/" component={Users} />
<Route path="/:id" component={User} />
</Route>;

The routes are still configured the same, however now their components will appear inside the parent component where the props.children is declared.

Routes can also be nested indefinitely. This example will only render the route /layer1/layer2, which will be nested in 3 divs.

<Route
path="/"
component={(props) => <div>Outermost layer starts here {props.children}</div>}
>
<Route
path="layer1"
component={(props) => <div>Second layer {props.children}</div>}
>
<Route path="layer2" component={() => <div>Innermost layer</div>} />
</Route>
</Route>

Preload functions

With preload functions, data fetching is started parallel to loading the route, so it can be used as soon as possible. The preload function prevents this by being called once the Route is loaded, or eagerly if links are hovered.

As the only argument, the preload function is passed an object that is used to access route information:

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 preload
}

The preload function is then passed in the <Route> definition:

<Route path="/users/:id" component={User} preload={loadUser} />

You can export preload functions and data wrappers that correspond to routes from a dedicated [route].data.js or [route].data.ts file. This pattern provides a way to import the data function without loading anything else.

src/pages/users/[id].data.js
import { query } from "@solidjs/router";
export const getUser = query(async (id) => {
return (await fetch(`https://swapi.tech/api/people/${id}/`)).json();
}, "getUser");
export function preloadUser({ params, location, intent }) {
return getUser(params.id);
}

preloadUser is passed an object which contains params, location and intent.

Please note that while it is best practice to write these files as [id].data.js, you can still write route.data.js.

The value of a preload function is passed to the page component when called at any time other than "preload". This means you can initialize the page, or use Data APIs.

index.jsx
import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import { preloadUser } from "./pages/users/[id].data.js";
const Home = lazy(() => import("./pages/Home"));
const User = lazy(() => import("./pages/users/[id]"));
render(
() => (
<Router>
<Route path="/" component={Home} />
<Route path="/users/:id" component={User} preload={preloadUser} />
</Router>
),
document.getElementById("root")
);

[id].jsx contains the component that gets rendered. When you wrap the function within createAsync with the imported function, it will yield a signal once the anticipated promise resolves.

[id].jsx
import { createAsync } from "@solidjs/router";
import { getUser } from "./[id].data";
export default function Users(props) {
console.log("Users.props", props);
const user = createAsync(() => getUser(props.params.id));
return (
<>
<h1>User</h1>
<div>
<pre>{JSON.stringify(user(), null, 2)}</pre>
</div>
</>
);
}

To learn more about routing your Solid applications, visit the Solid Router documentation.

Report an issue with this page