Skip to main content
Jaspr provides client-side routing through the jaspr_router package, enabling navigation between pages without full page reloads. The router works seamlessly on both client and server, supporting SSR and hydration.

Installation

Add jaspr_router to your pubspec.yaml:
dependencies:
  jaspr: ^0.x.x
  jaspr_router: ^0.x.x

Basic Setup

Define your routes using the Router component:
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';

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

Route Definition

Each route requires a path and a builder function:
Route(
  path: '/users/:id',           // Path with parameter
  title: 'User Profile',         // Page title
  builder: (context, state) {    // Builder function
    return UserProfile(
      userId: state.pathParameters['id']!,
    );
  },
)
Route properties:
  • path - URL path (supports parameters)
  • title - Page title (sets <title> tag)
  • builder - Function that returns the component to render
  • routes - Nested child routes
  • redirect - Redirect to another path
The Link component provides client-side navigation without page reloads:
import 'package:jaspr_router/jaspr_router.dart';

Link(
  to: '/about',
  [text('Go to About')],
)
Link properties:
Link(
  to: '/users/123',              // Target URL
  replace: false,                 // Replace instead of push
  extra: {'source': 'menu'},     // Extra data to pass
  preload: true,                  // Preload on hover
  target: Target.self,            // Link target
  classes: 'nav-link',           // CSS classes
  styles: Styles(...),            // Inline styles
  child: text('View User'),       // Single child
  // or
  children: [/* multiple */],     // Multiple children
)
Links automatically preload their target route when hovered (if preload: true), improving perceived performance.

Programmatic Navigation

Navigate programmatically using the Router methods:
class MyButton extends StatelessComponent {
  @override
  Component build(BuildContext context) {
    return button(
      events: {
        'click': (_) {
          // Push a new route
          Router.of(context).push('/about');
          
          // Or replace current route
          Router.of(context).replace('/login');
          
          // Go back
          Router.of(context).back();
        },
      },
      [text('Navigate')],
    );
  }
}
Router methods:
  • push(path) - Navigate to a new route
  • replace(path) - Replace the current route
  • back() - Go back in history
  • preload(path) - Preload a route

Path Parameters

Define dynamic segments in your routes:
Route(
  path: '/users/:userId/posts/:postId',
  builder: (context, state) {
    final userId = state.pathParameters['userId']!;
    final postId = state.pathParameters['postId']!;
    
    return PostDetail(
      userId: userId,
      postId: postId,
    );
  },
)
Access parameters from state.pathParameters.

Query Parameters

Access query parameters from the route state:
Route(
  path: '/search',
  builder: (context, state) {
    final query = state.uri.queryParameters['q'] ?? '';
    final page = state.uri.queryParameters['page'] ?? '1';
    
    return SearchResults(
      query: query,
      page: int.parse(page),
    );
  },
)
Navigate with query parameters:
Router.of(context).push('/search?q=jaspr&page=2')

Nested Routes

Create nested routing structures:
Route(
  path: '/dashboard',
  builder: (context, state) => DashboardLayout(),
  routes: [
    Route(
      path: 'overview',
      builder: (context, state) => OverviewPage(),
    ),
    Route(
      path: 'settings',
      builder: (context, state) => SettingsPage(),
    ),
    Route(
      path: 'profile',
      builder: (context, state) => ProfilePage(),
    ),
  ],
)
URLs will be:
  • /dashboard/overview
  • /dashboard/settings
  • /dashboard/profile

Redirects

Redirect from one path to another:
Route(
  path: '/old-path',
  redirect: (context, state) => '/new-path',
)
Conditional redirects:
Route(
  path: '/admin',
  redirect: (context, state) {
    final isLoggedIn = checkAuth();
    return isLoggedIn ? null : '/login';
  },
  builder: (context, state) => AdminPanel(),
)
Return null to continue to the route, or return a path to redirect.

404 Pages

Handle unknown routes with a catch-all route:
Router(
  routes: [
    // ... your routes
    
    // Catch-all route (must be last)
    Route(
      path: '/:_(.*)',
      builder: (context, state) => NotFoundPage(),
    ),
  ],
)

Passing Data Between Routes

Pass extra data when navigating:
// Navigate with data
Router.of(context).push(
  '/user/profile',
  extra: UserData(id: '123', name: 'Alice'),
);

// Access in target route
Route(
  path: '/user/profile',
  builder: (context, state) {
    final userData = state.extra as UserData?;
    return UserProfile(data: userData);
  },
)

Router State

Access the current router state:
final state = RouterState.of(context);

// Current location
final uri = state.uri;           // Uri object
final path = state.uri.path;     // Current path

// Path parameters
final userId = state.pathParameters['userId'];

// Query parameters
final search = state.uri.queryParameters['q'];

// Extra data
final extra = state.extra;

Complete Example

A full routing setup:
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';

class App extends StatelessComponent {
  @override
  Component build(BuildContext context) {
    return Router(
      routes: [
        Route(
          path: '/',
          title: 'Home',
          builder: (context, state) => HomePage(),
        ),
        Route(
          path: '/blog',
          builder: (context, state) => BlogLayout(),
          routes: [
            Route(
              path: '',
              title: 'Blog',
              builder: (context, state) => BlogList(),
            ),
            Route(
              path: ':slug',
              builder: (context, state) {
                final slug = state.pathParameters['slug']!;
                return BlogPost(slug: slug);
              },
            ),
          ],
        ),
        Route(
          path: '/about',
          title: 'About',
          builder: (context, state) => AboutPage(),
        ),
        Route(
          path: '/:_(.*)',
          title: '404',
          builder: (context, state) => NotFoundPage(),
        ),
      ],
    );
  }
}

class HomePage extends StatelessComponent {
  @override
  Component build(BuildContext context) {
    return div([
      h1([text('Welcome')]),
      nav([
        Link(to: '/', [text('Home')]),
        Link(to: '/blog', [text('Blog')]),
        Link(to: '/about', [text('About')]),
      ]),
    ]);
  }
}

class BlogPost extends StatelessComponent {
  const BlogPost({required this.slug, super.key});
  
  final String slug;

  @override
  Component build(BuildContext context) {
    return div([
      Link(
        to: '/blog',
        [text('← Back to Blog')],
      ),
      h1([text('Post: $slug')]),
      // ... post content
    ]);
  }
}

Best Practices

Make URLs meaningful and SEO-friendly:
// Good
Route(path: '/blog/how-to-use-jaspr')

// Avoid
Route(path: '/p/123')
Always set the title property for better SEO and user experience:
Route(
  path: '/about',
  title: 'About Us - My App',
  builder: (context, state) => AboutPage(),
)
Show loading indicators during navigation:
Route(
  path: '/users/:id',
  builder: (context, state) => UserLoader(
    userId: state.pathParameters['id']!,
  ),
)
Preload routes that users are likely to visit:
@override
void initState() {
  super.initState();
  // Preload important routes
  Router.of(context).preload('/dashboard');
}

Components

Learn about component basics

Data Fetching

Fetch data for your routes

SEO

Optimize routes for search engines

Build docs developers (and LLMs) love