A simple, flexible, and lightweight SPA router specifically designed for Svelte 5 with runes support.
:id
), optional parameters (:id?
), and wildcards (/*
)goto()
function and reactive storesnpm install svelte5-spa-router
# or
yarn add svelte5-spa-router
# or
pnpm add svelte5-spa-router
<!-- App.svelte -->
<script>
import Router from 'svelte5-spa-router/Router.svelte';
import Link from 'svelte5-spa-router/Link.svelte';
import { router } from 'svelte5-spa-router';
import Home from './routes/Home.svelte';
import About from './routes/About.svelte';
import UserProfile from './routes/UserProfile.svelte';
import NotFound from './routes/NotFound.svelte';
// Setup routes
router.addRoute('/', Home);
router.addRoute('/about', About);
router.addRoute('/user/:id', UserProfile);
router.setFallback(NotFound);
</script>
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href="/user/123">User Profile</Link>
</nav>
<Router />
// Components
import Router from 'svelte5-spa-router/Router.svelte';
import Link from 'svelte5-spa-router/Link.svelte';
// Router instance and functions
import {
router, // Main router instance
goto, // Programmatic navigation
getQueryParam, // Get query parameter
updateQueryParams // Update query params
} from 'svelte5-spa-router';
// Reactive stores
import {
currentRoute, // Current route info
routeParams, // Route parameters
queryParams, // Query parameters
hashFragment // Hash fragment
} from 'svelte5-spa-router';
import { router } from 'svelte5-spa-router';
import Home from './components/Home.svelte';
import About from './components/About.svelte';
import UserProfile from './components/UserProfile.svelte';
import BlogPost from './components/BlogPost.svelte';
import Search from './components/Search.svelte';
import AdminPanel from './components/AdminPanel.svelte';
import NotFound from './components/NotFound.svelte';
// Static Routes
router.addRoute('/', Home);
router.addRoute('/about', About);
// Dynamic Routes with Parameters
router.addRoute('/user/:id', UserProfile);
router.addRoute('/blog/:slug', BlogPost);
router.addRoute('/category/:type/item/:id', ItemDetail);
// Optional Parameters
router.addRoute('/search/:query?', Search);
// Wildcard Routes
router.addRoute('/admin/*', AdminPanel);
// Set fallback for 404
router.setFallback(NotFound);
<script>
import Link from 'svelte5-spa-router/Link.svelte';
</script>
<Link href="/about">About Us</Link>
<Link href="/user/123">User Profile</Link>
<Link href="/search?q=svelte">Search Svelte</Link>
<Link href="/docs#introduction">Documentation</Link>
import { goto } from 'svelte5-spa-router';
// Simple navigation
goto('/about');
// With query parameters
goto('/search', { q: 'svelte', page: '1' });
// With hash fragment
goto('/docs', {}, 'introduction');
// Combined
goto('/search', { q: 'svelte', category: 'frontend' }, 'results');
<script>
import { routeParams } from 'svelte5-spa-router';
// Access route parameters reactively
const userId = $derived($routeParams.id);
const allParams = $derived($routeParams);
</script>
<h1>User Profile: {userId}</h1>
<p>All params: {JSON.stringify(allParams)}</p>
<script>
import { queryParams, getQueryParam, updateQueryParams } from 'svelte5-spa-router';
// Get single parameter with default
const searchQuery = $derived(getQueryParam('q', ''));
// Get all parameters
const allQueryParams = $derived($queryParams);
// Update query parameters
function updateSearch(newQuery) {
updateQueryParams({ q: newQuery });
}
// Replace all query parameters
function setFilters() {
updateQueryParams({ category: 'tech', sort: 'date' }, true);
}
</script>
<input bind:value={searchQuery} onchange={() => updateSearch(searchQuery)} />
<p>Current query: {searchQuery}</p>
<p>All params: {JSON.stringify(allQueryParams)}</p>
<script>
import { hashFragment } from 'svelte5-spa-router';
const currentHash = $derived($hashFragment);
</script>
<p>Current hash: {currentHash}</p>
<Router>
Main router component that renders the current route based on the URL.
Usage:
<script>
import Router from 'svelte5-spa-router/Router.svelte';
import { router } from 'svelte5-spa-router';
// Setup your routes first
router.addRoute('/', HomeComponent);
router.setFallback(NotFoundComponent);
</script>
<Router />
<Link>
Link component with automatic active state handling and proper navigation.
Props:
href
(string): Target URLclass
(string, optional): CSS class for the linkUsage:
<script>
import Link from 'svelte5-spa-router/Link.svelte';
</script>
<Link href="/about" class="nav-link">About</Link>
goto(path, queryParams?, hash?)
Navigate programmatically.
path
: Target pathqueryParams
: Object of query parametershash
: Hash fragmentgetQueryParam(key, defaultValue?)
Get a specific query parameter.
updateQueryParams(params, replace?)
Update URL query parameters without navigation.
All stores are reactive and can be used with $
syntax:
currentRoute
: Current route information { path, component, params }
routeParams
: Parameters from current routequeryParams
: Current query parameters objecthashFragment
: Current hash fragment string<script>
import Link from 'svelte5-spa-router/Link.svelte';
</script>
<Link href="/" class="nav-link">Home</Link>
<style>
:global(.nav-link) {
text-decoration: none;
color: #007acc;
padding: 0.5rem 1rem;
border-radius: 4px;
transition: background-color 0.2s;
}
:global(.nav-link:hover) {
background-color: #f0f0f0;
}
</style>
<!-- App.svelte -->
<script>
import Router from 'svelte5-spa-router/Router.svelte';
import { currentRoute, goto, router } from 'svelte5-spa-router';
const protectedRoutes = ['/dashboard', '/profile'];
// Route guard
$effect(() => {
if ($currentRoute && protectedRoutes.includes($currentRoute.path)) {
if (!isAuthenticated()) {
goto('/login');
}
}
});
function isAuthenticated() {
// Your authentication logic
return localStorage.getItem('token') !== null;
}
</script>
<Router />
// vitest example
import { render, fireEvent } from '@testing-library/svelte';
import { goto, router } from 'svelte5-spa-router';
import Home from '../components/Home.svelte';
import About from '../components/About.svelte';
import App from '../App.svelte';
beforeEach(() => {
// Setup routes for testing
router.clearRoutes();
router.addRoute('/', Home);
router.addRoute('/about', About);
});
test('should navigate to about page', async () => {
const { getByText } = render(App);
await fireEvent.click(getByText('About'));
expect(getByText('About Page')).toBeInTheDocument();
});
test('should handle dynamic routes', async () => {
router.addRoute('/user/:id', UserProfile);
goto('/user/123');
const { getByText } = render(App);
expect(getByText('User ID: 123')).toBeInTheDocument();
});
- import router from 'svelte-spa-router'
+ import Router from 'svelte5-spa-router/Router.svelte'
+ import { router } from 'svelte5-spa-router'
- <Router {routes} />
+ // Setup routes first
+ router.addRoute('/', HomeComponent);
+ router.setFallback(NotFoundComponent);
+ <Router />
- import { router } from '@roxi/routify'
+ import { goto } from 'svelte5-spa-router'
- $router.goto('/path')
+ goto('/path')
This router works perfectly with SvelteKit for client-side routing:
<!-- src/app.html or main component -->
<script>
import Router from 'svelte5-spa-router/Router.svelte';
import { router } from 'svelte5-spa-router';
import Home from './routes/Home.svelte';
import About from './routes/About.svelte';
import NotFound from './routes/NotFound.svelte';
// Setup routes
router.addRoute('/', Home);
router.addRoute('/about', About);
router.setFallback(NotFound);
</script>
<Router />
Make sure you're importing from the correct path and the router handles SSR automatically.
Check your route patterns and ensure they match the URL structure exactly.
Ensure you're using the Link
component and not regular <a>
tags.
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/AmazingFeature
)git commit -m 'Add some AmazingFeature'
)git push origin feature/AmazingFeature
)This project is licensed under the MIT License - see the LICENSE file for details.
Made with โค๏ธ for the Svelte community
Report Bug โข Request Feature โข Documentation
Once you've created a project and installed dependencies with npm install
(or pnpm install
or yarn
), start a development server:
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
Everything inside src/lib
is part of your library, everything inside src/routes
can be used as a showcase or preview app.
To build your library:
npm run package
To create a production version of your showcase app:
npm run build
You can preview the production build with npm run preview
.
To deploy your app, you may need to install an adapter for your target environment.
Go into the package.json
and give your package the desired name through the "name"
option. Also consider adding a "license"
field and point it to a LICENSE
file which you can create from a template (one popular option is the MIT license).
To publish your library to npm:
npm publish