Routing & navigation
Edit this pageSolid 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 functionfunction 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.
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.
To prevent a fetch from happening more than once, or to trigger a refetch, you
can use the cache
function.
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.
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.