# useUncontrolled (/docs/hooks/use-uncontrolled)





`useUncontrolled` manages the state of a value that can be either controlled or uncontrolled — the same pattern the kit's own form components (`Switch`, `Checkbox`, `Combobox`, ...) use under the hood. Reach for it when building a custom component that should support both `value`/`onChange` (controlled) and `defaultValue` (uncontrolled) the way a native `<input>` does.

```tsx
import { useUncontrolled } from '@42-central/ui-react';

function Toggle({ value, defaultValue, onChange }) {
  const [checked, setChecked] = useUncontrolled({
    value,
    defaultValue,
    finalValue: false,
    onChange,
  });

  return <button onClick={() => setChecked((c) => !c)}>{checked ? 'On' : 'Off'}</button>;
}
```

The state is controlled whenever `value` is provided — `defaultValue` is ignored in that case — and uncontrolled otherwise. `null` is a valid value; use it instead of `undefined` when a controlled value is optional, since `undefined` is what makes the component fall back to uncontrolled mode.

## Usage [#usage]

The same custom toggle component, shown once uncontrolled (manages its own state) and once controlled (state lives in the parent).

<Preview code="&#x22;function Toggle({ value, defaultValue, onChange }) {\n  const [checked, setChecked] = useUncontrolled({ value, defaultValue, finalValue: false, onChange });\n  return <button onClick={() => setChecked((c) => !c)}>{checked ? 'On' : 'Off'}</button>;\n}\n\n// Uncontrolled\n<Toggle defaultValue={false} />\n\n// Controlled\nconst [value, setValue] = useState(false);\n<Toggle value={value} onChange={setValue} />&#x22;">
  <UncontrolledDemo />
</Preview>

## API [#api]

### Parameters [#parameters]

<TypeTable
  type="{
  value: {
    type: 'T',
    description: 'Value for controlled state. When provided, defaultValue is ignored.',
  },
  defaultValue: {
    type: 'T',
    description: 'Initial value for uncontrolled state.',
  },
  finalValue: {
    type: 'T',
    description:
      'Initial value for uncontrolled state when neither value nor defaultValue is provided.',
  },
  onChange: {
    type: '(value: T, ...payload: P) => void',
    description: 'Called with the resolved value on every change, controlled or not.',
  },
}"
/>

### Returns [#returns]

A `[value, setValue, isControlled]` tuple.

<TypeTable
  type="{
  value: {
    type: 'T',
    required: true,
    description: 'The current value.',
  },
  setValue: {
    type: '(updater: T | ((previous: T) => T), ...payload: P) => void',
    required: true,
    description:
      'Accepts a value or an updater function, resolves it against the current value, then passes the resolved value and payload to onChange.',
  },
  isControlled: {
    type: 'boolean',
    required: true,
    description: 'True if the state is controlled, false if uncontrolled.',
  },
}"
/>
