State Management
Edit this page on GitHubManaging state is one of the hardest parts in application development. In this section, we will go over the different types of state, how SvelteKit can help you with managing it, and different patterns for various use cases.
Types of statepermalink
Apps have a multitude of different states. State can broadly be classified by two adjacent categories: The semantic category of the state, and how it is represented.
Semantic categories of state are:
- Local state: State that is only relevant inside one component, and managed inside it.
- Global state: State that is relevant to many or all parts of the application, and is likely modified from different places.
How state can be represented:
- JavaScript variable: Not visible from the outside
- URL: What page you're on, but also information for the page itself through query parameters for example
- Cookies: Mostly modifiable through the server, contains for example information about the current user. Can be used to persist state across browser tabs or reloads.
- localStorage/sessionStorage: Persist state across tabs or reloads, for example user settings.
- API/Backend: Data that is saved inside a database, for example the list of TODOs the user entered.
The above are not exhaustive lists, and different measures could be used to categorize state. The key point is to think about state systematically before adding it to your application.
Careful with global statepermalink
If you are creating an SPA with SvelteKit, you can create global state freely, as you can be sure that it's only initialized inside the user's browser. If you use SSR however, you have to watch out for a couple of things when managing state. In many server environments, a single instance of your app will serve multiple users (this is not specific to SvelteKit - it's one of the gotchas of working with such environments). For that reason, per-request or per-user state must not be stored in global variables.
Consider the following example where the user is set from inside a load
function:
ts
// DON'T DO THIS!import {user } from '$lib/user';/** @type {import('./$types').PageLoad} */export async functionload ({fetch }) {constresponse = awaitfetch ('/api/user');user .set (awaitresponse .json ());}
If you are using SSR, the load
function will run on the server initially, which means that the whole server instance which serves all requests from all users has its user
state set to the one just requested from the API. To scope this to a single user, you have a couple of options:
- if you need to access the user state only inside
load
functions, uselocals
in serverload
functions - if you need to persist the user state across reloads, but only need to access it inside
load
functions, usecookies
in serverload
functions - if you need to access and update the state inside components, use Svelte's context feature. That way, the state is scoped to components, which means they are not shared across different requests on the server. The drawback is that you can only set and subscribe to the store at component initialization. SvelteKit's stores from
$app/stores
for example are setup like this (which is why you may have encountered a related error message)