An SPA router for Svelte that allows you to divide & conquer your app with nested routers, snippets, and more.
Live demo: https://svelte5-router.vercel.app
/foo/(.*?)/bar
) and/or named parameters together 🔥.component: async () => import("./my-component.svelte")
.npm install @mateothegreat/svelte5-router
All you need to do is define your routes and then use the Router
component with the routes
array.
To make a link, use the route
directive with the href
attribute such as <a use:route href="/foo">foo</a>
.
In some cases you may not be able to use the base path of your app as the root path. For example, if you are using a nested router, you may want to use a base path of /foo
instead of /
.
Simply pass the basePath
prop to the Router
component and it will handle the rest:
No need to update your routes either, it will support both
/mybasepath/foo
and/foo
just fine.
<Router basePath="/mybasepath" {routes} />
goto(path: string)
Navigates to the given path.
query(key: string): string | null
Returns the value of the query parameter for the given key or null if the key does not exist.
QueryString
classA helper class for working with the query string.
Check it out at src/lib/query.svelte.ts. or import it with:
import { QueryString } from "@mateothegreat/svelte5-router";
and start using it now!
Basic usage:
import { QueryString } from "@mateothegreat/svelte5-router";
const query = new QueryString();
query.get("foo", "bar"); // "bar"
query.set("foo", "baz");
query.toString(); // "foo=baz"
Using it with navigation:
import { QueryString } from "@mateothegreat/svelte5-router";
const query = new QueryString();
// ...
query.set("foo", "baz");
// ...
query.goto("/test"); // Navigates to "/test?foo=baz"
You can simply use static paths like /foo
or dynamic paths like /foo/(.*?)
with regex.
Example patterns:
Pattern | Description |
---|---|
/ |
The root path. |
/foo |
A static path. |
/foo/(.*?) |
A dynamic path. |
/cool/(.*?)/(.*?) |
A dynamic path with two parameters. |
For transparency, here's the type definition for a route:
Only
path
is required at a minimum with either pre/post hooks or a component/snippet.
export interface Route {
path: RegExp | string;
component?: Component<any> | Snippet;
props?: Record<string, any>;
pre?: PreHooks;
post?: PostHooks;
}
Hooks are typed as follows:
As you can see, you can pass an array of hooks or a single hook as a promise or not:
export type PreHooks = ((route: Route) => Route)[] | ((route: Route) => Promise<Route>)[] | ((route: Route) => Route) | ((route: Route) => Promise<Route>);
export type PostHooks = ((route: Route) => void)[] | ((route: Route) => Promise<void>)[] | ((route: Route) => void) | ((route: Route) => Promise<void>);
Use async routes simply with component: async () => import("./my-component.svelte")
.
const routes: Route[] = [
{
path: "simple",
component: Simple
},
{
path: "async",
component: async () => import("./lib/async/async.svelte")
}
];
For the quickest and easiest routes, you can use components:
const routes: Route[] = [
{
path: "/foo",
component: Foo
}
];
For more complex routing needs, you can use snippets:
<script lang="ts">
import { route, Router, type Route } from "@mateothegreat/svelte5-router";
import All from "./all.svelte";
const routes: Route[] = [
{
path: "/snippetsarecool",
component: mySnippet
}
];
</script>
{#snippet mySnippet()}
<div class="flex flex-col gap-3 bg-green-400 p-4">
I'm a snippet!<br />
Click on a link above to see the params..
</div>
{/snippet}
When your component is rendered, the route
object will be passed in as a prop. You can then access the parameter(s) of a route using the route.params
property:
<script lang="ts">
import type { Route } from "@mateothegreat/svelte5-router";
let { params }: { params: string[] } = $props();
</script>
<pre>{JSON.stringify(params, null, 2)}</pre>
If you were to route to /cool/bar/baz
, this will result in the following output:
[
"bar",
"baz"
]
You can pass props to a route by using the props
property on any route. These props will be passed to the component as a prop:
const routes: Route[] = [
{
path: "/user/profile",
component: UserProfile,
props: {
myProps: {
date: new Date(),
name: "mateothegreat"
}
}
}
];
Then, in your component, you can access the prop like this:
<script lang="ts">
let { myProps } = $props();
</script>
<pre>{JSON.stringify(myProps, null, 2)}</pre>
pre
and post
hooksUse pre
and post
hooks to run before and after a route is rendered to do things like authentication, logging, etc.
Syntax | Location | Description |
---|---|---|
<Router pre={myHooks}> |
<Router/> |
Runs before any route is rendered. |
<Router post={myHooks}> |
<Router/> |
Runs after any route is rendered. |
{ path: "/", pre: () => {...}} |
Route |
Runs before the specific route is rendered. |
{ path: "/", post: () => {...}} |
Route |
Runs after the specific route is rendered. |
You can pass an array or single method for the
pre
andpost
hooks.
const routes: Route[] = [
{
path: "unprotected",
component: Unprotected
post: () => {
console.log("post hook fired");
}
},
{
path: "protected",
component: Protected,
// Use a pre hook to simulate a protected route:
pre: (route: Route) => {
console.log("pre hook #1 fired for route:", route);
// Crude example of checking if the user is logged in. A more
// sophisticated example would use a real authentication system
// and a server-side API.
if (!localStorage.getItem("token")) {
// By returning a new route, the user will be redirected to the
// new route and then the post hook(s) will be executed:
return {
path: "/login",
component: Login
};
} else {
// By returning a new route, the user will be redirected to the
// new route and then the post hook(s) will be executed:
return {
path: "/bankaccount",
component: BankAccount
};
}
},
post: [
(route: Route): void => {
console.log("post hook #1 fired for route:", route);
},
(route: Route): void => {
console.log("post hook #2 fired for route:", route);
}
]
}
];
<script lang="ts">
import { Router } from "@mateothegreat/svelte5-router";
const routes: Route[] = [
{
path: "",
component: Homepage
},
{
path: "about",
component: About
}
];
let navigating: boolean; // This is $state<boolean>
</script>
<Router bind:navigating {routes} />
<span class="rounded border border-zinc-800 px-1 py-0.5 text-orange-500">
{navigating ? "(true) navigating..." : "(false) idle"}
</span>
<script lang="ts">
import type { Route } from "@mateothegreat/svelte5-router";
import { route, Router } from "@mateothegreat/svelte5-router";
...
const routes: Route[] = [
{
path: "/",
component: Homepage
},
{
path: "about",
component: About
},
{
path: "settings",
component: Settings
}
];
</script>
<div class="flex gap-2">
<a use:route href="/">Home</a>
<a use:route href="/about">About</a>
<a use:route href="/settings">Settings</a>
</div>
<Router base="/" {routes} />
</div>
For a real world example, check out the test app.
To deploy to Vercel, add the following to your vercel.json
:
{
"routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }]
}