HomeAbout Me

Advanced React Patterns: Control Props

By Daniel Nguyen
Published in React JS
July 09, 2025
2 min read
Advanced React Patterns: Control Props

šŸŽ›ļø Control Props in React: Letting Users Take the Wheel

One-liner: The Control Props pattern gives users full control over your component’s internal state—just like controlled form elements in React.

Let’s talk about a powerful pattern that will take your component APIs to the next level of flexibility: Control Props.

Imagine this: you’re building a reusable component. You want it to work out of the box, but you also want to give developers the option to control its behavior completely—from the outside.

Sound familiar?

It should. You’ve already seen this pattern in action a thousand times:

<input value={value} onChange={handleChange} />

This is a controlled input element. You provide the value, and React calls your onChange to ā€œsuggestā€ a new one.

Let’s take that same concept… and apply it to any reusable component.


🧩 A Quick Refresher on Controlled Inputs

Controlled inputs are the basis of most React forms:

function MyCapitalizedInput() {
const [capitalizedValue, setCapitalizedValue] = useState("")
return (
<input
value={capitalizedValue}
onChange={(e) => setCapitalizedValue(e.target.value.toUpperCase())}
/>
)
}

This is a classic example of using control props (value and onChange). The input doesn’t manage its own state anymore — you do.

Why is this so powerful?

  • šŸ”„ You can sync the input value with other components
  • šŸŽÆ You can transform user input before storing it
  • āš™ļø You gain full control over the UI’s behavior

šŸ”„ Applying This to Custom Components

Let’s say we’ve built a <Toggle /> component. It’s a simple on/off switch.

Here’s the typical version where the component manages its own state:

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

Nice and simple. But what if someone wants to control that on state from the outside?

Now we need to implement control props:

function Toggle({ on: controlledOn, onChange }) {
const [internalOn, setInternalOn] = useState(false)
const isControlled = controlledOn !== undefined
const on = isControlled ? controlledOn : internalOn
function toggle() {
const newState = !on
if (isControlled) {
onChange?.(newState)
} else {
setInternalOn(newState)
onChange?.(newState)
}
}
return <button onClick={toggle}>{on ? "On" : "Off"}</button>
}

🧠 Breakdown:

  • If on is passed in → the component is controlled

  • If on is undefined → the component manages state internally

  • When toggled →

    • In controlled mode: we call onChange with the suggested next state
    • In uncontrolled mode: we update internal state and still call onChange

This gives users two options:

  1. Use <Toggle /> like a normal self-contained component
  2. Or pass in on and onChange to fully manage its state externally

✨ Real-World Example: Synchronizing Inputs

What if we want two components to always reflect the same value?

function MyTwoInputs() {
const [value, setValue] = useState("")
function handleChange(e) {
setValue(e.target.value)
}
return (
<>
<input value={value.toUpperCase()} onChange={handleChange} />
<input value={value.toLowerCase()} onChange={handleChange} />
</>
)
}

Using control props here makes it possible to synchronize behavior between components. This is especially powerful when building design systems or shared UI libraries.


🧪 In Practice: Making Toggle Controlled

Back to our <Toggle />:

Your exercise is to make it behave like a controlled component. Specifically, you should:

  • Accept an on prop
  • Accept an onChange callback
  • Internally track state only if on is not provided
  • When toggled, always call onChange with the proposed next value

This will give consumers full flexibility:

  • Use the component ā€œas-isā€ without needing to manage state
  • Or pass in on and onChange to take control themselves

šŸ› ļø Real Projects That Use This Pattern

The Control Props pattern is used in many popular UI libraries, including:

  • downshift for autocomplete & dropdowns
  • @radix-ui/react-select for customizable select menus

These libraries rely on control props to give users the power to integrate deeply with their own state and data.


āœ… Summary

The Control Props pattern:

āœ… FeaturešŸ” Benefit
User-controlled stateLets devs hook your component into their own app logic
Bidirectional syncGreat for syncing values across components
Declarative flexibilityMakes your API more powerful without adding complexity

If you’re building a reusable component and want to let developers fully manage or sync state, then the Control Props pattern is exactly what you need.


🧠 Final Thought

Control Props is more than just a fancy name — it’s about making components flexible enough to be used in real-world, complex situations while still being dead simple for the easy cases.

The best APIs do both. šŸ’Ŗ



Tags

#ReactPatterns

Share

Previous Article
Advanced React Patterns: State Reducer

Table Of Contents

1
🧩 A Quick Refresher on Controlled Inputs
2
šŸ”„ Applying This to Custom Components
3
✨ Real-World Example: Synchronizing Inputs
4
🧪 In Practice: Making Toggle Controlled
5
šŸ› ļø Real Projects That Use This Pattern
6
āœ… Summary

Related Posts

Advanced React Patterns: State Reducer
July 08, 2025
2 min
Ā© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media