Share state without leaking it between users — the server pitfall to avoid, load data, and Svelte 5 rune stores in .svelte.ts.
Why: the server handles many users at once, so a value stored at the top level of a server module is SHARED across everyone — one user could see another’s data. Never keep per-user state in a module variable on the server.
// DON'T: this single variable is shared by every visitor on the server
let currentUser = null // ⚠️ leaks data between users
// DO: keep per-request data on event.locals (set in hooks.server.ts)
// and per-user data in cookies or a database keyed by session.Why: the safe, built-in way to get data to a page is a load function — it runs per request and the result belongs only to that request. Reach for shared client state only for things that live purely in the browser (like a theme toggle).
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types'
export const load: LayoutServerLoad = async ({ locals }) => {
// Available to every page via the data prop — no shared variables
return { user: locals.user }
}Why: for state that lives only in the browser (a cart, a theme), a .svelte.ts file can use the $state rune and export it. Note: the .svelte.ts extension is what lets a plain file use runes.
// src/lib/counter.svelte.ts
export const counter = $state({ count: 0 })
export function increment() {
counter.count += 1
}Why: import the rune store and read or update it directly — Svelte tracks it and re-renders when count changes. No provider or boilerplate.
<!-- src/lib/components/Counter.svelte -->
<script lang="ts">
import { counter, increment } from '$lib/counter.svelte'
</script>
<button onclick={increment}>Count: {counter.count}</button>