Sign in
Topics
Generate React Apps with Prompts or Figma
Controlled or uncontrolled—what’s the better fit for your form? This guide breaks down both React patterns with clear examples and practical tips, explaining when to use each for smoother form handling.
Handling user input in React can feel simple—until your form starts to grow. As fields multiply, so do the challenges around managing state, validation, and consistency.
What happens when you need full control over how data updates?
That’s where the decision between controlled and uncontrolled components becomes important. Choosing the right pattern can make your forms easier to maintain and extend.
This blog breaks down the difference between a controlled and an uncontrolled component. You'll see clear examples, learn common React patterns, and understand when each approach makes sense—all explained from the basics up to more advanced use cases.
In React, a controlled component is a form element whose input value is fully managed by React state. That means every keystroke or change updates the component’s state and re-renders it.
This setup gives you complete authority over the input element, making it predictable, testable, and dynamic.
1import React, { useState } from 'react'; 2 3function ControlledInput() { 4 const [name, setName] = useState(''); 5 6 const handleChange = (e) => { 7 setName(e.target.value); 8 }; 9 10 const handleSubmit = (e) => { 11 e.preventDefault(); 12 console.log('Submitted name:', name); 13 }; 14 15 return ( 16 <form onSubmit={handleSubmit}> 17 <input 18 type="text" 19 value={name} 20 onChange={handleChange} 21 /> 22 <button type="submit">Submit</button> 23 </form> 24 ); 25}
You can now implement form validation, error messages, and auto-save easily with controlled components.
Uncontrolled components work differently. They manage their state internally, just like traditional HTML form elements. Instead of using React’s state, we access the value through ref.
This is helpful when you don’t need to track every change in your input form field.
1import React, { useRef } from 'react'; 2 3function UncontrolledInput() { 4 const inputRef = useRef(); 5 6 const handleSubmit = (e) => { 7 e.preventDefault(); 8 console.log('Submitted name:', inputRef.current.value); 9 }; 10 11 return ( 12 <form onSubmit={handleSubmit}> 13 <input type="text" ref={inputRef} /> 14 <button type="submit">Submit</button> 15 </form> 16 ); 17}
Suitable for simple forms, minimal validation requirements, and cases where you need initial value without React reactivity.
This flowchart shows how controlled and uncontrolled components behave differently during input and form submission. One relies on React state, the other on DOM elements .
Feature | Controlled Components | Uncontrolled Components |
---|---|---|
State Source | React State | DOM |
React Control | Full control | Minimal control |
Input Value Access | From state | From ref |
Re-render on Change | Yes | No |
Suitable For | Complex Forms | Simple Forms |
Form Data Tracking | Real-time | On Submit |
Form Validation | Easy | Manual |
Initial Value | value prop | defaultValue |
In complex forms or apps requiring real-time updates, controlled components offer predictability. Since the form values are stored in the component’s state, the developer knows exactly what’s in the form at any point.
This makes it easier to:
Track changes
Implement form validation
Handle conditional logic
Write related unit tests
Tired of managing component state, refs, and form logic manually? Use Rocket.new to build fully functional React apps with just prompts—no coding required.
Imagine a multi-step signup form. You need to manage input values, validate fields, and show errors. Controlled components allow the parent component to control all form data centrally.
A single password input field that doesn’t need real-time validation? Use an uncontrolled component for quicker setup and better performance.
Use uncontrolled components in React when:
You’re working with non React code
You need simple and reusable components
You don’t need real-time tracking of input value
You’re integrating with third-party libraries that access DOM elements
You want to reduce unnecessary re-render cycles
Still, they can’t be your default choice if state management and validation matter.
React recommends using controlled components for form elements like:
Text fields
Dropdowns
Checkboxes
Radio buttons
That’s because form data is handled in a single source—the component’s state. This makes it easier to control input form logic and provide feedback instantly.
Also, the official React documentation aligns with this pattern for forms.
Building robust controlled components requires good practices around consistency, performance, and usability. Here are detailed best practices to follow when working with controlled components in React:
Keep the component’s state minimal: Only store what’s necessary. Avoid duplicating data already available elsewhere in the app or derived from props.
Use a single change handler for multiple inputs: Create dynamic change handlers that can update state based on name or id attributes. This reduces boilerplate and simplifies complex forms.
1const handleChange = (e) => { 2 const { name, value } = e.target; 3 setFormData((prev) => ({ ...prev, [name]: value })); 4};
Avoid unnecessary re-renders: Wrap components in React.memo when they don’t rely on frequent state changes, and split large forms into smaller controlled subcomponents.
Validate input while typing or on blur: Combine onChange and onBlur events to give users feedback at the right time without interrupting the typing experience.
Manage focus and accessibility: React refs and state can be combined to manage focus and ARIA attributes for screen readers.
Centralize form logic in a parent component: For large forms, managing all inputs from a parent helps keep the UI consistent and simplifies submission logic.
Initialize fields properly: Always provide an initial value for controlled inputs to avoid React warnings like switching from uncontrolled to controlled.
1<input value={value || ''} onChange={handleChange} />
Handle nested form data thoughtfully: When dealing with objects or arrays in form data, use utility functions or libraries like lodash’s set to update nested values cleanly.
Use useReducer for complex state: If the form involves many fields and actions, useReducer provides better scalability than useState.
These practices help maintain clarity in your form logic, improve developer experience, and support scalability in real-world applications.
Uncontrolled components manage state internally, which means:
You don’t manually update the input value
You use defaultValue instead of value
You rely on refs only when needed
While not perfect for complex forms, they shine in basic forms with fewer fields and logic.
There’s no one-size-fits-all. The decision between controlled and uncontrolled components depends on your use case.
Use Case | Recommendation |
---|---|
Complex Forms | Controlled Components |
Third-party DOM Libraries | Uncontrolled Components |
Minimal Interaction Forms | Uncontrolled |
Needs Real-time Validation | Controlled |
Performance-sensitive Inputs | Uncontrolled |
Keep the form’s intent and complexity in mind.
Understanding the difference between a controlled and uncontrolled component helps you build better forms in React.
Go with controlled components when you need full control of form state. They give you more flexibility for validation and updates.
Use uncontrolled components for simpler forms or when you're working with non-React code.
Now you're ready to choose the right pattern for your form—whether it’s a basic input or a more complex setup.