Suggest an editImprove this articleRefine the answer for “What does the "Control Props" pattern do?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)The **Control Props pattern** is an architectural technique in React that lets you make components both "controlled" and "uncontrolled", at the user's choice. **Key point:** the component hands the "levers of control" to the consumer, but if the consumer doesn't take them, the component works on its own.Shown above the full answer for quick recall.Answer (EN)ImageThe **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 | Variant | Who manages the state | Where the value is stored | |---|---|---| | Uncontrolled | The component itself | In useState inside it | | Controlled | The parent | In the parent's useState | | Both variants | Determined automatically by props | Depends on whether on is present | --- ## Why Control Props is needed | Problem | How it's solved | |---|---| | You want a component that works "out of the box" but can be customized | Lets 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 manager | Pass it value and onChange, and it becomes "controlled" | | You don't want to lose the convenience of simple usage | Without props it still works on its own | --- ## Another example: <Input> 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 | Library | Component | Control Prop | |---|---|---| | React | <input>, <textarea> | value / onChange | | React-Select | <Select> | value / onChange | | Downshift | <Combobox> | isOpen, selectedItem, inputValue | | Radix UI / Headless UI | <Dialog>, <Popover> | open / onOpenChange | Practically every stateful UI component is implemented via the Control Props Pattern. --- ## Summary | What | Description | |---|---| | Idea | Let a component be either "controlled" or "uncontrolled" | | How it works | If a prop (for example value or on) is passed, control shifts to the parent | | Goal | Flexibility: the component works both on its own and as part of complex systems | | Example | <Toggle on={...} onChange={...} /> | | Used in | Inputs, Dialogs, Dropdowns, Comboboxes, Checkboxes | | Analogy | The component says: "You can control me, or you can let me control myself." |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.