---
title: Router
description: Handles routing between different pages both server- and client-side.
---

---

<Info>
This component is part of the `jaspr_router` package. Make sure to add this to your dependencies before using
the component.
</Info>

You can use the `Router` component to add routing to your website. It takes a list of `Route`s to render based on the
current URL.

A simple use looks like this:

```dart title="lib/app.dart"
import 'package:jaspr_router/jaspr_router.dart';

import 'pages/home.dart';
import 'pages/about.dart' ;

class App extends StatelessComponent {
  @override
  Component build(BuildContext context) {
    return Router(routes: [
      Route(path: '/', builder: (context, state) => Home()),
      Route(path: '/about', builder: (context, state) => About()),
    ]);
  }
}
```

<Info>
`Router` has a similar API to `GoRouter` from the [`go_router`](https://pub.dev/packages/go_router) Flutter package.
If you know `go_router` a lot of the core concepts should feel familiar.
</Info>

## Defining Routes

To configure a `Route`, a `path` template and `builder` function must be provided:

```dart
Route(
  path: '/users/:userId',
  builder: (BuildContext context, RouteState state) {
    return const UserScreen();
  },
),
```

To navigate to this route, use `context.push()`. To learn more about how navigation works, see the [Routing](/concepts/routing) guide.

### Path Parameters

To specify a path parameter, prefix a path segment with a `:` character, followed by a unique name, for example, `:userId`.
You can access the value of the parameter by accessing it through the `RouteState` object provided to the `builder` function:

```dart
Route(
  path: '/users/:userId',
  builder: (context, state) => const UserScreen(id: state.params['userId']),
),
```

Similarly, to access a query string parameter (the part of URL after the `?`), use `RouteState`. For example,
a URL path such as `/users?filter=admins` can read the filter parameter:

```dart
Route(
  path: '/users',
  builder: (context, state) => const UsersScreen(filter: state.queryParams['filter']),
),
```

### Child routes

`Route`s can also have nested child `Route`s which will resolve their `path` relative to the parents `path`.

<Info>
Different to GoRouter in Flutter, nested `Route`s do **not** create a stack of pages.
Instead, only the most nested matched route is rendered.
</Info>

To define a nested child route, add it to the parent's `routes` list:

```dart
Route(
  path: '/users',
  builder: (context, state) => const UsersScreen(),
  routes: [
    Route(
      path: ':userId',
      builder: (context, state) => const UserScreen(id: state.params['userId']),
    ),
  ],
),
```

Navigating to `/users` will render the `UsersScreen()` and navigating to `/users/abc` will render the `UserScreen()`.

### Nested rendering

Some websites display content of pages in a subsection of the screen, for example, an app using a `<nav>` bar that
stays on-screen when navigating between pages.

By using `ShellRoute` and providing a `builder` you can render any layout around your `Route`s content:

```dart
ShellRoute(
  builder: (context, state, child) {
    return div([
      child,
    ]);
  },
  routes: <RouteBase>[
    Route(
      path: 'details',
      builder: (context, state) => const DetailsScreen(),
    ),
  ],
),
```

## Lazy Routes

For larger websites, we don't want to load everything together, but rather split our pages into smaller chunks of code.
`LazyRoute`s can be combined with Darts [deferred imports](https://dart.dev/language/libraries#lazily-loading-a-library)
to split the resulting javascript bundle into separate files for each `LazyRoute`:

```dart
import 'users_screen.dart' as users;

/* ... */

Route.lazy(
  path: '/users',
  load: users.loadLibrary,
  builder: (context, state) => users.UsersScreen(),
),
```

This will lazy load the needed `.js` file for the `/users` route only when navigating to it.

## Navigating

Depending on your routing setup you can either use normal browser-based navigation through links (the `<a>` tag) or
by using the `Router.of(context)` APIs.

<Warning>
The `Router.of(context)` is only available if you are using client-side routing, meaning the `Router` component is also
built on the client. If its only built on the server you have to use normal links instead.
</Warning>

### Navigating using Link component

The simplest way to add a navigation link to your site is by using the [`Link`](/api/components/link) component.
This is a drop-in replacement for the `<a>` tag and intelligently performs the correct navigation when clicked.

### Navigating to a URL

To push a new route, call `context.push()` with a URL:

```dart
context.push('/users/123');
```

This is shorthand for calling `Router.of(context).push('/users/123')` and will navigate to the target URL.

`push()` will add a new entry to the browser history and enables use of the browsers back button. Alternatively you
can use `context.replace()` to replace the current history entry with a new URL.

### Navigating back

You can use `context.back()` (or `Router.of(context).back()`) to trigger the browsers back
navigation.

### Navigating to a named Route

Instead of navigating to a route based on the URL, a `Route` can be given a unique name.
To configure a named route, use the `name` parameter:

```dart
Route(
   name: 'users',
   path: '/users/:userId',
   builder: /* ... */,
),
```

To navigate to a route using its name, call `pushNamed()` or `replaceNamed()`:

```dart
context.pushNamed('users', params: {'userId': '123'});
```

### Using extra

You can provide additional data along with navigation.

```dart
context.push('/123', extra: 'abc');
```

and retrieve the data from `RouteState`

```dart
final String extraString = RouteState.of(context).extra! as String;
```

The extra data will go through serialization when it is stored in the browser. You can only use primitive serializable
values like String, bool, int, double or Lists and Maps for the extra data.

## Redirection

Redirection changes the location to a new one based on application state. For example, redirection can be used to
show a sign-in page if the user is not logged in.

A redirect is a callback of the type `RouterRedirect`. To change incoming location based on some application state,
add a callback to either the `Router` or `Route` constructor:

```dart
redirect: (BuildContext context, RouteState state) {
  if (!AuthState.of(context).isSignedIn) {
    return '/signin';
  } else {
    return null;
  }
},
```

To display the intended route without redirecting, return `null` or the original route path.

### Top-level vs route-level redirection

There are two types of redirection:

- Top-level redirection: Defined on the `Router` constructor. Called before any navigation event.
- Route-level redirection: Defined on the `Route` constructor. Called when a navigation event is about to display the route.

### Redirecting to a named Route

You can use `context.namedLocation()` to look up the location for a named route. This is
useful for redirecting to a named route:

```dart
redirect: (BuildContext context, RouteState state) {
  if (AuthState.of(context).isSignedIn) {
    return context.namedLocation('signIn');
  } else {
    return null;
  }
},
```

### Considerations

You can specify a `redirectLimit` to configure the maximum number of redirects that are expected to occur in your site.
By default, this value is set to `5`. The `Router` will display the error screen if this redirect limit is exceeded.

## Preloading Routes

When using [Lazy Routes](/api/components/router#lazy-routes) you can use `Router.of(context).preload()` to preload this
route before actually navigating to it.

This is usually done as a performance improvement to estimate when the user is about to navigate to that route
and already load in the needed `.js` files before it is visited.

```dart
return button(
  events: {
    'mouseover': (event) {
      Router.of(context).preload('/about');
    },
    'click': (event) {
      Router.of(context).push('/about');
    },
  },
  [ /* ... */ ],
);
```

The above snippet will render a `<button>` that already preloads the target route when it is hovered,
resulting in a potentially faster navigation when the user eventually clicks it.

The [`Link`](/api/components/link) component has this behavior built in based on its `preload` parameter.
