A simple and efficient declarative routing library for Svelte 5 built with runes.
npm i svelte-tiny-router Here's a basic example demonstrating simple routes:
<!-- App.svelte --> <script> import { Router, Route } from 'svelte-tiny-router'; import Home from './Home.svelte'; import About from './About.svelte'; import User from './User.svelte'; </script> <Router> <!-- Exact match for home page --> <Route path="/" component={Home} /> <!-- Static route --> <Route path="/about" component={About} /> <!-- Dynamic route: "/user/123" will match and pass { id: "123" } as a prop --> <Route path="/user/:id" component={User} /> <!-- Fallback route: no "path" prop means it always matches (e.g. for a 404 page) --> <Route> <p>Page not found.</p> </Route> </Router><!-- App.svelte --> <script> import { Router, Route } from 'svelte-tiny-router'; import Home from './Home.svelte'; import About from './About.svelte'; import User from './User.svelte'; </script> <Router> <!-- Exact match for home page --> <Route path="/" component={Home} /> <!-- Static route --> <Route path="/about" component={About} /> <!-- Dynamic route: "/user/123" will match and pass { id: "123" } as a prop --> <Route path="/user/:id" component={User} /> <!-- Fallback route: no "path" prop means it always matches (e.g. for a 404 page) --> <Route> <p>Page not found.</p> </Route> </Router><!-- SomeComponent.svelte --> <script> import { useTinyRouter } from 'svelte-tiny-router'; const router = useTinyRouter(); function goToAbout() { router.navigate('/about'); // Use router.navigate } function goToUser(id) { router.navigate(`/user/${id}`); } function replaceWithHome() { router.navigate('/', { replace: true }); // Replace current history entry } function navigateWithQuery() { router.navigate('/search?q=svelte&category=router'); // Navigate with query string } </script> <button on:click={goToAbout}>Go to About Page</button> <button on:click={() => goToUser(123)}>Go to User 123</button> <button on:click={replaceWithHome}>Replace with Home</button> <button on:click={navigateWithQuery}>Search</button><!-- SomeComponent.svelte --> <script> import { useTinyRouter } from 'svelte-tiny-router'; const router = useTinyRouter(); // Access the entire query object console.log("Current query:", router.query); // Check if the "foo" query parameter exists (i.e /myroute?foo=bar) and log it if (router.hasQueryParam('foo')) { console.log("Value of foo:", router.getQueryParam('foo')); router.removeQueryParams(["foo"]); } // Get a specific query parameter const searchTerm = router.getQueryParam('q'); console.log("Search term:", searchTerm); </script>You can define navigation guards using the beforeEach prop on the <Router> component. These guards are functions that are executed before each navigation. They can be used to cancel navigation, redirect to a different route, or perform asynchronous tasks like authentication checks.
<!-- App.svelte (with navigation guards) --> <script> import { Router, Route } from 'svelte-tiny-router'; import Home from './Home.svelte'; import AdminDashboard from './AdminDashboard.svelte'; import Login from './Login.svelte'; // Example authentication check function function isAuthenticated() { // Replace with your actual auth logic (e.g., check token in localStorage) return localStorage.getItem('authToken') !== null; } // Define navigation guards const authGuard = async ({ to, from, next }) => { console.log('[authGuard] Navigating from:', from?.path, 'to:', to.path, 'Query:', to.query); if (to.path.startsWith('/admin') && !isAuthenticated()) { console.log('Authentication required for admin route, redirecting to login.'); // Redirect to login page, replacing the current history entry next({ path: '/login', replace: true }); } else { // Continue navigation next(); } }; const loggingGuard = ({ to, from, next }) => { console.log('[LOG] Navigation attempt:', from?.path || 'N/A', '->', to.path, 'Query:', to.query); next(); // Always call next() to proceed }; const myGuards = [loggingGuard, authGuard]; // Guards are executed in order </script> <Router beforeEach={myGuards}> <Route path="/" component={Home} /> <Route path="/admin" component={AdminDashboard} /> <Route path="/login" component={Login} /> <Route> <p>Page not found.</p> </Route> </Router>A navigation guard function receives an object with the following properties:
to: An object representing the target route ({ path: string, params: Record<string, string>, query: Record<string, string> }).from: An object representing the current route, ornullif this is the initial navigation ({ path: string, params: Record<string, string>, query: Record<string, string> } | null).next: A function that must be called to resolve the hook.next(): Proceed to the next hook in the pipeline, or to the navigation if no more hooks are left.next(false): Cancel the current navigation.next('/path')ornext({ path: '/path', replace: true }): Redirect to a different location. The current navigation is cancelled, and a new one is initiated.
The library supports nested routing, particularly useful with wildcard routes (/*). When a wildcard route matches, it automatically sets up a NestedRouterProvider context for its children <Route> components. These children routes then match paths relative to the parent wildcard's matched segment.
For example, with a structure like:
<Router> <Route path="/app/*"> <Route path="/" component={AppHome} /> {/* Matches /app */} <Route path="/settings" component={AppSettings} /> {/* Matches /app/settings */} </Route> </Router>Navigating to /app/settings will first match the /app/* route. The NestedRouterProvider within /app/* then makes /settings match relative to /app.
Alternatively, you can render a separate component that contains its own <Router> instance for nested routes. This component will receive the matched parameters from the parent route.
<!-- App.svelte --> <script> import { Router, Route } from 'svelte-tiny-router'; import Home from './Home.svelte'; import About from './About.svelte'; import User from './User.svelte'; import DashboardRouter from './DashboardRouter.svelte'; // Component containing nested routes </script> <Router> <Route path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/user/:id" component={User} /> <!-- Wildcard route rendering a component that contains a nested router --> <Route path="/dashboard/*" component={DashboardRouter} /> <Route> <p>Page not found.</p> </Route> </Router><!-- DashboardRouter.svelte --> <script> import { Router, Route } from 'svelte-tiny-router'; import DashboardHome from './DashboardHome.svelte'; import Profile from './Profile.svelte'; import Settings from './Settings.svelte'; // This component receives params from the parent route if any were captured // let { paramFromParent } = $props(); // Example if /dashboard/:param/* was used </script> <!-- This Router instance handles routes relative to the parent's matched path (/dashboard) --> <Router> <Route path="/" component={DashboardHome} /> {/* Matches /dashboard */} <Route path="/profile" component={Profile} /> {/* Matches /dashboard/profile */} <Route path="/settings" component={Settings} /> {/* Matches /dashboard/settings */} <!-- Nested fallback for /dashboard/* --> <Route> <p>Dashboard page not found.</p> </Route> </Router>This library now includes comprehensive TypeScript definitions, providing improved type checking and autocompletion for users of TypeScript or JavaScript with JSDoc. Key types include RouterContext, RouteInfo, NavigationGuard, and NextFunction.