Skip to content
shell
$ npx -y skills add agents-inc/skills --skill web-state-jotai --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/web-state-jotai
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

Atomic state management with auto-dependency tracking

SKILL.md

web-state-jotai.SKILL.md
name: web-state-jotai
description: Atomic state management with auto-dependency tracking

Jotai Atomic State Management

> **Quick Guide:** Jotai provides atomic, bottom-up state management with automatic dependency tracking. Define atoms at module level (never inside components). Use primitive atoms for values, derived atoms for computed state, write-only atoms for actions, and async atoms with Suspense for loading. Components only re-render when their specific atoms change. The `atomFamily` utility is deprecated -- use the `jotai-family` package for new code.

---

<critical_requirements>

CRITICAL: Before Using Jotai

**(You MUST define atoms OUTSIDE components -- creating atoms inside render causes broken state)**

**(You MUST wrap async atom consumers in Suspense boundaries -- async atoms trigger Suspense by default)**

**(You MUST use write atoms (action atoms) to encapsulate multi-atom updates and post-async state changes)**

**(You MUST use `jotai-family` package instead of deprecated `atomFamily` from `jotai/utils` for new code)**

</critical_requirements>

---

**Auto-detection:** Jotai, jotai, atom, useAtom, useAtomValue, useSetAtom, atomWithStorage, atomFamily, splitAtom, selectAtom, derived atom, loadable, unwrap, createStore, Provider store

**When to use:**

  • Fine-grained reactivity without manual memoization
  • Bottom-up state composition with automatic dependency tracking
  • Async state with React Suspense integration
  • Computed/derived state that auto-updates when dependencies change
  • Avoiding prop drilling for shared UI state

**Key patterns covered:**

  • Primitive, derived, write-only, and read-write atoms
  • Async atoms with Suspense and loadable/unwrap utilities
  • atomWithStorage for persistence
  • splitAtom for array item isolation
  • Store and Provider patterns for isolation and testing

**When NOT to use:**

  • Server/API data (use your data fetching solution)
  • Simple local component state (use useState)
  • State that should be in URL (use searchParams)

---

<philosophy>

Philosophy

Jotai takes an **atomic, bottom-up approach** to state management. The core principle: **"Anything that can be derived from the application state should be derived automatically."**

Consider atoms like cells in a spreadsheet:

  • Each atom is an independent cell holding a value
  • Derived atoms are formulas that reference other cells
  • When a cell changes, only formulas depending on it recalculate
  • This provides the minimum necessary work automatically

**Key characteristics:**

  • **Bottom-up state design**: Build state from small, composable atoms
  • **Automatic dependency tracking**: Atoms automatically update when dependencies change
  • **Fine-grained reactivity**: Components only re-render when their specific atoms change
  • **First-class async support**: Async operations integrate seamlessly with React Suspense

**Mental Model:** Instead of one large store, you have many small atoms that can be combined. This creates natural code splitting and enables precise re-render optimization without manual memoization.

</philosophy>

---

<patterns>

Core Patterns

Pattern 1: Primitive Atoms

The simplest atom type -- holds a single value with automatic type inference. Always define at module level.

import { atom } from "jotai";

const INITIAL_COUNT = 0;
const countAtom = atom(INITIAL_COUNT);
const userAtom = atom<User | null>(null);

export { countAtom, userAtom };

**Why good:** Module-level definition persists state, type inference works automatically, explicit typing only for unions/nullable

See [examples/core.md](examples/core.md) Pattern 1 for complete examples with type patterns.

---

Pattern 2: Derived (Read-Only) Atoms

Compute values from other atoms -- dependencies tracked automatically, cached until dependencies change.

const subtotalAtom = atom((get) => get(priceAtom) * get(quantityAtom));
const taxAtom = atom((get) => get(subtotalAtom) * get(taxRateAtom));
const totalAtom = atom((get) => get(subtotalAtom) + get(taxAtom));

**Why good:** Automatic dependency tracking, cached computation, chain of derivations is composable

See [examples/core.md](examples/core.md) Pattern 2 for derived atom chains and conditional derivations.

---

Pattern 3: Write-Only Atoms (Action Atoms)

Encapsulate side effects and multi-atom updates. First argument is `null` (no read value).

const resetAllAtom = atom(null, (get, set) => {
  set(countAtom, 0);
  set(itemsAtom, []);
  set(selectedAtom, null);
});

**Why good:** Actions are reusable, enables code splitting, multiple atoms updated atomically

See [examples/core.md](examples/core.md) Pattern 3 for action atoms with arguments.

---

Pattern 4: Read-Write Atoms

Atoms that can both read derived state and accept writes. Useful for lens-like property access on larger objects.

const nameAtom = atom(
  (get) => get(userAtom).name,
  (get, set, newName: string) => {
    set(userAtom, { ...get(userAtom), name: newName });
  },
);

**Why good:** Granular read/write access to object properties, keeps parent intact

See [examples/core.md](examples/core.md) Pattern 4 for lens patterns and transformations.

---

Pattern 5: Async Atoms with Suspense

Async atoms trigger Suspense by default. Use `loadable()` for manual loading states or `unwrap()` for fallback values.

// Triggers Suspense -- wrap consumer in <Suspense>
const userAtom = atom(async (get) => {
  const id = get(userIdAtom);
  const response = await fetch(`/api/users/${id}`);
  return response.json() as Promise<User>;
});

// Non-Suspense alternative
const loadableUserAtom = loadable(userAtom);
// Returns { state: 'loading' } | { state: 'hasData', data } | { state: 'hasError', error }

**Why good:** First-class Suspense integration, loadable provides type-safe discriminated union

See [examples/async.md](examples/async.md) for Suspense setup, loadable/unwrap patterns

Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin, auto-invoked