Pattern: Shared Module Store
Problem
Multiple components or modules need access to the same reactive state. You want a shared store without a global registry, a plugin system, or constructor injection.
Solution
Structure stores as plain modules — no class registration or plugin needed:
ts
// stores/auth.store.ts
import { computed, readonly, store } from '@vielzeug/stateit';
import type { ReadonlySignal } from '@vielzeug/stateit';
type User = { id: string; name: string };
type Credentials = { email: string; password: string };
async function authenticate(credentials: Credentials): Promise<{ token: string; user: User }> {
void credentials;
return { token: 'demo-token', user: { id: 'u1', name: 'Demo User' } };
}
type AuthState = {
token: string | null;
user: User | null;
loading: boolean;
};
const s = store<AuthState>({ token: null, user: null, loading: false });
// Computed signals for derived values
export const isAuthenticated = computed(() => !!s.value.token);
export const currentUser = computed(() => s.value.user);
// Public read-only view — callers can observe but not mutate
export const authStore: ReadonlySignal<AuthState> = readonly(s);
// Mutations (exported as functions, not methods)
export async function login(credentials: Credentials) {
s.patch({ loading: true });
try {
const { token, user } = await authenticate(credentials);
s.update(() => ({ token, user, loading: false }));
} catch {
s.patch({ loading: false });
}
}
export function logout() {
s.reset();
}Pitfalls
- A module-level store is a singleton. Tests that import it share the same instance — reset signal values to initial state in
afterEachto prevent cross-test contamination. - Circular imports between the store module and a module that uses it can cause the store to be
undefinedduring initialization. Keep the store in a leaf module with no upstream dependencies. - Exporting individual writable signals directly allows external code to mutate internal state, breaking encapsulation. Export derived read-only values or explicit setter functions instead.