CALM is both an acronym and a design philosophy for building Svelte 5 applications that remain maintainable over time.

The CALM Principles

Contained: Every piece of state has a single, explicit owner.

Automatic: Reactivity happens without manual wiring.

Local: State lives as close as possible to where it is used.

Minimal: Every dependency, abstraction, and feature must earn its place.

Core Tenets

Data Flow, Not Lifecycle

Think about your app in terms of how data flows, not which lifecycle event fires when. Svelte 5’s runes encourage this mindset — reactivity tracks data dependencies, not lifecycle hooks.

Derived State as the Backbone

Prefer $derived over imperative $effect for computed values. Derived state is declarative, automatically consistent, and free of timing bugs.

Mutation Is Fine

Svelte 5 embraces mutation of reactive state. There is no need for immutable update patterns — reassigning properties of $state objects triggers reactive updates naturally.

No Global Store Until Proven Guilty

Start with local $state. Escalate to Context API for shared subtrees. Only introduce a global store when you have proven that local + context patterns are insufficient.

Load Functions Are Pure

SvelteKit load functions should be pure data-fetching operations — no side effects, no component logic. This makes them testable, cacheable, and serializable.

Routes Are State Boundaries

Each route in SvelteKit is an implicit state boundary. Data loaded in one route is isolated from others. Use layout data for shared state across routes.

Error Handling at the Boundary

Handle errors at the component boundary (<svelte:boundary>), the route boundary (+error.svelte), and the app boundary (hooks.server.js). Never inside business logic.

Practical Patterns

  • Component communication: Prefer props (parent to child) and context (ancestor to descendant). Use events for child-to-parent signaling. Avoid global stores for local concerns.
  • Testing: Because state is local and functions are pure, testing becomes straightforward. Mock at data-loading boundaries, not inside components.
  • When to use $effect: For synchronizing with non-Svelte systems (localStorage, WebSockets, analytics), NOT for deriving values.
  • Maintenance: A calm system is one where you can trace how data flows without opening every file. Explicit ownership and local state make this possible.
  • The cost of cleverness: Prefer simple, obvious code over clever abstractions. The best code is the code your future self can understand in one read.
  • What not to do: Avoid premature abstraction, unnecessary stores, over-engineering for hypothetical future needs, and mixing concerns in single components.

See Also