discover · compare · collections · requirements · contribute · feed · watching · stats · help with your tokens
hanielu/svelte-router · source
Snapshot 0 · 2026-11-04T12:47:28Z · TypeScript · MIT
Content fingerprint
c4dfb8771407407b8dd429a71e5f69b04a677eafd53d25b648d31f6327a44ac3GitHub description at capture
Declarative routing for svelte
README at capture
# @hvniel/svelte-router
A powerful Svelte 5 port of React Router - the modern, lightweight, performant, and accessible routing library for single-page applications.
> **🚧 Work in Progress**: This project is actively being developed. Issues, feature requests, and pull requests are welcome! Help us make this the best react-router port for Svelte 5.
## Quick start
Version `0.1.0` requires Svelte `5.57.0` or newer within Svelte 5. Upgrading from
`0.0.6` requires migrating value hooks to readonly `.current` handles; see the
[migration contract](#svelte-5-reactivity-integration) below.
Install it:
```bash
npm i @hvniel/svelte-router
# or
yarn add @hvniel/svelte-router
# or
pnpm add @hvniel/svelte-router
```
## Overview
This library is building toward React Router's declarative- and data-mode behaviour while adapting the API to Svelte 5. It is not yet a complete port; use the [React Router parity plan](./react-router-parity.md) to see what is tested, partial, or missing.
The [upstream test audit](./docs/test-parity.md) records the pinned React Router revision, completed counterpart suites, and explicit Svelte adaptations. A passing local suite is not a claim that the entire upstream suite has been ported.
The router supports both **data routing** and **declarative routing** modes, giving you flexibility in how you structure your application.
> **Note on Framework Mode**: This port focuses exclusively on data and declarative routing modes for client-side applications. The "framework" mode (equivalent to Remix) is not planned for this library since [SvelteKit](https://kit.svelte.dev/) already provides an exceptional full-stack framework experience for Svelte applications. If you need server-side rendering, file-based routing, or full-stack capabilities, we recommend using SvelteKit instead.
## Table of Contents
- [Quick start](#quick-start)
- [Routing Modes](#routing-modes)
- [Data Routing Mode](#data-routing-mode)
- [Declarative Routing Mode](#declarative-routing-mode)
- [Key Features](#key-features)
- [Examples](#examples)
- [Key Differences from React Router](#key-differences-from-react-router)
- [Core Concepts](#core-concepts)
- [API Reference](#api-reference)
- [React Router parity plan](./react-router-parity.md)
- [Contributing](#contributing)
## Routing Modes
### Data Routing Mode
Data routing registers route objects with an engine that owns navigation,
loaders/actions, revalidation, and blockers. This example is a client-side SPA
entry; `Root.svelte` renders an `Outlet`, and `Home.svelte` is application UI:
```svelte
<script lang="ts">
import { onDestroy } from 'svelte';
import { createBrowserRouter, RouterProvider } from '@hvniel/svelte-router';
import Root from './Root.svelte';
import Home from './Home.svelte';
import User, { loader as userLoader } from './User.svelte';
const router = createBrowserRouter([
{
path: "/",
Component: Root,
children: [
{
index: true,
Component: Home,
},
{
path: "users/:id",
Component: User,
loader: userLoader,
},
],
},
]);
// This entry creates and owns the engine, not merely its subscription.
onDestroy(() => router.dispose());
</script>
<RouterProvider {router} />
```
The `User.svelte` component and loader are shown below. Exporting a loader from a
component does not register it automatically; the `loader` route property does.
### Declarative Routing Mode
Declarative routing uses familiar component-based syntax for defining routes:
```svelte
<script>
import { BrowserRouter, Routes, Route } from '@hvniel/svelte-router';
import Home from './Home.svelte';
import About from './About.svelte';
import User from './User.svelte';
</script>
<BrowserRouter>
<Routes>
<Route path="/" Component={Home} />
<Route path="/about" Component={About} />
<Route path="/users/:id" Component={User} />
</Routes>
</BrowserRouter>
```
Both modes are useful. A descendant `Routes` table can also render under a data
router's splat route, but its definitions do not become engine-managed
loader/action routes. In `0.0.6`, `useBlocker` requires a data router, even if the
application owns its data fetching. See [router architecture](docs/architecture.md).
## Key Features
- **Multiple Router Types**: BrowserRouter, HashRouter, MemoryRouter, and StaticRouter
- **Data Loading**: Built-in loader and action functions for data fetching
- **Nested Routing**: Nested routes and layouts through `Outlet`
- **Native HTML**: Links and forms enhance ordinary elements; a client-only app still requires JavaScript
- **TypeScript**: Public route/data/controller types checked against the emitted package with `skipLibCheck: false`
- **Svelte 5**: Components, snippets, runes, and live readonly `.current` value handles
- **Non-navigation operations**: Keyed fetchers with engine-owned cancellation and revalidation
- **Scroll restoration**: Window or one active element, with an application-content readiness gate
See [client data, error recovery, and scroll](docs/client-data.md) for the new
fetcher and scroll APIs, working examples, and their limits. These additions and
the `.current` change are included in `0.1.0`, not `0.0.6`.
## Examples
### Basic Navigation
```svelte
<script>
import { Link, useNavigate } from '@hvniel/svelte-router';
const navigate = useNavigate();
</script>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<button onclick={() => navigate('/dashboard')}>
Go to Dashboard
</button>
</nav>
```
### Data Loading with Loaders
This is the component registered by the data-router example above. The sample
assumes `/api/users/:id` returns a user object with a string `name`:
```svelte
<!-- User.svelte -->
<script module lang="ts">
import type { LoaderFunctionArgs } from '@hvniel/svelte-router';
export async function loader({ params, request }: LoaderFunctionArgs) {
const response = await fetch(`/api/users/${params.id}`, {
signal: request.signal,
});
if (!response.ok) throw response;
const user: unknown = await response.json();
if (!user || typeof user !== 'object' || !('name' in user) || typeof user.name !== 'string') {
throw new Error('Expected a user with a string name');
}
return { user: { name: user.name } };
}
</script>
<script lang="ts">
import { useLoaderData, useParams } from '@hvniel/svelte-router';
const data = useLoaderData<typeof loader>();
const params = useParams<'id'>();
</script>
<h1>User: {data.current?.user.name ?? 'Loading...'}</h1>
<p>ID: {params.current.id}</p>
```
### Forms and Actions
Render this inside `RouterProvider`, with an `action` registered on the `/login`
data route. The form markup alone does not create the action or a server endpoint:
```svelte
<script>
import { Form } from '@hvniel/svelte-router';
</script>
<Form method="post" action="/login">
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Login</button>
</Form>
```
### Nested Routes and Layouts
```svelte
<!-- Layout.svelte -->
<script>
import { Outlet } from '@hvniel/svelte-router';
</script>
<div class="layout">
<header>My App</header>
<main>
<Outlet />
</main>
<footer>© 2024</footer>
</div>
```
## Key Differences from React Router
The main differences lie in how reactive values are handled and how route elements are defined, since Svelte uses a different component model than React:
### Component vs Element
In React Router, you can use either `Component` or `element` props for route definitions. In this Svelte port:
- **`Component`**: Works the same as React Router - pass a Svelte component
- **`element`**: Instead of JSX elements, this accepts Svelte **snippets**
```svelte
<script>
import { Route } from '@hvniel/svelte-router';
import UserProfile from './UserProfile.svelte';
</script>
<!-- Alternative definitions inside Routes; choose one. -->
<!-- Using Component (same as React Router) -->
<Route path="/user/:id" Component={UserProfile} />
<!-- Using element with snippets (Svelte-specific) -->
<Route path="/user/:id">
{#snippet element()}
<UserProfile />
{/snippet}
</Route>
```
For data routing, put `Component: UserProfile` on the route object, as in the
entry example. Its alternative `element` property accepts a snippet reference,
not JSX or JavaScript written directly into component markup.
### Svelte 5 Reactivity Integration
In both routing modes, value hooks return stable readonly `.current` handles.
Initialize them once; reads stay live without remounting the component:
```svelte
<script>
import { useLocation, useParams } from '@hvniel/svelte-router';
const location = useLocation();
const params = useParams();
// Use $effect for side effects
$effect(() => {
console.log('Location changed:', location.current.pathname);
});
</script>
<p>{location.current.pathname}: {params.current.id ?? 'no user selected'}</p>
```
Version `0.1.0` introduces a **breaking change from 0.0.6**. Replace
`$derived(useLocation())` with `useLocation()` and read `location.current`.
Use getters for changing inputs: `useHref(() => to, () => ({ relative }))`.
`$derived` remains useful for calculations from the current value. See the
[reactivity guide](docs/svelte-reactivity.md) for the full migration contract.
### Router Configuration
Declarative mode supplies browser, hash, and memory wrappers (fragments shown
below). Data mode uses the corresponding `create*Router` functions and
`RouterProvider` instead:
```svelte
<script>
import { BrowserRouter, HashRouter, MemoryRouter } from '@hvniel/svelte-router';
</script>
<!-- Browser History (clean URLs) -->
<BrowserRouter>
<!-- routes -->
</BrowserRouter>
<!-- Hash History (hash-based URLs) -->
<HashRouter>
<!-- routes -->
</HashRouter>
<!-- Memory History (for testing/SSR) -->
<MemoryRouter initialEntries={["/"]}>
<!-- routes -->
</MemoryRouter>
```
## Core Concepts
The library shares these core routing concepts with React Router:
- **Routers**: BrowserRouter, HashRouter, MemoryRouter, StaticRouter
- **Route Matching**: Path patterns, dynamic segments, nested routes
- **Navigation**: Link components, programmatic navigation
- **Data Loading**: Loaders, actions, and error boundaries
- **Route Protection**: Route guards and authentication flows
Use the [original React Router documentation][react-router-docs] for conceptual
background, and this package's [parity plan](react-router-parity.md) for actual
support and limitations. Client-side route protection is not server authorization.
## API Reference
### Data Routing Components
| Component | Description |
| ---------------- | --------------------------------------------- |
| `RouterProvider` | Provides router context for data routing mode |
| `Outlet` | Renders child route components |
| `ScrollRestoration` | Restores window/element scroll in a data-router layout |
### Declarative Routing Components
| Component | Description |
| --------------- | ------------------------------------ |
| `BrowserRouter` | Router using browser history API |
| `HashRouter` | Router using URL hash for navigation |
| `MemoryRouter` | Router storing history in memory |
| `StaticRouter` | Router for server-side rendering |
| `Routes` | Container for route definitions |
| `Route` | Individual route definition |
### Navigation Components
| Component | Description |
| --------- | -------------------------------------- |
| `Link` | Navigation link component |
| `NavLink` | Link with active/pending state and snippet content |
| `Form` | Enhanced form with routing integration |
### Data Router Creation Functions
| Function | Description |
| --------------------------------------- | ------------------------------------------ |
| `createBrowserRouter(routes, options?)` | Creates a data router with browser history |
| `createHashRouter(routes, options?)` | Creates a data router with hash history |
| `createMemoryRouter(routes, options?)` | Creates a data router with memory history |
### Navigation Hooks
| Hook | Description |
| ------------------- | ---------------------------------------------- |
| `useNavigate()` | Returns a function for programmatic navigation |
| `useLocation()` | Read the location through `.current` |
| `useParams()` | Read current route params through `.current` |
| `useSearchParams()` | Returns `[paramsHandle, setParams]`; read `paramsHandle.current` |
| `useHref(to)` | Returns a live href handle; accepts a value or getter |
### Data Hooks (Data Routing Mode)
| Hook | Description |
| -------------------- | ----------------------------------------------------- |
| `useLoaderData<T>()` | Returns data from the route loader |
| `useActionData<T>()` | Returns data from the route action |
| `useRouteError()` | Returns the current route error |
| `useNavigation()` | Returns navigation state information |
| `useSubmit()` | Returns a function to submit forms programmatically |
| `useRevalidator()` | Returns a `revalidate()` command and live readonly `state` |
| `useFetcher<T>()` | Stable `.current`, `key`, `load` and `submit` controller |
| `useFetchers()` | Readonly handle listing active keyed operations |
| `useScrollRestoration(options?)` | Mount-owned scroll lifecycle; no return value |
Value readers such as `useNavigation` and `useLoaderData` return readonly `.current` handles. Loader and
action data may be `undefined` before it exists. `useSubmit` remains a command.
### Route Definition Types
| Interface | Description |
| ---------------- | ------------------------------------------- |
| `LoaderFunction` | Type for route loader functions |
| `ActionFunction` | Type for route action functions |
`RouteObject`, `DataRouteObject`, `NavigateOptions`, `SerializeFrom`,
`FetcherController`, and scroll option/prop types are public. `SerializeFrom`
describes client results, not generated framework types or server serialization.
`ErrorBoundary` is a route property accepting a Svelte component, not an exported interface.
## Contributing
Start with [AGENTS.md](AGENTS.md), then the relevant guide:
- [Architecture and ownership](docs/architecture.md)
- [Svelte reactivity and API design](docs/svelte-reactivity.md)
- [Development, compiler probes, and verification](docs/frontend-development.md)
These guides define modern Svelte conventions and how to correct outdated
assumptions. The [parity plan](react-router-parity.md) tracks capability evidence;
the historical `todos.md` checklist is not a support contract.
## License
MIT © [Haniel Ubogu](https://github.com/HanielU)
[react-router-docs]: https://reactrouter.com/