Monday, March 3, 2025
React Forms - Controlled vs Uncontrolled Components Explained
Posted by

Handling forms in React can sometimes be a bit tricky, especially for beginners. React provides two primary ways to handle form data: controlled components and uncontrolled components. Each approach has its own use cases and benefits. In this blog, we will delve into both concepts, explaining the differences and helping you decide which one to use in your projects.
Controlled Components
Controlled components are those whose form data is controlled by the React component's state. In other words, the state of the form inputs (like text boxes, checkboxes, and radio buttons) is stored in the component's state and is updated through events.
How Controlled Components Work
- State Initialization: The state is initialized in the component's constructor or using the
useState
hook in functional components. - Event Handling: Event handlers are added to the form inputs to update the state whenever the user makes changes.
- Binding State to Input: The value of the form inputs is bound to the component's state.
Example of a Controlled Component
Let's create a simple form using controlled components. This form will have a text input and a submit button.
import React, { useState } from 'react';
function ControlledForm() {
const [inputValue, setInputValue] = useState('');
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
alert('Input Value: ' + inputValue);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={inputValue}
onChange={handleInputChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
export default ControlledForm;
- State Initialization:
useState('')
initializes the input's value in the component's state. - Event Handling:
handleInputChange
updates the state with the current input value whenever a change occurs. - Binding State to Input:
value={inputValue}
binds the input's value to the state.
Advantages of Controlled Components
- Validation: Easier to validate. You can easily validate data as it changes.
- Real-time Updates: Can easily update or reset form fields and reflect those changes immediately in the UI.
- Multiple Inputs: Handling multiple inputs with a single state object is straightforward.
Example with Multiple Inputs
import React, { useState } from 'react';
function MultipleInputsControlledForm() {
const [formData, setFormData] = useState({
name: '',
email: ''
});
const handleInputChange = (event) => {
const { name, value } = event.target;
setFormData(prevState => ({
...prevState,
[name]: value
}));
};
const handleSubmit = (event) => {
event.preventDefault();
alert('Name: ' + formData.name + ', Email: ' + formData.email);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
name="name"
value={formData.name}
onChange={handleInputChange}
/>
</label>
<br />
<label>
Email:
<input
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
/>
</label>
<br />
<input type="submit" value="Submit" />
</form>
);
}
export default MultipleInputsControlledForm;
- State Initialization:
useState
initializes an object with multiple form fields. - Event Handling: Updates the corresponding state field based on the input name.
- Binding State to Inputs: Each input's value is bound to the state.
Uncontrolled Components
Uncontrolled components, on the other hand, use the DOM to manage form data. Instead of managing form data in the state, you can use a ref to access the form values at the time of submission.
How Uncontrolled Components Work
- Form Inputs: Regular form inputs are used without any
value
oronChange
attributes. - Refs: React
refs
are used to access the input values. - Submit Event: The form's
onSubmit
event handler can access the input values directly from refs.
Example of an Uncontrolled Component
Here’s a simple example of an uncontrolled form using the useRef
hook.
import React, { useRef } from 'react';
function UncontrolledForm() {
const nameInput = useRef(null);
const emailInput = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
alert('Name: ' + nameInput.current.value + ', Email: ' + emailInput.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
ref={nameInput}
/>
</label>
<br />
<label>
Email:
<input
type="email"
ref={emailInput}
/>
</label>
<br />
<input type="submit" value="Submit" />
</form>
);
}
export default UncontrolledForm;
- Refs:
useRef
creates a reference to each input element. - Accessing Values: Upon form submission, the values are accessed using
ref.current.value
.
Advantages of Uncontrolled Components
- Simplicity: Less code compared to controlled components. No need for event handlers to update the state.
- Performance: Suitable for large forms where managing state for every input can be performance-intensive.
- Integration: Easier to integrate with third-party components that are not originally designed to be controlled.
Example with Multiple Inputs
import React, { useRef } from 'react';
function MultipleInputsUncontrolledForm() {
const nameInput = useRef(null);
const emailInput = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
alert('Name: ' + nameInput.current.value + ', Email: ' + emailInput.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
ref={nameInput}
/>
</label>
<br />
<label>
Email:
<input
type="email"
ref={emailInput}
/>
</label>
<br />
<input type="submit" value="Submit" />
</form>
);
}
export default MultipleInputsUncontrolledForm;
- Refs: Separate refs for each input.
- Submit Handling: Accessing form values directly from
ref.current.value
.
Comparing Controlled and Uncontrolled Components
To make it easier to understand the differences between controlled and uncontrolled components, let's compare them in terms of key features.
Feature | Controlled Components | Uncontrolled Components |
---|---|---|
Data Handling | Handled through state and event handlers. | Handled through refs. |
Validation | Easier to validate in real-time. | Requires manual validation on form submission. |
Code Length | More code due to state management and event handlers. | Less code, easier integration with third-party components. |
Performance | Can be less performant for large forms. | Better performance for large forms. |
Integration | Requires custom event handlers for third-party components. | Easier to integrate with third-party components. |
Best Practices
Choosing between controlled and uncontrolled components often depends on the specific requirements of your application.
-
Use Controlled Components When:
- You need real-time validation.
- You need to reset form fields.
- You have multiple related fields that need to be validated against each other or calculated from each other.
-
Use Uncontrolled Components When:
- You have a simple form.
- You need to speed up form processing time.
- Integrating with third-party libraries that manage their own state.
Tips and Tricks
-
Default Values: For uncontrolled components, you can set default values using the
defaultValue
prop. For controlled components, set the initial state with the desired default value.// Uncontrolled Component with Default Value <input type="text" ref={nameInput} defaultValue="John Doe" /> // Controlled Component with Default Value const [name, setName] = useState('John Doe');
-
Handling Complex Forms: For complex forms with many fields, consider using libraries like Formik or React Hook Form to manage form state and validation more efficiently.
-
Cross-Browser Compatibility: Uncontrolled components are generally compatible with a wider range of browsers since they rely on the native DOM.
Conclusion
Both controlled and uncontrolled components have their place in React form handling. Understanding when to use each approach is crucial for building efficient and maintainable forms.
- Controlled components are better suited for forms that require real-time validation and tight integration with React's state management.
- Uncontrolled components are more suitable for simpler forms or when integrating with third-party form libraries.
By choosing the right approach, you can build more efficient and user-friendly React applications. Remember to consider the specific needs of your application and choose the component type that best fits those needs.
Summary
- Controlled Components: Managed through state, useful for real-time validation and reset operations.
- Uncontrolled Components: Managed through refs, useful for simple forms and faster processing.
- Choosing: Decide based on form complexity, validation needs, and integration with third-party libraries.
This comprehensive explanation should help you decide which approach to use for your next React form project. Happy coding!