Getting started
Core concepts
Build and deploy
Advanced
Best practices
Appendix
Reference
- @sveltejs/kit
- @sveltejs/kit/hooks
- @sveltejs/kit/node/polyfills
- @sveltejs/kit/node
- @sveltejs/kit/vite
- $app/environment
- $app/forms
- $app/navigation
- $app/paths
- $app/server
- $app/state
- $app/stores
- $env/dynamic/private
- $env/dynamic/public
- $env/static/private
- $env/static/public
- $lib
- $service-worker
- Configuration
- Command Line Interface
- Types
Shallow routing
As you navigate around a SvelteKit app, you createhistory entries. Clicking the back and forward buttons traverses through this list of entries, re-running anyload
functions and replacing page components as necessary.
Sometimes, it’s useful to create history entrieswithout navigating. For example, you might want to show a modal dialog that the user can dismiss by navigating back. This is particularly valuable on mobile devices, where swipe gestures are often more natural than interacting directly with the UI. In these cases, a modal that isnot associated with a history entry can be a source of frustration, as a user may swipe backwards in an attempt to dismiss it and find themselves on the wrong page.
SvelteKit makes this possible with thepushState
andreplaceState
functions, which allow you to associate state with a history entry without navigating. For example, to implement a history-driven modal:
<script>import{ pushState }from'$app/navigation';import{ page }from'$app/state';importModalfrom'./Modal.svelte';functionshowModal() {pushState('',{showModal:true});}</script>{#ifpage.state.showModal}<Modalclose={()=>history.back()} />{/if}
<scriptlang="ts">import{ pushState }from'$app/navigation';import{ page }from'$app/state';importModalfrom'./Modal.svelte';functionshowModal() {pushState('',{showModal:true});}</script>{#ifpage.state.showModal}<Modalclose={()=>history.back()} />{/if}
The modal can be dismissed by navigating back (unsettingpage.state.showModal
) or by interacting with it in a way that causes theclose
callback to run, which will navigate back programmatically.
API
The first argument topushState
is the URL, relative to the current URL. To stay on the current URL, use''
.
The second argument is the new page state, which can be accessed via thepage object aspage.state
. You can make page state type-safe by declaring anApp.PageState
interface (usually insrc/app.d.ts
).
To set page state without creating a new history entry, usereplaceState
instead ofpushState
.
Legacy mode
page.state
from$app/state
was added in SvelteKit 2.12. If you’re using an earlier version or are using Svelte 4, use$page.state
from$app/stores
instead.
Loading data for a route
When shallow routing, you may want to render another+page.svelte
inside the current page. For example, clicking on a photo thumbnail could pop up the detail view without navigating to the photo page.
For this to work, you need to load the data that the+page.svelte
expects. A convenient way to do this is to usepreloadData
inside theclick
handler of an<a>
element. If the element (or a parent) usesdata-sveltekit-preload-data
, the data will have already been requested, andpreloadData
will reuse that request.
<script>import{ preloadData,pushState,goto }from'$app/navigation';import{ page }from'$app/state';importModalfrom'./Modal.svelte';importPhotoPagefrom'./[id]/+page.svelte';let{ data }=$props();</script>{#eachdata.thumbnailsasthumbnail}<ahref="/photos/{thumbnail.id}"onclick={async(e)=>{if(innerWidth<640// bail if the screen is too small||e.shiftKey// or the link is opened in a new window||e.metaKey||e.ctrlKey// or a new tab (mac: metaKey, win/linux: ctrlKey)// should also consider clicking with a mouse scroll wheel)return;// prevent navigatione.preventDefault();const{href}=e.currentTarget;// run `load` functions (or rather, get the result of the `load` functions// that are already running because of `data-sveltekit-preload-data`)constresult=awaitpreloadData(href);if(result.type==='loaded'&&result.status===200) {pushState(href,{ selected:result.data });}else{// something bad happened! try navigatinggoto(href);}}}><imgalt={thumbnail.alt}src={thumbnail.src} /></a>{/each}{#ifpage.state.selected}<Modalonclose={()=>history.back()}><!-- pass page data to the +page.svelte component,just like SvelteKit would on navigation --><PhotoPagedata={page.state.selected} /></Modal>{/if}
<scriptlang="ts">import{ preloadData,pushState,goto }from'$app/navigation';import{ page }from'$app/state';importModalfrom'./Modal.svelte';importPhotoPagefrom'./[id]/+page.svelte';let{ data }=$props();</script>{#eachdata.thumbnailsasthumbnail}<ahref="/photos/{thumbnail.id}"onclick={async(e)=>{if(innerWidth<640// bail if the screen is too small||e.shiftKey// or the link is opened in a new window||e.metaKey||e.ctrlKey// or a new tab (mac: metaKey, win/linux: ctrlKey)// should also consider clicking with a mouse scroll wheel)return;// prevent navigatione.preventDefault();const{href}=e.currentTarget;// run `load` functions (or rather, get the result of the `load` functions// that are already running because of `data-sveltekit-preload-data`)constresult=awaitpreloadData(href);if(result.type==='loaded'&&result.status===200) {pushState(href,{ selected:result.data });}else{// something bad happened! try navigatinggoto(href);}}}><imgalt={thumbnail.alt}src={thumbnail.src} /></a>{/each}{#ifpage.state.selected}<Modalonclose={()=>history.back()}><!-- pass page data to the +page.svelte component,just like SvelteKit would on navigation --><PhotoPagedata={page.state.selected} /></Modal>{/if}
Caveats
During server-side rendering,page.state
is always an empty object. The same is true for the first page the user lands on — if the user reloads the page (or returns from another document), state willnot be applied until they navigate.
Shallow routing is a feature that requires JavaScript to work. Be mindful when using it and try to think of sensible fallback behavior in case JavaScript isn’t available.