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

Set Up the Router

  1. Install @solidjs/router. This package is not included by default.
  1. To use the router, import the Router component to wrap the root component. This allows the routes to be displayed throughout the entire application.
import { render } from "solid-js/web";
import App from "./App";
import { Router } from "@solidjs/router";
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById("root")
);

Configure the Routes

Solid Router offers a convenient way to configure routes using JSX syntax:

  1. Using the Routes component, you can specify where the routes should appear in the application:
import { Routes, Route } from "@solidjs/router";
export default function App() {
return (
<>
<h1>This is my site</h1>
<Routes>// Routes will go here</Routes>
</>
);
}
  1. The Route component takes a path prop, which specifies what path or element to render when navigated to. When this path is successfully matched, the component or element prop will be triggered, rendering what it corresponds to. For grouping multiple routes, or for nesting, the Routes component must be used. For example:
import { Routes, Route } from "@solidjs/router";
import Home from "./pages/Home";
import About from "./pages/About";
export default function App() {
return (
<>
<h1>My Site with many pages</h1>
<Routes>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/wow" element={<div>You can even do this!</div>} />
</Routes>
</>
);
}

The A Component

The A component creates an anchor tag that navigates to routes within an application. Additionally, the component takes in an href prop which acts as the URL path that the link will navigate to.

import { A } from "@solidjs/router";
export default function App() {
return (
<div>
<header>
<A href="/home">Home</A>
<A href="/about">About</A>
</header>
</div>
);
}

The A component can also take in the activeClass prop, which is a class that will be applied when the current location matches the href prop.

import { A } from "@solidjs/router";
export default function App() {
return (
<div>
<header>
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<A href="/about">Home</A>
<A
href="/about"
activeClass="underlined" // when active, this link will now be underlined
>
About
</A>
</header>
</div>
);
}

A Component Props

proptypedescription
hrefstringIndicates the URL path to navigate to, which must start with a forward slash ("/").
noScrollbooleanIf true, the default behavior of scrolling to the top of the new page will be turned off.
replacebooleanIf set to true, the new page will not be added to the browser history, preventing the back button from navigate to the previous route. By default, the new page is added to the browser history.
stateserializable objectPasses a serialized object to the history stack during navigation. When the user navigates to the new state, an event is triggered, granting access to a copy of the state object associated with that particular entry in the browsing history. See history.pushState() for more info.
inactiveClassstringWill specifies the CSS class to apply when the location and href prop do not match.
activeClassstringThe CSS class that will be applied when the location and href prop match.
endbooleanWhen set to true, the link is considered active only when the current location exactly matches the href. However, when set to false, the link is considered active if the current location starts with the href value.

The Navigate component

Solid router offers a Navigate component that works similarly to A, however, it will immediately navigate to the provided path as soon as the component is rendered. In addition to the href prop, there is an additional option to pass a function to href that returns a path to navigate to.

function getPath({ navigate, location }) {
// `navigate` is the result of calling useNavigate();
// `location` is the result of calling useLocation().
// You can use those to dynamically determine a path to navigate to
return "/some-path";
}
//Navigating to /redirect will redirect you to the result of getPath
<Route path="/redirect" element={<Navigate href={getPath} />} />;

Nested Routes

Solid Router supports nested routes, allowing for more complex routing structures.

// Nested routes can be written as a single route
<Route path="/about/me" component={User} />
// Or they can be defined separately
<Route path="/about">
<Route path="/me" component={User} />
</Route>

However, only the innermost Route nodes will be given a route. To ensure the parent has its own route, it must be specified separately.

// This will not work
<Route path="/about" component={About}>
<Route path="/me" component={User} />
</Route>
// This will work
<Route path="/about" component={About} />
<Route path="/about/me" component={Me} />
<Route path="/about">
<Route path="/" component={About} />
<Route path="/me" component={Me} />
</Route>

The <Outlet /> Component

Nested routes can also take advantage of the <Outlet /> component to render the nested content within a parent component. The <Outlet /> component serves as a placeholder that will be be replaced with the content of the matched child route.

import { Outlet } from "@solidjs/router";
function PageWrapper() {
return (
<div>
<h1>Solid is great!</h1>
<Outlet />
<A href="/">Back Home</A>
</div>
);
}
<Route path="/about" component={PageWrapper}>
<Route path="/" component={About} />
<Route path="/me" component={User} />
</Route>;

In this example, the <Outlet /> component is used to specify where the content of the child routes should be rendered within the PageWrapper component. When the /about route is matched, the PageWrapper component will be rendered, and the content of the child routes (About and Me) will be rendered inside the component.

This approach allows you to define a shared layout or container component for a group of nested routes and render the specific element based on the child routes. As long as there are the appropriate parent component <Outlet /> placeholders defined at each level, you can nest routes indefinitely using this approach.


Dynamic Routing

Dynamic routing lets an application handle URLs where some segments are not known ahead of time. You can create a dynamic route by using a colon (:) before a segment in the path prop of a Route component. This indicates that the segment can be any string.

import { Routes, Route } from "@solidjs/router";
import Home from "./pages/Home";
import About from "./pages/About";
import User from "./pages/User";
export default function App() {
return (
<>
<h1>My Site with many pages</h1>
<Routes>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/user/:id" component={User} />
<Route path="/wow" element={<div>You can even do this!</div>} />
</Routes>
</>
);
}

In the example above, the :id in the /user/:id path will be treated as a parameter. As long as the URL matches this pattern, the User component will be displayed.

Accessing parameters

In cases where you may need to access a dynamic route's parameters within your components, the useParams primitive is available. And 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>
);
};

This primtive can be especially useful with other Solid primitives, such as createResource and createSignal, which can create dynamic behaviours 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>
{data.loading ? (
<p>Loading...</p>
) : (
<div>
<p>Name: {data().name}</p>
<p>Email: {data().email}</p>
<p>Phone: {data().phone}</p>
</div>
)}
</div>
);
};

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

Validating routes

When working with dynamic routes, it is often necessary to validate the route parameters to ensure they meet certain criteria. This task can be performed through MatchFilters.

MatchFilters provides the option to define validation rules for each parameter in the route path. By including the matchFilters={filters} prop in the <Route> component, the parameters in the route path are automatically validated against their respective filters. If any validation fails, the route will not be matched.

//...
const filters: MatchFilters = {
parent: ["mom", "dad"], // allow enum values
id: /^\d+$/, // only allow numbers
withHtmlExtension: (v: string) => v.length > 5 && v.endsWith(".html"), // custom validation function
};
export default function App() {
// ...
return (
<>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route
path="/users/:id/:/parent/:withHtmlExtension"
component={User}
matchFilters={filters}
/>
</Routes>
</>
);
}

In this example, the filters object defines the validation rules for each parameter:

  • The parent parameter allows only the values "mom" or "dad" .
  • The id parameter allows only numeric values.
  • The withHtmlExtension parameter is validated using a custom function that checks for a length greater than 5 and a ".html" extension.

Creating optional routes

Routes can be specified as optional by adding ? to the end of a parameter name for more flexible routing patterns. This is especially useful when specific segments of the path may or may not be present.

<Route path="/stories/:id?" element={<Stories />} />

Specifying wildcard routes

Wildcard routes allow matching of any descendent within a given path. The wildcard token (*) can be used to represent any value in that segment of the path. Additionally, this value can be exposed as a parameter to the component.

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

In this example, the route path "foo/*" matches any path that begins with "foo". This includes paths like "foo/", "foo/a/", or "foo/a/b/c".

To expose the wildcard part of the path to the component as a parameter, you can do the following:

<Route path="foo/*any" element={<div>{useParams().any}</div>} />

Note: The wildcard token must be the last part of the path; foo/*any/bar would not create any routes.


Defining Multiple Paths

Routes can be defined with multiple paths using an array. This feature allows a Route component to remain mounted and avoid re-rendering when switching between multiple locations that match any of the predetermined paths. Using multiple paths can be useful when the same component and state are to be shared between different URLs that serve a similar purpose.

<Route path={["login", "register"]} component={Login} />

Creating data components

Solid Router also supports data components, which are components that fetch and manage data for a specific route. These components offer a declarative and reusable approach to handling data fetching within routes.

To create a data component, you can define a component that fetches and manages data, and then pass it as the data prop to the Route component. This function is then passed to the data prop of the Route component, initiating the data fetching in parallel with the page load. In doing this, the data is available as soon as possible, reducing any delays that people may experience.

import UserData from "./pages/users/[id].data.js";
const User = lazy(() => import("/pages/users/[id].js"));
// In the Route definition
<Route path="/users/:id" component={User} data={UserData} />;

Once the data component is set up, the fetched data can be accessed within the route component by using the useRouteData hook. This utility allows you to retrieve the results of the data function once the route is loaded.

// Inside the route component
const data = useRouteData();

This data function receives an object as its argument, containing valuable route information, including:

KeyTypeDescription
paramsobjectThe 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())
navigate(to: string, options?: NavigateOptions) => voidA function that you can call to navigate to a different route instead (corresponds to useNavigate())
dataunknownThe data returned by the parent's data function, if any. (Data will pass through any intermediate nesting.)

Organizing data functions

A possible way to use this is through exporting the data function of the corresponding route into a dedicated route.data.js or route.data.ts file. This imports the data functions independently, without loading any additional resources.

import { lazy } from "solid-js";
import { Route } from "@solidjs/router";
- import { fetchUser } ...
+ import UserData from "./pages/users/[id].data.js";
const User = lazy(() => import("/pages/users/[id].js"));
// In the Route definition
<Route path="/users/:id" component={User} data={UserData} />;

Using this pattern means there is no need to import the fetchUser function directly in the route component since it will be encapsulated within the [id].data.js file.


Hash-based routing mode

The default behaviour in Solid Router is to use location.pathname to find the route path. However, there is more flexibility built into the router which gives you the option to easily switch to hash mode through the source property in the <Router> component.

import { Router, hashIntegration } from "@solidjs/router";
<Router source={hashIntegration()}>
<App />
</Router>;

This adjustment tells your application to use the URL hash to manage routes, which can be useful for ensuring compatability with browsers that do not support the History API or for serving static files.


Config-based routing

Defining routes in Solid does not always require the use of JSX. An alternative approach is to use a configuration object to define your routes. This method is helpful when you aim to define your routes in a separate file, import them into your app, and then pass them to the useRoutes function.

import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router, useRoutes, A } from "@solidjs/router";
// Define your routes using a config object
const routes = [
{
path: "/users",
component: lazy(() => import("/pages/users.js")),
},
{
path: "/users/:id",
component: lazy(() => import("/pages/users/[id].js")),
children: [
{
path: "/",
component: lazy(() => import("/pages/users/[id]/index.js")),
},
{
path: "/settings",
component: lazy(() => import("/pages/users/[id]/settings.js")),
},
{
path: "/*all",
component: lazy(() => import("/pages/users/[id]/[...all].js")),
},
],
},
{
path: "/",
component: lazy(() => import("/pages/index.js")),
},
{
path: "/*all",
component: lazy(() => import("/pages/[...all].js")),
},
];
function App() {
// useRoutes function takes in the routes config
const Routes = useRoutes(routes);
return (
<>
<h1>Awesome Site</h1>
<A class="nav" href="/">
Home
</A>
<A class="nav" href="/users">
Users
</A>
<Routes />
</>
);
}
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById("app")
);

In this example, the routes config object contains the definitions for various routes and their respective components. The useRoutes function then takes the configuration object and generates the corresponding routes for the application.


Solid Route primitives

Solid Router offers a variety of primitives that interact with the Router and Route context. These primitives provide functionalities such as retrieving route parameters, navigating between routes, and accessing location information.

useParams

The useParams primitive retrieves a reactive object containing the current route path paramteres as defined in the Route.

import { useParams } from "@solidjs/router";
const params = useParams();
// Fetch user based on the id path parameter
const [user] = createResource(() => params.id, fetchUser);

useNavigate

useNavigate provides a method for imperative navigation. It returns a navigate function that accepts a path and an optional options object, allowing for customized navigation behavior.

const navigate = useNavigate();
if (unauthorized) {
navigate("/login", { replace: true });
}

The available options include:

  • resolve (boolean, default true): Resolve the path against the current route.
  • replace (boolean, default false): Replace the history entry.
  • scroll (boolean, default true): Scroll to top after navigation.
  • state (any, default undefined): Pass custom state to location.state.

useLocation

useLocation retrieves a reactive location object, which is useful for accessing properties like pathname, search, hash, state, etc.

import { useLocation } from "@solidjs/router";
const location = useLocation();
const pathname = createMemo(() => location.pathname);

useSearchParams

useSearchParams returns a destructured array that contains a reactive object to read the current location's query parameters and a method to update them. The setter method merges the entries into the current query string, and updates behave like a navigation.

const [searchParams, setSearchParams] = useSearchParams();
return (
<div>
<span>Page: {searchParams.page}</span>
<button onClick={() => setSearchParams({ page: searchParams.page + 1 })}>
Next Page
</button>
</div>
);

useIsRouting

useIsRouting retrieves a signal indicating whether the route is currently in a Transition. This is useful for displaying stale/pending states during concurrent rendering.

Copy code
const isRouting = useIsRouting();
return (
<div classList={{ "grey-out": isRouting() }}>
<MyAwesomeContent />
</div>
);

useRouteData

useRouteData retrieves the return value from the data function.

import { useRouteData } from "@solidjs/router";
const user = useRouteData();
return <h1>{user().name}</h1>;

useMatch

useMatch creates a Memo that returns match information if the current path matches the provided path, which is useful for determining if a given path matches the current route.

const match = useMatch(() => props.href);
return <div classList={{ active: Boolean(match()) }} />;

useRoutes

useRoutes is used to define routes via a config object instead of JSX. See Config-Based Routing for more details.

useBeforeLeave

useBeforeLeave takes a function that will be called prior to leaving a route, providing various parameters and methods to manage route changes.

useBeforeLeave((e: BeforeLeaveEventArgs) => {
if (form.isDirty && !e.defaultPrevented) {
e.preventDefault();
setTimeout(() => {
if (window.confirm("Discard unsaved changes - are you sure?")) {
e.retry(true);
}
}, 100);
}
});
Report an issue with this page