If you’ve ever built a form in React and felt like the input fields had a mind of their own, you’re not alone. One minute your form is working fine, the next you’re staring at a blank input that won’t update. Or React throws a warning like “A component is changing an uncontrolled input of type text to be controlled.” and you’re not even sure what that means.

I didn’t really get it either until I realized that React doesn’t just read form inputs – it can own them. And whether you let React control your inputs or let the DOM handle them makes a real difference in how your form behaves.

In this article, I’ll break down:

  • What controlled and uncontrolled components are

  • Why the difference matters

  • When to use each one

  • And how to avoid common beginner mistakes

You’ll get real code, clear examples, and a no-nonsense guide to making React forms behave exactly how you want.

Here’s what we’ll cover:

What Is a Controlled Component?

A controlled component in React is an input (like a text box or dropdown) where React keeps track of the value.

Instead of the browser handling the input on its own, you use React state to tell the input what to show, and update that state when the user types. Basically, for every keystroke the state updates and the component re-renders.

Here’s a basic example:

import { useState } from "react";

function NameForm() {
  const [name, setName] = useState("");

  return (
    <input
      type="text"
      value={name} //Whatever the state is, that is what the value of the input field will be
      onChange={(e) => setName(e.target.value)} //When you type, this function runs and updates the state
    />
  );
}

React re-renders with the new value, keeping the UI and the data in sync. You're always in control of what’s in that field.

Why use this approach?

  • You always know the current value.

  • It’s easy to validate, reset, or change the input from code.

  • It’s the standard approach in most React apps.

What Is an Uncontrolled Component?

An uncontrolled component is the opposite of what we just looked at. Instead of using React state to manage the input, you let the browser handle it on its own, like a regular HTML form.

To get the value, you use something called a ref (short for “reference”) to reach into the DOM and grab it when you need it.

In React, refs are created using a built-in hook called useRef. This hook lets you create a reference to a DOM element (like an <input> ), so you can directly access its current value whenever you need it (for example, when a form is submitted).

Unlike useState, which tracks changes and causes re-renders, useRef simply gives you a way to "point to" or "reach into" an element in the DOM without triggering re-renders. It’s useful when you don’t want React to manage the input’s state, but you still need to read its value later.

Here’s what that looks like:

import { useRef } from "react";

function NameForm() {
  const inputRef = useRef();

  const handleSubmit = () => {
    alert(inputRef.current.value); //displays the value of the input element
  };

  return (
    <>
      <input type="text" ref={inputRef} />; //gives you direct access to the input element
      <button onClick={handleSubmit}>Submit</button> //
    </>
  );
}

React isn’t involved in tracking every keystroke. It only checks the value when you ask for it and the input keeps track of it’s value on it’s own.

Why use this?

  • It’s simpler for quick forms where you only need the value at the end (like on submit).

  • It avoids re-renders while typing, which can be useful in performance-sensitive apps.

But: it’s harder to do things like validation, real-time updates, or syncing with other parts of your app.

Controlled vs Uncontrolled: What's the Difference?

Now that you’ve seen both, let’s make the differences crystal clear.

Controlled ComponentsUncontrolled Components
React is in charge.The browser is in charge.
You use useState to store the value.You use useRef to access the value.
You update the value with onChange.The input keeps its own value.
React re-renders the input every time the value changes.You access it using a ref only when you need it.

Think of a controlled component like a parent carefully tracking what their kid is writing in a notebook, checking every word as it's written.

An uncontrolled component is more like letting the kid write freely and just reading what they wrote at the end.

When to Use Controlled vs Uncontrolled Components

Both controlled and uncontrolled components have their place. The key is knowing when each one makes sense for your project and what you want to achieve.

Use Controlled Components When:

  • You need to validate input while the user types.
    Example: Show an error if the user leaves a field empty.

  • You want to enable/disable buttons based on input.
    Example: Disable the “Submit” button until all fields are filled.

  • You’re building dynamic forms.
    Example: Show or hide fields based on what the user selects.

  • You need to sync input values with other state.
    Example: Update a live preview as the user types.

Use Uncontrolled Components When:

  • You just need the value when the form is submitted.
    Example: A basic contact form that sends data once.

  • You don’t need to update the UI based on input.

  • You want better performance in large forms.
    Example: Dozens of inputs that don’t need to trigger re-renders on every change.

In short:

  • If you need to watch, validate, or react to what the user types(interact with your app’s state or UI), go with controlled.

  • If you just need to grab the value later, uncontrolled can work fine.

Conclusion

Controlled vs. Uncontrolled Components might seem like a small technical distinction at first but understanding the difference gives you much more control over how your forms behave in React.

Controlled components put you in the driver’s seat. React manages the form state for you, so you always know what’s in your inputs. This makes it easy to validate user input in real-time, sync form data with other parts of your app, and build more interactive, dynamic experiences.

Uncontrolled components, on the other hand, keep things minimal. The browser handles the input’s state, and you only reach in to get the value when you need it, usually using a ref.

There’s no one-size-fits-all answer for which is better. It depends entirely on your needs. If your form needs to react to user input as it changes or connect tightly with app logic, go controlled. If you just need to collect some values and move on, uncontrolled might be simpler.