@gbarillot/svelte-router
v1.0.1
Published
Another vue-router inspired Svelte router
Downloads
2
Maintainers
Readme
Svelte Router
Yet another SPA vue-router inspired Svelte router Based on the excellent https://github.com/shaunlee/svelte-router
Installation
npm
npm install @gbarillot/svelte-router
Getting Started
<script>
import { createRouter, link, Link, View } from '@gbarillot/svelte-router'
import Home from './Home.svelte'
import User from './User.svelte'
import NotFound from './NotFound.svelte'
const routes = [
{ path: '/', component: Home },
{ path: '/users/:userId(\\d+)'component: User },
{ path: '/users/:userId/edit', name: 'edit_user', component: User },
{ path: '*', component: NotFound }
]
const router = createRouter({ routes })
</script>
<Link path="/">Home</Link>
<Link path="/users/123">Someone</Link>
<Link path="/users/321" replace>Replace to someone else's page</Link>
<Link path="/not-exists">To page not exists</Link>
<Link to="edit_user" params={{userId: 123}}>Edit user</a>
<button type="button" on:click={() => router.push('/users/123')}>Click to someone's page</button>
<button type="button" on:click={() => router.goto('user_edit', {userId: 123}})}>Click to someone's page</button>
<button type="button" on:click={() => router.replace('/users/321')}>Click replace to someone else's page</button>
<a use:link href="/users/111">A link with action</a>
<a href="http://domain.com/outside.html">An external link with regular behavior</a>
<View></View>
Base path
You can setup a base path to prefix all your route definitions:
import {
createRouter
} from '@gbarillot/svelte-router'
const routes = [
{ path: '/', component: Home },
{ path: '/users/:userId/edit', name: 'edit_user', component: User },
{ path: '*', component: NotFound }
]
const router = createRouter({ base: 'fr/', routes: routes })
Dynamic Route Matching with Params
Very often we will need to map routes with the given pattern to the same component. For example we may have a User
component which should be rendered for all users but with different user IDs. In @gbarillot/svelte-router
we can use a dynamic segment in the path to achieve that, we call that a param:
import User from './User.svelte'
// these are passed to `createRouter`
const routes = [
// dynamic segments start with a colon
{ path: '/users/:id', component: User },
]
Now URLs like /users/johnny
and /users/jolyne
will both map to the same route.
A param is denoted by a colon :
. When a route is matched, the value of its params will be exposed as $router.params
. Therefore, we can render the current user ID by updating User's template to this:
<script>
import { router } from '@gbarillot/svelte-router'
</script>
<div>User ID: {$router.params.id}</div>
The same:
<script>
export let id
</script>
<div>User ID: {id}</div>
You can have multiple params in the same route, and they will map to corresponding fields on $router.params
. Examples:
| pattern | matched path | $router.params |
| --- | --- | --- |
| /users/:username | /users/eduardo | { username: 'eduardo' }
|
| /users/:username/posts/:postId | /users/eduardo/posts/123 | { username: 'eduardo', postId: '123' }
|
In addition to $router.params
, the $router
object also exposes other useful information such as $router.query
(if there is a query in the URL), $router.path
, etc.
Named routes
Instead of using a path to target a route, you can also use its name and pass along its params:
<Link to="edit_user" params={{userId: 123}}>Edit user</a>
Will generate this
<a href="/users/123/edit">Edit user</a>
You can also pass any HTML attribute, as well as data attributes:
<Link to="edit_user" params={{userId: 123}} attrs={{class: "card", title: "Edit user"}}>Edit user</Link>
<Link to="edit_user" params={{userId: 123}} data={{is: "edit-link", do: "edit"}}>Edit user</Link>
Will generate this
<a href="/users/123/edit" class="card" title="Edit user">Edit user</a>
<a href="/users/123/edit" data-is="edit-link" data-do="edit">Edit user</a>
Custom regex in params
When defining a param like :userId
, we internally use the following regex ([^/]+)
(at least one character that isn't a slash /
) to extract params from URLs.
This works well unless you need to differentiate two routes based on the param content. Imagine two routes /:orderId
and /:productName
, both would match the exact same URLs, so we need a way to differentiate them.
The easiest way would be to add a static section to the path that differentiates them:
const routes = [
// matches /o/3549
{ path: '/o/:orderId' },
// matches /p/books
{ path: '/p/:productName' },
]
But in some scenarios we don't want to add that static section /o/p
. However, orderId
is always a number while productName
can be anything, so we can specify a custom regex for a param in parentheses:
const routes = [
// /:orderId -> matches only numbers
{ path: '/:orderId(\\d+)' },
// /:productName -> matches anything else
{ path: '/:productName' },
]
Now, going to /25
will match /:orderId
while going to anything else will match /:productName
. The order of the routes
array doesn't even matter!
TIP Make sure to escape backslashes (
\
) like we did with\d
(becomes\\d
) to actually pass the backslash character in a string in JavaScript.
Handling Not found / 404
There are 2 ways of doing this, depending on your requirements:
Using you own custom component
import NotFound from './shared/not_found.svelte'
const routes = [
...
{ path: '*', component: 'NotFound' },
]
Redirecting to some path
onRouteChange((before, after) => {
// No match has been found
if(after === null) {
window.location.href = '/404.html'
}
})
Programmatic Navigation
Aside from using <Link>
to create anchor tags for declarative navigation, we can do this programmatically using the router's instance methods.
Navigate to a different location
To navigate to a different URL, use router.push
. This method pushes a new entry into the history stack, so when the user clicks the browser back button they will be taken to the previous URL.
This is the method called internally when you click a <Link>
, so clicking <Link href="...">
is the equivalent of calling router.push(...)
.
| Declarative | Programmatic |
| --- | --- |
| <Link href="...">
| router.push(...)
|
The argument is a string path. Examples:
router.push('/users/eduardo')
router.push('/users?page=2')
router.push('/users', { page: 2 })
Using router.goto
lets you use named routes
router.goto('users')
router.goto('user_edit', params: {id: 1})
Replace current location
It acts like router.push
, the only difference is that it navigates without pushing a new history entry, as its name suggests - it replaces the current entry.
| Declarative | Programmatic |
| --- | --- |
| <Link href="..." replace>
| router.replace('...')
|
Traverse history
This method takes a single integer as parameter that indicates by how many steps to go forward or go backward in the history stack, similar to window.history.go(n)
. Examples:
// go forward by one record, the same as router.forward()
router.go(1)
// go back by one record, the same as router.back()
router.go(-1)
// go forward by 3 records
router.go(3)
// fails silently if there aren't that many records
router.go(-100)
router.go(100)
History Manipulation
You may have noticed that router.push
, router.replace
and router.go
are counterparts of window.history.pushState
, window.history.replaceState
and window.history.go
, and they do imitate the window.history
APIs.
Therefore, if you are already familiar with Browser History APIs, manipulating history will feel familiar when using @gbarillot/svelte-router
.
It is worth mentioning that @gbarillot/svelte-router
navigation methods (push, replace, go) work consistently no matter the kind of mode
option is passed when creating the router instance.
Different History modes
The mode
option when creating the router instance allows us to choose among different history modes.
Hash Mode
The hash history mode is created with 'hash'
:
import { createRouter } from '@gbarillot/svelte-router'
const router = createRouter({
mode: 'hash',
routes: [
// ...
]
})
It uses a hash character (#
) before the actual URL that is internally passed. Because this section of the URL is never sent to the server, it doesn't require any special treatment on the server level. It does however have a bad impact in SEO. If that's a concern for you, use the HTML5 history mode.
HTML5 Mode
The HTML5 mode is created with 'web'
and is the recommended mode:
import { createRouter } from '@gbarillot/svelte-router'
const router = createRouter({
mode: 'web',
routes: [
// ...
]
})
When using 'web'
, the URL will look "normal," e.g. https://example.com/user/id
. Beautiful!
License
MIT