Skip to main content

What does the "Control Props" pattern do?

The Control Props pattern is one of the most interesting and powerful architectural techniques in React, which lets you make components both "controlled" and "uncontrolled", at the user's choice.

This pattern underlies many libraries (for example, React Select, Downshift, Headless UI, Radix UI, and others), because it gives flexibility in managing a component's state.

Let's break it down step by step.


Definition

The Control Props Pattern is a pattern in which a component can work in two modes:

  • Uncontrolled (internal) - manages its own state
  • Controlled (external) - accepts state and callbacks via props, handing control outward

In other words:

The component hands the "levers of control" to the consumer, but if the consumer doesn't take them, the component works on its own.


Example: a toggle switch

Without the pattern (internal state only)

javascript
function Toggle() { const [on, setOn] = useState(false); return ( <button onClick={() => setOn(!on)}> {on ? "ON" : "OFF"} </button> ); }

The component works, but it always manages itself. We cannot control the on state from the outside.


With Control Props

javascript
function Toggle({ on: controlledOn, onChange }) { const [internalOn, setInternalOn] = useState(false); // If the "on" prop is passed, use it; otherwise use internal state const isControlled = controlledOn !== undefined; const on = isControlled ? controlledOn : internalOn; const toggle = () => { const newState = !on; if (!isControlled) setInternalOn(newState); // internal control onChange?.(newState); // notify the outside world }; return <button onClick={toggle}>{on ? "ON" : "OFF"}</button>; }

Usage

Internally managed (uncontrolled)

javascript
<Toggle />

The component stores the state itself.


Externally managed (controlled)

javascript
function App() { const [on, setOn] = useState(false); return ( <Toggle on={on} onChange={setOn} /> ); }

Now the parent controls the on state, and Toggle just "signals" the changes.


What happens

VariantWho manages the stateWhere the value is stored
UncontrolledThe component itselfIn useState inside it
ControlledThe parentIn the parent's useState
Both variantsDetermined automatically by propsDepends on whether on is present

Why Control Props is needed

ProblemHow it's solved
You want a component that works "out of the box" but can be customizedLets you control the state from the outside when needed
You need a component that can be either "managed" or "autonomous"The same code supports both modes
You want to plug the component into a complex form or state managerPass it value and onChange, and it becomes "controlled"
You don't want to lose the convenience of simple usageWithout props it still works on its own

Another example: with Control Props

javascript
function Input({ value: controlledValue, onChange, defaultValue = "" }) { const [internalValue, setInternalValue] = useState(defaultValue); const isControlled = controlledValue !== undefined; const value = isControlled ? controlledValue : internalValue; const handleChange = (e) => { if (!isControlled) setInternalValue(e.target.value); onChange?.(e.target.value); }; return <input value={value} onChange={handleChange} />; }

Usage:

javascript
// Uncontrolled <Input defaultValue="Hello" /> // Controlled const [name, setName] = useState(""); <Input value={name} onChange={setName} />

One component, two modes of operation. That's the essence of the Control Props Pattern.


Important principles

  1. Decide which props can be "controllable" For example, value, checked, open, selected, expanded, etc.
  2. Add a callback for notification For example, onChange, onToggle, onSelect.
  3. Internal state must stay synchronized with external state If the prop is passed, use it. If not, use your own useState.
  4. Be predictable A component should not suddenly switch behavior between "controlled" and "uncontrolled" during its lifetime.

Control via a "control prop"

javascript
<Toggle on={true} /> // fully controlled by the parent <Toggle /> // managed internally <Toggle onChange={handler} /> // works in both scenarios

Where this pattern is used

LibraryComponentControl Prop
React, value / onChange
React-Selectvalue / onChange
DownshiftisOpen, selectedItem, inputValue
Radix UI / Headless UI, open / onOpenChange

Practically every stateful UI component is implemented via the Control Props Pattern.


Summary

WhatDescription
IdeaLet a component be either "controlled" or "uncontrolled"
How it worksIf a prop (for example value or on) is passed, control shifts to the parent
GoalFlexibility: the component works both on its own and as part of complex systems
Example
Used inInputs, Dialogs, Dropdowns, Comboboxes, Checkboxes
AnalogyThe component says: "You can control me, or you can let me control myself."

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.