alepha@docs:~/docs/guides/frontend$
cat 3-state-management.md
2 min read
Last commit:

#State Management

Alepha provides $atom for defining typed, validated state and useStore for consuming it in React components. State is a simple key-value store with schema validation, SSR hydration, and event-driven updates.

#Defining Atoms

Atoms are defined at the module level using $atom. Each atom has a unique name, a TypeBox schema, and an optional default value.

typescript
1import { $atom, t } from "alepha";2 3const counter = $atom({4  name: "app:counter",5  schema: t.object({6    count: t.integer(),7  }),8  default: { count: 0 },9});

Atoms must contain only serializable data. Avoid storing class instances, functions, or DOM elements.

Options:

Option Type Description
name string Unique identifier for the atom.
schema TObject TypeBox schema for validation.
default Static<T> Default value. Required unless schema is fully optional.
description string Optional description for documentation.

#useStore Hook

The useStore hook connects React components to Alepha state. It returns a [value, setValue] tuple, similar to useState.

typescript
 1import { useStore } from "alepha/react"; 2  3function Counter() { 4  const [state, setState] = useStore(counter); 5  6  return ( 7    <button onClick={() => setState({ count: state.count + 1 })}> 8      Count: {state.count} 9    </button>10  );11}

When the state changes (from any source), the component re-renders automatically.

#Using with Atom

typescript
1const [value, setValue] = useStore(counter);

#Using with Store Keys

You can also access state by string key, without an atom:

typescript
1const [lang, setLang] = useStore("alepha.react.i18n.lang");

This is used internally by Alepha modules (e.g., i18n, router) for framework-level state.

#Default Values

Pass a default value as the second argument. It is applied only if the current value is null or undefined:

typescript
1const [prefs, setPrefs] = useStore(userPrefs, { theme: "light" });

#Non-React Access

Outside of React components, use alepha.store directly:

typescript
1// Read2const value = alepha.store.get(counter);3 4// Write5alepha.store.set(counter, { count: 42 });

Setting a value triggers the state:mutate event, which causes any useStore subscribers to re-render.

#SSR Hydration

Atoms participate in SSR automatically. On the server, state is serialized into the HTML response. On the client, the state is hydrated before React renders, so components see the server-set values on first render without flicker.

#Event System

State mutations emit a state:mutate event:

typescript
1alepha.events.on("state:mutate", ({ key, value }) => {2  console.log(`State changed: ${key}`, value);3});

This is how useStore knows when to re-render -- it listens for mutations matching its atom key.

#Example: Feature Flags

typescript
 1import { $atom, t } from "alepha"; 2import { useStore } from "alepha/react"; 3  4const featureFlags = $atom({ 5  name: "app:features", 6  schema: t.object({ 7    darkMode: t.boolean(), 8    betaFeatures: t.boolean(), 9  }),10  default: {11    darkMode: false,12    betaFeatures: false,13  },14});15 16function FeatureToggle() {17  const [flags, setFlags] = useStore(featureFlags);18 19  return (20    <label>21      <input22        type="checkbox"23        checked={flags.darkMode}24        onChange={() => setFlags({ ...flags, darkMode: !flags.darkMode })}25      />26      Dark Mode27    </label>28  );29}