# How to Use Storybook Preview API Hooks: A Complete Guide to useArgs, useState, and More

> Master Storybook Preview API hooks like useArgs, useState, and useEffect. Manage state, side effects, and story config for enhanced component development with this comprehensive guide.

- Repository: [Storybook/storybook](https://github.com/storybookjs/storybook)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Storybook Preview API hooks let you manage state, side effects, and story configuration inside story functions and decorators using React-compatible APIs like `useState`, `useArgs`, and `useEffect`, all implemented through a custom `HooksContext` in the preview-api package.**

Storybook's preview-api package provides a React-style hook system that runs inside the story rendering lifecycle. These hooks allow you to manipulate story arguments, global parameters, and local component state without leaving the story function context. All hooks are implemented in [`code/core/src/preview-api/modules/addons/hooks.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/hooks.ts) and re-exported from `storybook/preview-api`.

## What Are Storybook Preview API Hooks?

Storybook Preview API hooks are a custom implementation that mimics React's hook API but is designed specifically for Storybook's story execution context. Unlike standard React hooks that run inside React components, these hooks operate within story functions and decorators, giving you access to Storybook's internal channel, state management, and story metadata.

The hooks are backed by a `HooksContext` class that tracks the current render phase (`MOUNT`, `UPDATE`, or `NONE`), manages hook state across re-renders, and synchronizes updates through Storybook's event channel.

## Complete List of Preview API Hooks

The following table lists every hook available in the preview-api package, its purpose, and its location in the source code:

| Hook | Purpose | Core Implementation |
|------|---------|----------------------|
| `useArgs` | Read and update story args | [hooks.ts: 998‑1034](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L998-L1034) |
| `useCallback` | Memoize a callback function | [hooks.ts: 43‑45](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L43-L45) |
| `useChannel` | Subscribe/unsubscribe to channel events | [hooks.ts: 14‑16, 30‑33](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L14-L16) |
| `useEffect` | Register side‑effects that run after render | [hooks.ts: 98‑104](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L98-L104) |
| `useGlobals` | Read and update global args | [hooks.ts: 49‑51](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L49-L51) |
| `useMemo` | Memoize a computed value | [hooks.ts: 22‑24](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L22-L24) |
| `useParameter` | Retrieve a story parameter (or default) | [hooks.ts: 86‑88](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L86-L88) |
| `useReducer` | State management with a reducer function | [hooks.ts: 57‑59](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L57-L59) |
| `useRef` | Mutable ref object that survives re‑renders | [hooks.ts: 41‑43](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L41-L43) |
| `useState` | Simple state holder | [hooks.ts: 34‑36](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L34-L36) |
| `useStoryContext` | Access the full story context (id, parameters, args, globals, etc.) | [hooks.ts: 60‑62](https://github.com/storybookjs/storybook/blob/next/code/core/src/preview-api/modules/addons/hooks.ts#L60-L62) |

## Importing Hooks from storybook/preview-api

All preview hooks are re-exported from the main entry point. Import them directly from `'storybook/preview-api'`:

```typescript
import {
  useArgs,
  useCallback,
  useChannel,
  useEffect,
  useGlobals,
  useMemo,
  useParameter,
  useReducer,
  useRef,
  useState,
  useStoryContext,
} from 'storybook/preview-api';

```

The export map is defined in [`code/core/src/preview-api/index.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/index.ts), which aggregates the hook implementations from [`code/core/src/preview-api/modules/addons/hooks.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/hooks.ts).

## State Management Hooks

The preview API provides standard React-compatible hooks for managing local state within stories.

### useState

`useState` stores values in a mutable ref and triggers re-renders via the internal update cycle. It is implemented in [`hooks.ts`](https://github.com/storybookjs/storybook/blob/main/hooks.ts) at lines 34-36.

```tsx
import { useState } from 'storybook/preview-api';

export const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <>
      <p>Current count: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>Increase</button>
    </>
  );
};

```

### useReducer

For complex state logic, `useReducer` (lines 57-59 in [`hooks.ts`](https://github.com/storybookjs/storybook/blob/main/hooks.ts)) provides a reducer pattern similar to React.

```tsx
import { useReducer } from 'storybook/preview-api';

type Action = { type: 'inc' } | { type: 'dec' };
const reducer = (state: number, action: Action) => {
  switch (action.type) {
    case 'inc': return state + 1;
    case 'dec': return state - 1;
    default: return state;
  }
};

export const CounterWithReducer = () => {
  const [count, dispatch] = useReducer(reducer, 0);
  return (
    <>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'inc' })}>+</button>
      <button onClick={() => dispatch({ type: 'dec' })}>‑</button>
    </>
  );
};

```

### useRef

`useRef` (lines 41-43) creates a mutable container that persists across re-renders without triggering updates.

```tsx
import { useRef, useState } from 'storybook/preview-api';

export const RefExample = () => {
  const countRef = useRef(0);
  const [, forceRender] = useState(0);
  const increment = () => {
    countRef.current += 1;
    forceRender((n) => n + 1);
  };
  return <button onClick={increment}>Ref count: {countRef.current}</button>;
};

```

### useMemo

For expensive calculations, `useMemo` (lines 22-24) caches computed values based on dependency arrays.

```tsx
import { useMemo, useState } from 'storybook/preview-api';

export const ExpensiveCalc = () => {
  const [num, setNum] = useState(0);
  const result = useMemo(() => {
    let sum = 0;
    for (let i = 0; i < 1e6; i++) sum += Math.sqrt(num + i);
    return sum;
  }, [num]);

  return (
    <>
      <p>Result: {result}</p>
      <input type="number" value={num} onChange={(e) => setNum(+e.target.value)} />
    </>
  );
};

```

### useCallback

`useCallback` (lines 43-45) memoizes callback functions to maintain stable references across renders.

```tsx
import { useCallback, useState } from 'storybook/preview-api';

export const StableHandler = () => {
  const [value, setValue] = useState(0);
  const onClick = useCallback(() => setValue((v) => v + 1), []);
  return <button onClick={onClick}>Clicked {value} times</button>;
};

```

## Story Configuration Hooks

These hooks connect your story to Storybook's arg system, global parameters, and story-specific parameters.

### useArgs

`useArgs` (lines 998-1034) allows stories to read and modify their own arguments, triggering Storybook's controls to update.

```tsx
import { useArgs } from 'storybook/preview-api';

export const ArgsDemo = (args) => {
  const [{ label }, updateArgs] = useArgs<{ label: string }>();
  return (
    <>
      <p>Label prop: {label}</p>
      <button onClick={() => updateArgs({ label: 'Updated!' })}>Change label</button>
    </>
  );
};
ArgsDemo.args = { label: 'Initial' };

```

### useGlobals

`useGlobals` (lines 49-51) provides access to global Storybook state, such as theme or locale settings, across all stories.

```tsx
import { useGlobals } from 'storybook/preview-api';

export const GlobalsDemo = () => {
  const [globals, updateGlobals] = useGlobals();
  return (
    <>
      <p>Current theme: {globals.theme ?? 'light'}</p>
      <button onClick={() => updateGlobals({ theme: 'dark' })}>Dark mode</button>
    </>
  );
};

```

### useParameter

`useParameter` (lines 86-88) retrieves story-specific parameters with an optional default value.

```tsx
import { useParameter } from 'storybook/preview-api';

export const ParameterDemo = () => {
  const myParam = useParameter<string>('myParam', 'fallback');
  return <p>Parameter "myParam": {myParam}</p>;
};
ParameterDemo.parameters = { myParam: 'Hello world' };

```

## Side Effects and Event Handling

Manage asynchronous operations and communicate with the Storybook manager using these hooks.

### useEffect

`useEffect` (lines 98-104) registers side effects that run after the story renders, supporting cleanup functions.

```tsx
import { useEffect, useState } from 'storybook/preview-api';

export const EffectDemo = () => {
  const [time, setTime] = useState(Date.now());
  useEffect(() => {
    const id = setInterval(() => setTime(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);

  return <p>Current time: {new Date(time).toLocaleTimeString()}</p>;
};

```

### useChannel

`useChannel` (lines 14-16, 30-33) subscribes to custom events on the Storybook channel and returns an emitter function.

```tsx
import { useChannel } from 'storybook/preview-api';

export const ChannelDemo = () => {
  const emit = useChannel({
    'my-event': (msg) => console.log('Received:', msg),
  });

  return <button onClick={() => emit('my-event', 'Hello from story')}>Send Event</button>;
};

```

## Accessing Story Metadata with useStoryContext

`useStoryContext` (lines 60-62) exposes the full story context including ID, title, args, and parameters.

```tsx
import { useStoryContext } from 'storybook/preview-api';

export const ContextDemo = () => {
  const { id, title, args } = useStoryContext();
  return (
    <pre>{JSON.stringify({ id, title, args }, null, 2)}</pre>
  );
};

```

## Architecture and Implementation Details

Understanding how the preview API hooks work under the hood helps debug complex story interactions and write efficient decorators.

### HooksContext and the Hookify Pattern

The core engine resides in [`code/core/src/preview-api/modules/addons/hooks.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/hooks.ts). The `HooksContext` class maintains:
- **`hookListsMap`**: A `WeakMap` linking story functions to their hook lists
- **`currentHooks`**: The active hook list during rendering
- **`nextHookIndex`**: Cursor tracking the current hook position
- **`currentPhase`**: Either `MOUNT`, `UPDATE`, or `NONE`
- **`currentEffects` / `prevEffects`**: Queues for effect scheduling

When a story executes, `applyHooks` wraps it with `hookify`, which swaps the global `STORYBOOK_HOOKS_CONTEXT` to the current `HooksContext`, runs the function, then restores the previous context. This mirrors React's render-with-current-context pattern.

### Hook Lifecycle and Re-Rendering

During the **MOUNT** phase, each hook call creates a new `Hook` object and pushes it onto `currentHooks`. The callback executes immediately. During **UPDATE**, `useHook` pulls the next stored hook with `getNextHook`, validates ordering, and only re-executes callbacks when dependencies differ.

State-changing hooks (`useState`, `useReducer`, `useArgs`, `useGlobals`) call `triggerUpdate`, which sets `hasUpdates = true`. `applyHooks` then re-executes the story until no updates remain or the `RENDER_LIMIT` (25) is exceeded, preventing infinite loops.

### Effect Handling and Cleanup

`useEffect` (lines 98-104) registers an `Effect` object. After rendering completes, `HooksContext.triggerEffects` runs the `create` function for new effects and calls `destroy` for removed ones. This guarantees deterministic cleanup analogous to React's effect model.

### Channel Communication

State-changing hooks emit events on the global Storybook channel:
- `UPDATE_STORY_ARGS` – dispatched by `useArgs`
- `RESET_STORY_ARGS` – dispatched by `useArgs`
- `UPDATE_GLOBALS` – dispatched by `useGlobals`
- `FORCE_RE_RENDER` – emitted when updates occur outside render cycles

The channel listens for `STORY_RENDERED` to flush pending effects via `HooksContext.renderListener`.

### Error Guardrails

If a hook is called outside a story or decorator, `invalidHooksError` throws immediately, explaining that preview hooks must only be called within Storybook's execution context. This prevents accidental misuse in application code.

## Summary

- **Storybook Preview API hooks** provide React-compatible state management and side-effect handling inside story functions and decorators.
- All hooks are implemented in [`code/core/src/preview-api/modules/addons/hooks.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/hooks.ts) and exported from `storybook/preview-api`.
- **State hooks** (`useState`, `useReducer`, `useRef`, `useMemo`, `useCallback`) work identically to React but use `HooksContext` instead of React's internal dispatcher.
- **Configuration hooks** (`useArgs`, `useGlobals`, `useParameter`) connect stories to Storybook's args and globals system, emitting channel events on changes.
- **Effect hooks** (`useEffect`, `useChannel`) handle side effects and cross-window communication through the Storybook channel.
- **Context hook** (`useStoryContext`) exposes full story metadata including ID, title, and parameters.
- The system prevents infinite loops with a `RENDER_LIMIT` of 25 and throws `invalidHooksError` when hooks are called outside Storybook's render cycle.

## Frequently Asked Questions

### Can I use preview API hooks outside of Storybook stories?

No. Preview API hooks rely on `HooksContext`, which is only initialized when Storybook renders a story or decorator. If you call these hooks outside this context, `invalidHooksError` throws immediately. For regular React components, use standard React hooks instead.

### How do preview hooks differ from React hooks?

While the API is identical (same function signatures and dependency array behavior), Storybook's preview hooks use a custom `HooksContext` class instead of React's internal fiber architecture. This context tracks render phases (`MOUNT` vs `UPDATE`), manages a `hookListsMap` for each story function, and synchronizes state changes through Storybook's event channel rather than React's re-render cycle.

### Why does my story re-render when I call `updateArgs`?

`useArgs` dispatches `UPDATE_STORY_ARGS` on the Storybook channel, which triggers a full story re-render to reflect the new argument values. This is intentional: args are the primary mechanism for controls, and changes must propagate to the component. To prevent unnecessary re-renders, ensure your component is memoized or use `useMemo` inside the story to stabilize derived values.

### Can I use these hooks in decorators?

Yes. Preview API hooks work in both story functions and decorators because both execute within the `HooksContext` wrapper. Decorators often use `useGlobals` to read theme settings or `useParameter` to access configuration. When using hooks in decorators, remember they execute before the story, so state set in a decorator will be available when the story function runs.