Skip to content

State in Modules

React developers share state across the component tree with Context — createContext, a Provider wrapping part of the tree, and useContext in every consumer. It works, but it requires ceremony: the Provider must wrap all consumers, and adding a new piece of shared state means updating the Provider.

Svelte 5 introduces a simpler model: .svelte.ts modules. You declare $state and $derived at module scope in a .svelte.ts (or .svelte.js) file, export those variables, and import them in any component. The reactivity just works — every component that reads the variable re-renders when it changes. No Provider, no context object, no useContext call.

React
// userStore.ts
import { createContext, useContext, useState } from 'react';
const UserContext = createContext(null);
export function UserProvider({ children }) {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
export function useUser() {
return useContext(UserContext);
}
// App.tsx — must wrap consumers
<UserProvider>
<Header />
<Main />
</UserProvider>
// Header.tsx
const { user } = useUser();
Svelte
// user.svelte.ts
export let user = $state(null);
export function setUser(next) { user = next; }
// Header.svelte — just import, no Provider needed
<script>
import { user, setUser } from './user.svelte.ts';
</script>
<p>Hello, {user?.name ?? 'Guest'}</p>
<button onclick={() => setUser({ name: 'Ada' })}>Log in</button>
// Main.svelte — same import, same reactive state
<script>
import { user } from './user.svelte.ts';
</script>
{#if user}
<p>Welcome back, {user.name}!</p>
{/if}

Any component that imports user from user.svelte.ts automatically re-renders when user changes, because the Svelte compiler treats $state variables as reactive even at module scope.

.svelte.ts modules require multiple files, which the playground can’t show. Below is a self-contained demo that uses the same reactive pattern — $state and $derived declared at the top of the script block, shared across simulated “sections” of the UI.

In a real project you would move the state declarations to a .svelte.ts file and import them.

What file extension enables $state and $derived at module scope in Svelte 5?
A component imports a $state variable from a .svelte.ts module. When does the component re-render?
What React pattern does module-level $state replace, and what does it remove?