---
title: useControllableState
description: Support both controlled and uncontrolled usage of a stateful value from a single hook.
icon: sliders-horizontal
author:
  name: Radix UI
  url: https://github.com/radix-ui/primitives
---

### Installation

```package-install
npx shadcn@latest add @redpanda/use-controllable-state
```

`useControllableState` lets a component work in **either** controlled or uncontrolled mode from one hook. When a `prop` is provided the value is controlled and changes are reported through `onChange`; when it's omitted the hook holds its own state seeded from `defaultProp`. This is the pattern behind `value`/`defaultValue` pairs across the registry's form and disclosure components.

In development it warns if a component flips between controlled and uncontrolled between renders.

## Usage

```tsx showLineNumbers
import { useControllableState } from "@/hooks/use-controllable-state";

function Toggle({
  pressed,
  defaultPressed = false,
  onPressedChange,
}: {
  pressed?: boolean;
  defaultPressed?: boolean;
  onPressedChange?: (pressed: boolean) => void;
}) {
  const [value, setValue] = useControllableState({
    prop: pressed,
    defaultProp: defaultPressed,
    onChange: onPressedChange,
    caller: "Toggle",
  });

  return (
    <button type="button" aria-pressed={value} onClick={() => setValue((p) => !p)}>
      {value ? "On" : "Off"}
    </button>
  );
}
```

## API

### `useControllableState<T>({ prop, defaultProp, onChange?, caller? })`

| Option        | Type                 | Description                                                                              |
| ------------- | -------------------- | ---------------------------------------------------------------------------------------- |
| `prop`        | `T \| undefined`     | The controlled value. When defined, the hook is controlled; when `undefined`, uncontrolled. |
| `defaultProp` | `T`                  | Initial value used in uncontrolled mode.                                                 |
| `onChange`    | `(state: T) => void` | Called whenever the value changes (in both modes).                                       |
| `caller`      | `string`             | Component name used in the dev-only controlled/uncontrolled warning.                     |

**Returns** `[value, setValue]` — a `useState`-compatible tuple. `setValue` accepts a next value or an updater function.

## Credits

Adapted from [Radix UI's `useControllableState`](https://github.com/radix-ui/primitives/blob/main/packages/react/use-controllable-state/src/use-controllable-state.ts).
