Skip to main content

API Reference

9 min read

Svelte Hooks

Svelte hooks API reference. All hooks take system as an explicit first parameter – no context injection needed. Values are returned as Svelte Readable stores.


Quick Reference

ExportTypeDescription
useFactHookRead single/multi facts – useFact(system, key)
useDerivedHookRead single/multi derivations – useDerived(system, id)
useSelectorHookSelect from all facts with custom equality – useSelector(system, selector, eq?)
useEventsHookTyped event dispatchers – useEvents(system)
useDispatchHookLow-level event dispatch – useDispatch(system)
useWatchHookSide-effect watcher (auto-detects kind) – useWatch(system, key, cb)
useInspectHookSystem inspection with optional throttle – useInspect(system, options?)
useConstraintStatusHookReactive constraint inspection – useConstraintStatus(system, constraintId?)
useExplainHookReactive requirement explanation – useExplain(system, reqId)
useRequirementStatusHookRequirement status – useRequirementStatus(statusPlugin, type)
useOptimisticUpdateHookOptimistic mutations with rollback – useOptimisticUpdate(system, statusPlugin?, type?)
useDirectiveHookScoped system with selected or all subscriptions
useTimeTravelHookReactive time-travel state – useTimeTravel(system)
createTypedHooksFactoryCreate fully typed hooks for a schema
createFactStoreFactoryFact store outside components
createDerivedStoreFactoryDerivation store outside components
createDerivedsStoreFactoryMulti-derivation store outside components
createInspectStoreFactoryInspection store outside components
shallowEqualUtilityShallow equality for selectors

useFact

Subscribe to a single fact or multiple facts. Returns a Svelte Readable store.

function useFact<K extends string>(system: System, key: K): Readable<InferFacts<S>[K]>
function useFact<K extends string[]>(system: System, keys: K): Readable<Record<K[number], unknown>>

useFact Usage

<script>
  import { useFact } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Subscribe to a single fact value
  const count = useFact(system, 'count');

  // Subscribe to multiple facts at once
  const multi = useFact(system, ['count', 'name']);
</script>

<p>Count: {$count}</p>
<p>Name: {$multi.name}, Count: {$multi.count}</p>

Need a transform?

Use useSelector to derive values from facts. It auto-tracks dependencies and supports custom equality.


useDerived

Subscribe to a single derivation or multiple derivations. Returns a Svelte Readable store.

function useDerived<K extends string>(system: System, key: K): Readable<InferDerivations<S>[K]>
function useDerived<K extends string[]>(system: System, keys: K): Readable<Record<K[number], unknown>>

useDerived Usage

<script>
  import { useDerived } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Subscribe to a single derivation
  const total = useDerived(system, 'cartTotal');

  // Subscribe to multiple derivations at once
  const stats = useDerived(system, ['isRed', 'elapsed']);
</script>

<p>Total: ${$total}</p>
<p>{$stats.isRed ? `Red for ${$stats.elapsed}s` : 'Not red'}</p>

Need a transform?

Use useSelector to derive values from facts. It auto-tracks dependencies and supports custom equality.


useSelector

Auto-tracking selector over facts and derivations. Returns a Svelte Readable store.

function useSelector<R>(
  system: System,
  selector: (state: Record<string, any>) => R,
  equalityFn?: (a: R, b: R) => boolean,
): Readable<R>

useSelector Usage

<script>
  import { useSelector } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Select and combine values from multiple facts
  const summary = useSelector(system, (state) => ({
    userName: state.user?.name,
    itemCount: state.items?.length ?? 0,
  }));
</script>

<p>{$summary.userName} has {$summary.itemCount} items</p>

useEvents

Get typed event dispatchers for all events defined in the module schema.

function useEvents(system: System): TypedEventDispatchers

useEvents Usage

<script>
  import { useEvents } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Get typed event dispatchers for the module
  const events = useEvents(system);
</script>

<button on:click={() => events.increment({ amount: 1 })}>
  Increment
</button>

useDispatch

Get the low-level dispatch function for sending events.

function useDispatch(system: System): (event: SystemEvent) => void

useDispatch Usage

<script>
  import { useDispatch } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Get the low-level dispatch function
  const dispatch = useDispatch(system);
</script>

<button on:click={() => dispatch({ type: 'increment' })}>
  Increment
</button>

useWatch

Execute a side-effect callback when a fact or derivation changes. Auto-detects whether the key refers to a fact or a derivation – no discriminator needed. Does not return a store; used for effects only. Automatically cleaned up when the component is destroyed.

// Unified API – auto-detects fact vs derivation
function useWatch<T>(
  system: System,
  key: string,
  callback: (newValue: T, prevValue: T | undefined) => void,
): void

// Deprecated – still works but prefer the unified form above
function useWatch<T>(
  system: System,
  type: "fact",
  factKey: string,
  callback: (newValue: T, prevValue: T | undefined) => void,
): void

useWatch Usage

<script>
  import { useWatch } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Watch a derivation – auto-detected
  useWatch(system, 'pageViews', (newValue, prevValue) => {
    analytics.track('pageViews', { from: prevValue, to: newValue });
  });

  // Watch a fact – also auto-detected, no "fact" discriminator needed
  useWatch(system, 'count', (newValue) => {
    console.log('Count changed to', newValue);
  });
</script>

Deprecated pattern

The four-argument form useWatch(system, "fact", "key", cb) still works but is deprecated. Use useWatch(system, "key", cb) instead.


useInspect

Get system inspection data reactively. Supports an optional throttle to limit update frequency.

function useInspect(system: System, opts?: { throttleMs?: number }): Readable<InspectState>

useInspect Usage

<script>
  import { useInspect } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Get reactive system inspection data with throttled updates
  const inspection = useInspect(system, { throttleMs: 200 });
</script>

<pre>
  Unmet: {$inspection.unmet.length}
  Inflight: {$inspection.inflight.length}
  Settled: {$inspection.isSettled}
</pre>

useConstraintStatus

Get reactive constraint inspection data. Optionally filter by constraint ID.

function useConstraintStatus(system: System): Readable<ConstraintInfo[]>
function useConstraintStatus(system: System, constraintId: string): Readable<ConstraintInfo | null>
interface ConstraintInfo {
  id: string;
  active: boolean;
  priority: number;
}

useConstraintStatus Usage

<script>
  import { useConstraintStatus } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Check a specific constraint by ID
  const status = useConstraintStatus(system, 'transition');
</script>

{#if $status}
  <p>Constraint "{$status.id}" is {$status.active ? 'active' : 'inactive'} (priority: {$status.priority})</p>
{/if}

useExplain

Get a reactive explanation of why a requirement was generated and how it was resolved.

function useExplain(system: System, requirementType: string): Readable<string | null>

useExplain Usage

<script>
  import { useExplain } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Get a reactive explanation string for a requirement
  const explanation = useExplain(system, 'FETCH_USER');
</script>

{#if $explanation}
  <p>{$explanation}</p>
{/if}

useRequirementStatus

Get requirement status reactively. Takes statusPlugin as its first parameter (not system). Supports a single type or multiple types.

function useRequirementStatus(statusPlugin: StatusPlugin, type: string): Readable<RequirementTypeStatus>
function useRequirementStatus(statusPlugin: StatusPlugin, types: string[]): Readable<Record<string, RequirementTypeStatus>>

useRequirementStatus Usage

<script>
  import { useRequirementStatus } from '@directive-run/svelte';

  // statusPlugin is created via createRequirementStatusPlugin()
  // and passed to createSystem({ plugins: [statusPlugin.plugin] })

  // Track the loading state of a specific requirement type
  const status = useRequirementStatus(statusPlugin, 'FETCH_USER');
</script>

{#if $status.isLoading}
  <p>Loading...</p>
{:else if $status.hasError}
  <p>Error: {$status.lastError?.message}</p>
{:else}
  <p>User loaded</p>
{/if}

useOptimisticUpdate

Perform optimistic mutations with automatic rollback on failure. Returns a dispatch function and status.

function useOptimisticUpdate(
  system: System,
  statusPlugin?: StatusPlugin,
  requirementType?: string,
): OptimisticUpdateResult
interface OptimisticUpdateResult {
  mutate: (updateFn: () => void) => void;
  isPending: Readable<boolean>;
  error: Readable<Error | null>;
  rollback: () => void;
}

useOptimisticUpdate Usage

<script>
  import { useOptimisticUpdate } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Set up optimistic mutations with automatic rollback
  const { mutate, isPending, error, rollback } = useOptimisticUpdate(system, statusPlugin, 'UPDATE_ITEM');

  function updateName(name) {
    // Optimistically update facts; rolls back automatically on resolver failure
    mutate(() => {
      system.facts.item = { ...system.facts.item, name };
    });
  }
</script>

<input on:change={(e) => updateName(e.target.value)} />
{#if $isPending}<span>Saving...</span>{/if}
{#if $error}<span>Failed: {$error.message}</span>{/if}

useDirective

Create a scoped system tied to the component lifecycle. Two modes:

  • Selective – pass facts and/or derived keys to subscribe to specific state
  • Subscribe all – omit keys to subscribe to all facts and derivations
function useDirective<M extends ModuleSchema>(
  moduleDef: ModuleDef<M>,
  config?: {
    facts?: string[];
    derived?: string[];
    plugins?: Plugin[];
    debug?: DebugConfig;
    errorBoundary?: ErrorBoundaryConfig;
    tickMs?: number;
    zeroConfig?: boolean;
    initialFacts?: Record<string, any>;
    status?: boolean;
  },
): {
  system: System<M>;
  facts: Readable<InferFacts<M>>;
  derived: Readable<InferDerivations<M>>;
  events: System<M>["events"];
  dispatch: (event: InferEvents<M>) => void;
  statusPlugin?: StatusPlugin;
}

useDirective Usage

<script>
  import { useDirective } from '@directive-run/svelte';
  import { counterModule } from './modules/counter';

  // Subscribe all: omit keys for everything
  const { facts, derived, events, dispatch } = useDirective(counterModule);
</script>

<p>Count: {$facts.count}</p>
<p>Doubled: {$derived.doubled}</p>
<button on:click={() => events.increment()}>+</button>

Selective subscriptions:

<script>
  import { useDirective } from '@directive-run/svelte';
  import { counterModule } from './modules/counter';

  // Selective: subscribe to specific keys only
  const { facts, derived, dispatch } = useDirective(counterModule, {
    facts: ['count'],
    derived: ['doubled'],
  });
</script>

<p>Count: {$facts.count}</p>
<button on:click={() => dispatch({ type: 'increment' })}>+</button>

createTypedHooks

Factory that creates fully typed hooks for a specific module schema. The returned hooks still take system as the first parameter but provide full autocomplete for keys and events.

function createTypedHooks<M extends ModuleSchema>(): {
  useFact: <K extends keyof InferFacts<M>>(system: System, factKey: K) => Readable<InferFacts<M>[K] | undefined>;
  useDerived: <K extends keyof InferDerivations<M>>(system: System, derivationId: K) => Readable<InferDerivations<M>[K]>;
  useDispatch: (system: System) => (event: InferEvents<M>) => void;
  useEvents: (system: System) => System<M>["events"];
}

createTypedHooks Usage

// hooks.ts
import { createTypedHooks } from '@directive-run/svelte';
import type { MyModuleSchema } from './modules/my-module';

// Create typed hooks – full autocomplete for fact keys and event types
export const {
  useFact,
  useDerived,
  useEvents,
} = createTypedHooks<MyModuleSchema>();
<script>
  import { useFact, useEvents } from './hooks';
  import { system } from '$lib/directive';

  // Fully typed – fact key autocompletes, return type inferred
  const count = useFact(system, 'count');

  // Typed event dispatchers
  const events = useEvents(system);
</script>

Store Factories

Create Svelte Readable stores outside of components. These take the system as a parameter and can be used in plain .ts files.

createFactStore

function createFactStore<K extends string>(
  system: SingleModuleSystem<any>,
  key: K,
): Readable<InferFacts<S>[K]>

createDerivedStore

function createDerivedStore<K extends string>(
  system: SingleModuleSystem<any>,
  key: K,
): Readable<InferDerivations<S>[K]>

createDerivedsStore

function createDerivedsStore(
  system: SingleModuleSystem<any>,
  keys: string[],
): Readable<Record<string, unknown>>

createInspectStore

function createInspectStore(
  system: SingleModuleSystem<any>,
): Readable<SystemInspection>

Store Factories Usage

// stores.ts – create stores outside of components
import { createFactStore, createDerivedStore } from '@directive-run/svelte';

// Subscribe to a single fact as a Readable store
export const count$ = createFactStore(system, 'count');

// Subscribe to a derivation as a Readable store
export const total$ = createDerivedStore(system, 'cartTotal');
<script>
  import { count$, total$ } from './stores';
</script>

<p>Count: {$count$}, Total: {$total$}</p>

useTimeTravel

Reactive time-travel state. Returns a Readable store containing the time-travel controls, or null when time-travel is disabled.

function useTimeTravel(system: System): Readable<TimeTravelState | null>
interface TimeTravelState {
  canUndo: boolean;
  canRedo: boolean;
  undo: () => void;
  redo: () => void;
  currentIndex: number;
  totalSnapshots: number;
}

useTimeTravel Usage

<script>
  import { useTimeTravel } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Get reactive time-travel controls (null when disabled)
  const tt = useTimeTravel(system);
</script>

{#if $tt}
  <button on:click={$tt.undo} disabled={!$tt.canUndo}>Undo</button>
  <button on:click={$tt.redo} disabled={!$tt.canRedo}>Redo</button>
  <span>{$tt.currentIndex + 1} / {$tt.totalSnapshots}</span>
{/if}

Enable time-travel in the system configuration:

// Enable time-travel debugging on the system
const system = createSystem({
  module: myModule,
  debug: { timeTravel: true, maxSnapshots: 100 },
});

shallowEqual

Utility function for shallow equality comparison. Useful as an equalityFn for selectors to prevent unnecessary store updates.

function shallowEqual(a: unknown, b: unknown): boolean

shallowEqual Usage

<script>
  import { useSelector, shallowEqual } from '@directive-run/svelte';
  import { system } from '$lib/directive';

  // Use shallowEqual to prevent updates when values haven't changed
  const summary = useSelector(
    system,
    (facts) => ({ name: facts.name, count: facts.count }),
    shallowEqual,
  );
</script>

<p>{$summary.name}: {$summary.count}</p>

Next Steps

Previous
Vue Composables

We care about your data. We'll never share your email.

Powered by Directive. This signup uses a Directive module with facts, derivations, constraints, and resolvers – zero useState, zero useEffect. Read how it works

Directive - Constraint-Driven State Management for TypeScript