Controlled State

Learn how to use controlled state in Bits UI components.

Bits UI components offer flexibility in state management, allowing you to choose between uncontrolled and controlled states. This guide will help you understand when and how to use controlled state effectively.

Understanding State Management

Uncontrolled State (Default)

By default, Bits UI components operate in an uncontrolled state. In this mode:

  • The component internally manages its own state.
  • You can bind: to the state for reference.
  • The component decides when and how to update its state.
  • You can update the state of the component yourself from the outside, but you can't prevent the component from updating it.

Here's an example of an uncontrolled Accordion:

	<script lang="ts">
	import { Accordion } from "bits-ui";
	let myValue = $state("");
</script>
 
<Accordion.Root bind:value={myValue} type="single">
	<!-- Accordion content -->
</Accordion.Root>

In this example, the Accordion.Root component manages its value state internally. When a user interacts with the accordion, the component updates the value automatically. The local myValue is synced with the component's internal value state in both directions.

Controlled State

Controlled state puts you in charge of the component's state management. Use this approach when:

  • You need to meet specific conditions before state updates.
  • You want to synchronize the component's state with other parts of your application.
  • You require custom logic for state updates.

To implement controlled state:

  • Set the controlled<State> prop to true (e.g., controlledValue).
  • Pass a local state variable to the component.
  • Use the on<State>Change callback to update the local state (e.g., onValueChange).

Here's an example of how you might use controlled state with the Accordion component:

	<script lang="ts">
	import { Accordion } from "bits-ui";
 
	let myValue = $state("");
</script>
 
<Accordion.Root type="single" controlledValue value={myValue} onValueChange={(v) => (myValue = v)}>
	<!-- ... -->
</Accordion.Root>

In this controlled state example:

  • We set controlledValue to true.
  • We pass our local myValue state to the value prop.
  • We use onValueChange to handle state updates

Best Practices

  • Choose wisely: Use controlled state only when necessary. Uncontrolled state is simpler and sufficient for most use cases.
  • Consistent control: If you opt for controlled state, ensure you handle all related state updates to maintain consistency.
  • Performance consideration: Be mindful of potential performance impacts when using controlled state, especially with frequently updating components.

Common Controlled State Scenarios

  • Form validation before state updates
  • Syncing component state with external data sources
  • Implementing undo/redo functionality
  • Creating interdependent component behaviors