Svelte Integration

@quantajs/svelte turns QuantaJS stores into standard Svelte stores, so $ subscriptions work in Svelte 4 and 5 and in SvelteKit. The stores themselves are ordinary QuantaJS stores: the same definition works in React, Vue or plain TypeScript.

Installation

npm install @quantajs/svelte @quantajs/core
# or
pnpm add @quantajs/svelte @quantajs/core
# or
yarn add @quantajs/svelte @quantajs/core

The shortest version

// src/lib/stores/cart.ts
import { defineStore } from '@quantajs/core';

export const useCartStore = defineStore('cart', {
  state: () => ({ items: [] as { name: string; price: number }[] }),
  getters: { total: (s) => s.items.reduce((n, i) => n + i.price, 0) },
  actions: {
    add(name: string, price: number) {
      this.items.push({ name, price });
    },
  },
});
<!-- Cart.svelte -->
<script lang="ts">
  import { useQuantaActions, useQuantaValue } from '@quantajs/svelte';
  import { useCartStore } from '$lib/stores/cart';

  const total = useQuantaValue(useCartStore, (s) => s.total);
  const cart = useQuantaActions(useCartStore);
</script>

<p>Total: {$total}</p>
<button onclick={() => cart.add('Tea', 4)}>Add tea</button>

No setup is needed in a client-only app: stores resolve against the default container.

Choosing a function

FunctionNotifies onUse for
useQuantaValue(definition, selector, options?)What the selector readsMost components
useQuanta(definition)Any change to the storeSmall stores, or components that read most of it
useQuantaActions(definition)NothingComponents that only call actions; returns the store itself
useLocalStore(definition)Any change to the storeA store owned by one component, disposed with it

Each returns a standard Svelte store. In a component, $ subscribes and unsubscribes for you. In a .svelte.ts file, fromStore from svelte/store turns one into state. A store tracks QuantaJS state only while something is subscribed, so it can also be created at module level.

useQuantaValue(definition, selector, options?)

The selector runs inside QuantaJS's own tracking, so subscribers are notified when state the selector read changes, including an array or object mutated in place, and not otherwise. A selector that builds a new object on each run should pass shallow:

import { shallow, useQuantaValue } from '@quantajs/svelte';
import { useCartStore } from '$lib/stores/cart';

export const summary = useQuantaValue(
  useCartStore,
  (s) => ({ count: s.items.length, total: s.total }),
  { equalityFn: shallow },
);

useQuanta(definition)

A Svelte store holding the store itself, notifying on any change to it:

<script lang="ts">
  import { useQuanta } from '@quantajs/svelte';
  import { useCartStore } from '$lib/stores/cart';

  const cart = useQuanta(useCartStore);
</script>

{#each $cart.items as item}
  <li>{item.name}</li>
{/each}

useLocalStore(definition)

Each component instance gets its own store, in a container disposed when the component is destroyed. Call it while the component initialises.

SvelteKit and server rendering

Call setQuantaContainer() in the root layout. On the server the layout renders once per request, so each request gets its own container, disposed when the layout is destroyed:

<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { setQuantaContainer } from '@quantajs/svelte';

  let { data, children } = $props();
  setQuantaContainer(undefined, { snapshot: data.snapshot });
</script>

{@render children()}

To load state on the server, resolve stores against a container in a load function and return its snapshot. The layout applies it before anything renders, so the client's markup matches the server's:

// src/routes/+layout.server.ts
import { createContainer } from '@quantajs/core';
import { useCartStore } from '$lib/stores/cart';

export async function load() {
  const container = createContainer();
  useCartStore(container).add('Loaded on the server', 0);
  const snapshot = container.dehydrate();
  container.dispose();
  return { snapshot };
}

Never resolve stores against the default container on the server: it is shared by every request. Pass your own container to setQuantaContainer(container) to manage its lifetime yourself; getQuantaContainer() returns the container in context.

DevTools

npm install -D @quantajs/devtools
import { enableDevTools } from '@quantajs/svelte';

if (import.meta.env.DEV) {
  enableDevTools({ redact: ['token'] });
  import('@quantajs/devtools').then(({ mountDevTools }) => mountDevTools());
}

See DevTools for what the panel shows.

Learn More

Spot something that needs improving?

Edit page