VOOZH about

URL: https://blog.logrocket.com/controlled-vs-uncontrolled-components-in-react/

⇱ Controlled vs. uncontrolled components in React - LogRocket Blog


2021-09-29
1121
#react
Chidume Nnamdi
69333
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

The form is one of the most-used HTML elements in web development. Since the introduction of React, the way forms have been handled has changed in many ways.

👁 Controlled vs. Uncontrolled Components in React

In React, there are two ways to handle form data in our components. The first way is by using the state within the component to handle the form data. This is called a controlled component. The second way is to let the DOM handle the form data by itself in the component. This is known as an uncontrolled component.

In this tutorial, we’ll explain the difference between controlled and uncontrolled components in React. We’ll also demonstrate how each works with practical examples.

🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

What are controlled components in React?

Controlled components in React are those in which form data is handled by the component’s state.

Forms are used to store information in a document section. The information from this form is typically sent to a server to perform an action. This data is held by form input elements and control elements, such as input, select, textarea, etc., which maintain and control their states or values.

What do I mean by that?

Each form element contains a value. The value may be typed (input, textarea) or selected (checkbox, select, radiobutton, etc) by the user or browser. When the element’s value is changed (triggered by the act of typing or selecting), it is updated accordingly.

You can get the value of an element using the .value property in its HTMLElement instance. You can also use the .value property to set values in the form elements.

Now we can use state in our component to hold or manage the values of the elements in a form element. Here’s an example:

function App() {
 const [name, setName] = useState("");
 const [email, setEmail] = useState("");

 function onSubmit() {
 console.log("Name value: " + name);
 console.log("Email value: " + email);
 }
 return (
 <form onSubmit={onSubmit}>
 <input
 type="text"
 name="name"
 value={name}
 onChange={(e) => setName(e.target.value)}
 required
 />
 <input
 type="email"
 name="email"
 value={email}
 onChange={(e) => setEmail(e.target.value)}
 required
 />
 <input type="submit" value="Submit" />
 </form>
 );
}

Here we have two states: name and email. These states are assigned to the value property of the name and email input elements.

The name state holds the value of the name input element. When a value is being typed in the name input, the onChange event attached to it sets the value of the input to the name state using the setName updater function.

The email state holds the value of the email input element. The onChange event attached to the email input changes the email state via setEmail() to hold the value typed into the element.

As you can see, the values of our input elements name and email are controlled by the React state; the state becomes the “single source of truth” for the input elements. Therefore, the App component shown above is a controlled component.

The drawback to using controlled components is that the number of states in a component increases as more control elements are added to the form element.


Over 200k developers use LogRocket to create better digital experiences

👁 Image
Learn more →

What are uncontrolled components in React?

Uncontrolled components are those for which the form data is handled by the DOM itself. “Uncontrolled” refers to the fact that these components are not controlled by React state.

The values of the form elements are traditionally controlled by and stored on the DOM. We will have to refer to the instance of the form elements to retrieve their values from the DOM.

function App() {
 function onSubmit() {
 console.log("Name value: " + window.name.value);
 console.log("Email value: " + window.email.value);
 }
 return (
 <form onSubmit={onSubmit}>
 <input type="text" name="name" id="name" required />
 <input type="email" name="email" id="email" required />
 <input type="submit" value="Submit" />
 </form>
 );
}

In the above code, we assigned ID attributes to the name and email input elements with values name and email, respectively. We used these id attributes to get the value of the input element when the form is being submitted.

The above component is an uncontrolled component because React has no control over the values of the form input elements.

In this example, we used DOM APIs directly. Now let’s refactor the code to do it in a React way:

function App() {
 const nameRef = useRef();
 const emailRef = useRef();

 function onSubmit() {
 console.log("Name value: " + nameRef.current.value);
 console.log("Email value: " + emailRef.current.value);
 }
 return (
 <form onSubmit={onSubmit}>
 <input type="text" name="name" ref={nameRef} required />
 <input type="email" name="email" ref={emailRef} required />
 <input type="submit" value="Submit" />
 </form>
 );
}

We created two React refs, nameRef and emailRef, and assigned them to the ref attributes of name and email inputs, respectively. This will cause the refs to hold the HTMLElement instances of the elements in their .current property. From .current, we can reference the .value property to get the values of the input elements.



Controlled vs. uncontrolled components: Key differences

Now that we understand what React controlled and uncontrolled components are, let’s review some key differences between them:

  • Controlled components are predictable because the state of the form elements is handled by the component
  • Uncontrolled components are not predictable because, during the lifecycle of a component, the form elements can lose their reference and may be changed/affected by other sources
  • Controlled components enable you to effectively employ form validation to your forms. It doesn’t matter what changes the form elements. Their values are safe in our local states, so that’s where we perform our validation
  • With controlled components, you are very much in control of your form elements’ values. You can dictate how they go and what can and cannot be inserted

So which should you use in your React project? The question is not whether controlled are uncontrolled components are better, but which better serves your use case and fits your personal preference. Controlled components, obviously, afford you more control over your data, but if you’re more comfortable using uncontrolled components in your project, more power to you.

There are no defined rules to help you determine when and how to use uncontrolled components versus controlled components in React; it all depends on how much control you want to have over your inputs.

Conclusion

In this tutorial, we zoomed in on form elements and form data, both generally and within the React framework. Next, we introduced two ways to handle form data in React components: controlled and uncontrolled. Finally, we took a deep dive into both types of component and demonstrated how they behave with practical examples.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

👁 Image
Emmanuel John
Jun 17, 2026 ⋅ 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

👁 Image
Chizaram Ken
Jun 16, 2026 ⋅ 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo — with email/password login, Google OAuth, session persistence, and protected routes.

👁 Image
Chinwike Maduabuchi
Jun 9, 2026 ⋅ 13 min read
View all posts

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now