Routing
Edit this page on GitHubAt the heart of SvelteKit is a filesystem-based router. This means that the structure of your application is defined by the structure of your codebase — specifically, the contents of src/routes
.
You can change this to a different directory by editing the project config.
There are two types of route — pages and endpoints.
Pages typically generate HTML to display to the user (as well as any CSS and JavaScript needed for the page). By default, pages are rendered on both the client and server, though this behaviour is configurable.
Endpoints run only on the server (or when you build your site, if prerendering). This means it's the place to do things like access databases or APIs that require private credentials or return data that lives on a machine in your production network. Pages can request data from endpoints. Endpoints return JSON by default, though may also return data in other formats.
Pagespermalink
Pages are Svelte components written in .svelte
files (or any file with an extension listed in config.extensions
). By default, when a user first visits the application, they will be served a server-rendered version of the page in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel where the common portions in the layout do not need to be rerendered.
The filename determines the route. For example, src/routes/index.svelte
is the root of your site:
src/routes/index.svelte
<svelte:head>
<title>Welcome</title>
</svelte:head>
<h1>Hello and welcome to my site!</h1>
<a href="/about">About my site</a>
A file called either src/routes/about.svelte
or src/routes/about/index.svelte
would correspond to the /about
route:
src/routes/about.svelte
<svelte:head>
<title>About</title>
</svelte:head>
<h1>About this site</h1>
<p>TODO...</p>
<a href="/">Home</a>
Note that SvelteKit uses
<a>
elements to navigate between routes, rather than a framework-specific<Link>
component.
Dynamic parameters are encoded using [brackets]
. For example, a blog post might be defined by src/routes/blog/[slug].svelte
. These parameters can be accessed in a load
function or via the page
store.
A route can have multiple dynamic parameters, for example src/routes/[category]/[item].svelte
or even src/routes/[category]-[item].svelte
. (Parameters are 'non-greedy'; in an ambiguous case like x-y-z
, category
would be x
and item
would be y-z
.)
Endpointspermalink
Endpoints are modules written in .js
(or .ts
) files that export request handler functions corresponding to HTTP methods. Request handlers make it possible to read and write data that is only available on the server (for example in a database, or on the filesystem).
Their job is to return a { status, headers, body }
object representing the response.
src/routes/random.js
ts
export async functionget () {return {status : 200,headers : {'access-control-allow-origin': '*'},body : {number :Math .random ()}};}
status
is an HTTP status code:2xx
— successful response (default is200
)3xx
— redirection (should be accompanied by alocation
header)4xx
— client error5xx
— server error
headers
can either be a plain object, as above, or an instance of theHeaders
classbody
can be a plain object or, if something goes wrong, anError
. It will be serialized as JSON
A GET
or HEAD
response must include a body
, but beyond that restriction all three properties are optional.
Page endpointspermalink
If an endpoint has the same filename as a page (except for the extension), the page gets its props from the endpoint — via fetch
during client-side navigation, or via direct function call during SSR. (If a page uses syntax for named layouts or matchers in its filename then the corresponding page endpoint's filename must also include them.)
For example, you might have a src/routes/items/[id].svelte
page...
src/routes/items/[id].svelte
<script>
// populated with data from the endpoint export let item;
</script>
<h1>{item.title}</h1>
...paired with a src/routes/items/[id].js
endpoint (don't worry about the $lib
import, we'll get to that later):
src/routes/items/[id].js
ts
importdb from '$lib/database';export async functionget ({params }) {// `params.id` comes from [id].jsconstitem = awaitdb .get (params .id );if (item ) {return {status : 200,headers : {},body : {item }};}return {status : 404};}
The type of the
get
function above comes from./__types/[id].d.ts
, which is a file generated by SvelteKit (inside youroutDir
, using therootDirs
option) that provides type safety when accessingparams
. See the section on generated types for more detail.
To get the raw data instead of the page, you can include an accept: application/json
header in the request, or — for convenience — append /__data.json
to the URL, e.g. /items/[id]/__data.json
.
Standalone endpointspermalink
Most commonly, endpoints exist to provide data to the page with which they're paired. They can, however, exist separately from pages. Standalone endpoints have slightly more flexibility over the returned body
type — in addition to objects and Error
instances, they can return a Uint8Array
or a ReadableStream
.
Standalone endpoints can be given a file extension if desired, or accessed directly if not:
filename | endpoint |
---|---|
src/routes/data/index.json.js | /data.json |
src/routes/data.json.js | /data.json |
src/routes/data/index.js | /data |
src/routes/data.js | /data |
POST, PUT, PATCH, DELETEpermalink
Endpoints can handle any HTTP method — not just GET
— by exporting the corresponding function:
ts
export functionpost (event ) {...}export functionput (event ) {...}export functionpatch (event ) {...}export functiondel (event ) {...} // `delete` is a reserved word
These functions can, like get
, return a body
that will be passed to the page as props. Whereas 4xx/5xx responses from get
will result in an error page rendering, similar responses to non-GET requests do not, allowing you to do things like render form validation errors:
src/routes/items.js
ts
import * asdb from '$lib/database';export async functionget () {constitems = awaitdb .list ();return {body : {items }};}export async functionpost ({request }) {const [errors ,item ] = awaitdb .create (request );if (errors ) {// return validation errorsreturn {status : 400,body : {errors }};}// redirect to the newly created itemreturn {status : 303,headers : {location : `/items/${item .id }`}};}
src/routes/items.svelte
<script>
// The page always has access to props from `get`... export let items;
// ...plus props from `post` when the page is rendered // in response to a POST request, for example after // submitting the form below export let errors;
</script>
{#each items as item}
<Preview item={item}/>
{/each}
<form method="post">
<input name="title">
{#if errors?.title}
<p class="error">{errors.title}</p>
{/if}
<button type="submit">Create item</button>
</form>
Body parsingpermalink
The request
object is an instance of the standard Request class. As such, accessing the request body is easy:
ts
export async functionpost ({request }) {constdata = awaitrequest .formData (); // or .json(), or .text(), etcawaitcreate (data );return {status : 201 };}
Setting cookiespermalink
Endpoints can set cookies by returning a headers
object with set-cookie
. To set multiple cookies simultaneously, return an array:
ts
export functionget () {return {headers : {'set-cookie': [cookie1 ,cookie2 ]}};}
HTTP method overridespermalink
HTML <form>
elements only support GET
and POST
methods natively. You can allow other methods, like PUT
and DELETE
, by specifying them in your configuration and adding a _method=VERB
parameter (you can configure the name) to the form's action
:
svelte.config.js
ts
constconfig = {kit : {methodOverride : {allowed : ['PUT', 'PATCH', 'DELETE']}}};export defaultconfig ;
<form method="post" action="/todos/{id}?_method=PUT">
<!-- form elements --></form>
Using native
<form>
behaviour ensures your app continues to work when JavaScript fails or is disabled.
Private modulespermalink
Files and directories with a leading _
or .
(other than .well-known
) are private by default, meaning that they do not create routes (but can be imported by files that do). You can configure which modules are considered public or private with the routes
configuration.
Advanced routingpermalink
Rest parameterspermalink
If the number of route segments is unknown, you can use rest syntax — for example you might implement GitHub's file viewer like so...
/[org]/[repo]/tree/[branch]/[...file]
...in which case a request for /sveltejs/kit/tree/master/documentation/docs/01-routing.md
would result in the following parameters being available to the page:
ts
{org: 'sveltejs',repo : 'kit',branch : 'master',file : 'documentation/docs/01-routing.md'}
src/routes/a/[...rest]/z.svelte
will match/a/z
(i.e. there's no parameter at all) as well as/a/b/z
and/a/b/c/z
and so on. Make sure you check that the value of the rest parameter is valid, for example using a matcher.
Matchingpermalink
A route like src/routes/archive/[page]
would match /archive/3
, but it would also match /archive/potato
. We don't want that. You can ensure that route parameters are well-formed by adding a matcher — which takes the parameter string ("3"
or "potato"
) and returns true
if it is valid — to your params
directory...
src/params/integer.js
ts
export functionmatch (param ) {return /^\d+$/.test (param );}
...and augmenting your routes:
src/routes/archive/[page]
src/routes/archive/[page=integer]
If the pathname doesn't match, SvelteKit will try to match other routes (using the sort order specified below), before eventually returning a 404.
Matchers run both on the server and in the browser.
Sortingpermalink
It's possible for multiple routes to match a given path. For example each of these routes would match /foo-abc
:
src/routes/[...catchall].svelte
src/routes/[a].js
src/routes/[b].svelte
src/routes/foo-[c].svelte
src/routes/foo-abc.svelte
SvelteKit needs to know which route is being requested. To do so, it sorts them according to the following rules...
- More specific routes are higher priority (e.g. a route with no parameters is more specific than a route with one dynamic parameter, and so on)
- Standalone endpoints have higher priority than pages with the same specificity
- Parameters with matchers (
[name=type]
) are higher priority than those without ([name]
) - Rest parameters have lowest priority
- Ties are resolved alphabetically
...resulting in this ordering, meaning that /foo-abc
will invoke src/routes/foo-abc.svelte
, and /foo-def
will invoke src/routes/foo-[c].svelte
rather than less specific routes:
src/routes/foo-abc.svelte
src/routes/foo-[c].svelte
src/routes/[a].js
src/routes/[b].svelte
src/routes/[...catchall].svelte
Encodingpermalink
Filenames are URI-decoded, meaning that (for example) a filename like %40[username].svelte
would match characters beginning with @
:
ts
assert .equal (decodeURIComponent ('%40[username].svelte'),'@[username].svelte');
To express a %
character, use %25
, otherwise the result will be malformed.