Course outline

Svelte / Intermediate: Kit routes and server data policy / Lesson 11

Context, stores and request-state isolation

Choose state sharing by owner and lifetime rather than exporting mutable private state from a server module.

About 4 min readContent reviewed
On this page

Lesson goals

  • Distinguish component context, stores and request-owned server data.
  • Preserve current-value access and nested ownership across component reuse.

A shared API does not imply global shared private state

A store or reactive module can expose a useful state interface, but its lifetime depends on where the instance is created. A module-level mutable user store on the server can be shared by unrelated requests. Component context provides a scoped way for descendants to obtain a value created by an ancestor, while event.locals belongs to a server request. These scopes solve different problems. Runes reduce some historical reasons to reach for stores, but stores remain useful for supported subscription-oriented interfaces. Do not replace every store mechanically or assume a rune variable is automatically request-isolated when exported from a singleton server module.

Provide current data through a scoped getter

The layout fragment sets context during component initialization and provides a getter for current catalog data. It assumes a shared stable token module and framework-supplied data/children props. Passing a getter avoids treating the initial data value as permanently current when Kit reuses the layout. A descendant must consume the same token and read the value in an appropriate reactive context. No component context was created here. The getter is not a credential verifier and must return only the permitted view model, not a server client or private configuration object.

svelte Illustrative scoped current-value context
<script>
  import { setContext } from "svelte";
  import { CATALOG_CONTEXT } from "#lib/context-key.js";
  let { data, children } = $props();
  setContext(CATALOG_CONTEXT, () => data.catalog);
</script>
{@render children()}

The shared token and public data contract are assumed. No context lookup or layout reuse was executed.

Keep subscription and mutable-reference ownership explicit

A store contract includes subscription behavior and cleanup, not just a value field. Automatic store syntax belongs to documented compiled contexts; a plain imported object with a similar name does not become a valid store. Context also does not deep-copy values: descendants can share the same mutable object deliberately or accidentally. Define which data is immutable and which state belongs to one component subtree or request. If an object is replaced, preserve the intended current-value relationship instead of leaving consumers with an old reference. Test lifetime and unsubscribe behavior, not merely that a first value appears.

Allocate independent nested state for each owner

The local factory creates a fresh principal wrapper and Map for each synthetic owner. Editing one draft set leaves the other untouched. This tests ordinary allocation and explicitly avoids shared nested mutable state. It is not a Svelte context, store or SSR test. A real application may safely share immutable service definitions, but not request-dependent credentials or drafts without an explicit protocol. Keep immutable values and one-operation state distinguished in the design; a fresh outer wrapper alone cannot isolate a shared inner object. Verify actual framework ownership and cross-request behavior separately.

javascript Complete offline nested-state isolation
function owner(id){return {principal:{id},drafts:new Map()};}
const a=owner("p1"),b=owner("p2");
a.drafts.set("c1","Draft A");
console.assert(a!==b && a.principal!==b.principal);
console.assert(a.drafts!==b.drafts && !b.drafts.has("c1"));

The two owners do not share principal wrappers or draft maps. No server requests or component contexts were created.

Align account changes, cleanup and server lifetimes

Retire old-account client state and reject incompatible pending results after login/logout changes. Keep server secrets in server-only modules and event-owned context, and review cache keys if any data is shared across callers. Test sequential and overlapping principals, component reuse, object replacement and unsubscribe cleanup in the actual authorized integration. This course executes only the allocation model; it does not run context, store subscriptions, SSR, logout or navigation. Record that limitation so a clean local Map test is not called proof that a deployed private store is isolated under concurrency.

Common errors and fixes

A server module store leaks another user’s state

Why it happens
Request-dependent mutable data lived in a shared module instance.
How to fix it
Create private state under its request or component owner.
Prevent it next time
Test overlapping and sequential principals.

A context consumer keeps an old catalog reference

Why it happens
Initial value capture was mistaken for current reactive access.
How to fix it
Provide a deliberate getter or stable reactive owner contract.
Prevent it next time
Test replacement and layout reuse without reload.

A fresh wrapper still shares editable inner state

Why it happens
Only outer identity changed while nested mutable references remained shared.
How to fix it
Define nested ownership and allocate or freeze values deliberately.
Prevent it next time
Assert nested mutation isolation, not only a!==b.

Check your understanding

1. Why is a new outer object not enough for request isolation?

Show worked solution

It may still point to the same nested principal, cache or draft collection. A shallow wrapper does not deep-copy dependencies. Define immutable shared values separately from mutable state owned by one operation, then test nested changes explicitly. Verify the real SSR/context allocation rather than inferring it from a local factory.

2. Are stores obsolete simply because Svelte 5 has runes?

Show worked solution

No. Runes cover many reactive-state use cases, while stores remain useful for their documented subscription interfaces and integrations. Choose by ownership, lifecycle and consumers rather than a blanket replacement rule. Both approaches can be unsafe if private server state is accidentally shared across requests.

Key takeaways

  • State sharing needs a named owner and lifetime.
  • Context and stores do not automatically isolate nested private state.

Official references