# Controlled and uncontrolled components in React explained

> Learn the difference between controlled and uncontrolled components in React, when to use each, and how they handle form fields with useState and useRef.

**Source:** https://hackmamba.io/engineering/what-are-controlled-and-uncontrolled-form-fields-in-react-js/
**Published:** 8 Apr 2022
**Author:** Asjad Khan
**Category:** Engineering

---
Form elements in HTML, `<input>`, `<textarea>`, `<select>`, keep their own internal state and update it based on user input. React gives you two ways to handle that state: let React own it (controlled), or let the DOM own it (uncontrolled). Understanding the difference is one of the most common React interview questions and a practical decision you make every time you build a form.

## What are controlled and uncontrolled components in React?

A **controlled component** is a form element whose value is driven by React state. React is the single source of truth, every keystroke updates state, and the state value is what the input displays. An **uncontrolled component** is a form element that manages its own value in the DOM. React reads the value when needed using a `ref`, rather than tracking every change.

The simplest way to remember it: controlled components use `useState`, uncontrolled components use `useRef`.

## Controlled vs uncontrolled components: key differences

| | Controlled | Uncontrolled |
|---|---|---|
| Source of truth | React state | DOM |
| How value is read | `value` prop + `onChange` | `ref.current.value` |
| Re-renders on change | Yes, every keystroke | No |
| Validation | Easy, on every keystroke | Only on submit |
| File inputs | Not supported | Required |
| Code complexity | Higher (more boilerplate) | Lower for simple forms |
| React Hook Form | Uses uncontrolled internally for performance | Native fit |

## Controlled components

In a controlled component, every change to the input is reflected in React state, and the state value is what drives the input's displayed value. The state variable is the single source of truth.

A controlled component requires two things: a `value` prop tied to state, and an `onChange` handler that updates that state on every keystroke, including backspace.

```jsx
import React, { useState } from "react";

function App(props) {
  const [message, updateMessage] = useState("");

  return (
    <div className="App" style={{ height: "20px" }}>
      <div className="container">
        <input
          type="text"
          placeholder="Enter message here.."
          value={message}
          onChange={(event) => updateMessage(event.target.value)}
        />
        <p>the message is: {message}</p>
      </div>
    </div>
  );
}

export default App;
```

In the `App` component above, `message` is the state variable and `updateMessage` is the setter. The input's `onChange` handler calls `updateMessage` with the new value on every keystroke, keeping state and UI in sync at all times.

Note that a parent `<form>` element is not required for an input to be controlled, the `value` prop and `onChange` handler are what make it controlled, not the surrounding structure.

**Where controlled components shine:**
- Real-time validation (flag an invalid email on every keystroke)
- Conditional UI (enable a submit button only when all fields are filled)
- Derived state (show a character count as the user types)
- Shared state between components (lift the value up to a parent)

**The trade-off:** On pages with many input fields, each one needs its own `value` prop and `onChange` handler. This is manageable but adds boilerplate. Libraries like [React Hook Form](https://react-hook-form.com/) solve this by using uncontrolled components under the hood for performance, while exposing a controlled-like API.

View the full demo [here](https://codesandbox.io/s/controlled-inputs-e09hp?file=/src/Form.js:0-520).

## Uncontrolled components

In an uncontrolled component, the DOM stores the input's value. React does not track changes on every keystroke, instead, you read the value when you need it (typically on form submit) using a `ref`.

```jsx
import { useRef } from "react";

const NameForm = () => {
  const inputRef = useRef(null);

  const handleSubmit = () => {
    alert(inputRef.current.value);
  };

  return (
    <div>
      <input type="text" placeholder="Type..." ref={inputRef} />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
};

export default NameForm;
```

In this example, `inputRef` is created with `useRef` and attached to the `<input>` via the `ref` attribute. The input's value is only read when the submit button is clicked, React does not re-render on every keystroke.

Uncontrolled components behave more like plain HTML form elements. The DOM, not the component, stores the data.

**Where uncontrolled components make sense:**
- Simple submit-once forms (contact forms, search bars, newsletter signups) where you only need the value at the end
- Integrating with non-React libraries that manipulate the DOM directly
- File inputs, file inputs are always uncontrolled because their value is read-only for security reasons and cannot be set programmatically
- Performance-sensitive forms with many fields where avoiding re-renders on every keystroke matters

View the full demo [here](https://codesandbox.io/s/uncontrolled-form-fields-ro8w7?file=/src/App.js).

## When to use controlled vs uncontrolled components

The React team's general recommendation is to start with controlled components. They are more predictable, easier to debug, and work better with validation and complex form logic.

Use **controlled components** when:
- You need to validate input on every keystroke
- You need to enable or disable UI based on the current value
- You need to share the input value with other components
- You need to transform or format the value as the user types

Use **uncontrolled components** when:
- The form is simple and you only need values on submit
- You are integrating with a third-party DOM library
- You are handling file uploads
- You need to reduce re-renders for performance reasons
- You are using React 19 form actions, which work naturally with uncontrolled inputs

A common mistake is switching a component between controlled and uncontrolled during its lifecycle. If you initialize an input with `value={undefined}` and later pass a real value, React will warn you. Pick one approach per input and stay consistent.

## Controlled vs uncontrolled: performance

Controlled components re-render on every keystroke because each character triggers a state update. For most forms this is imperceptible. For forms with dozens of fields or very complex render logic, the cumulative re-renders can become noticeable.

This is why React Hook Form uses uncontrolled components internally, it avoids re-rendering the entire form on every keystroke by reading values from the DOM via refs at submit time, only triggering re-renders when necessary (e.g., for validation feedback).

If you are building a large, complex form and performance is a concern, consider React Hook Form rather than reimplementing the uncontrolled pattern yourself.

## Choosing the right approach for your form

Both controlled and uncontrolled components have a place in React development. The choice depends on what the form needs to do.

For straightforward forms with minimal validation, uncontrolled components keep the code simple and avoid unnecessary re-renders. For forms that need real-time feedback, conditional logic, or integration with broader application state, controlled components give you the control you need.

If you are building React forms at scale and want to understand broader patterns like state management across components, [explaining web app state management](https://hackmamba.io/engineering/explaining-web-apps-state-management-like-abc/) covers how React state works across component trees.

## FAQs

1, **What is the difference between controlled and uncontrolled components in React?**

A controlled component is a form element whose value is managed by React state. The `value` prop is tied to a state variable, and an `onChange` handler updates that state on every keystroke, making React the single source of truth. An uncontrolled component lets the DOM manage the value. React reads it only when needed using a `ref`, without tracking every change.

2, **When should I use controlled vs uncontrolled components in React?**

Use controlled components when you need real-time validation, conditional UI, or to share the input value with other components. Use uncontrolled components for simple submit-once forms, file inputs, integration with non-React libraries, or when reducing re-renders matters for performance. The React team recommends starting with controlled components and switching to uncontrolled only when there is a specific reason.

3, **Can you mix controlled and uncontrolled inputs in the same form?**

Yes, you can have some inputs controlled and others uncontrolled in the same form. However, you cannot switch a single input between controlled and uncontrolled during its lifecycle. If you pass `value={undefined}` to an input and later pass a real value, React will throw a warning. Decide the approach per input and keep it consistent.

4, **Why is file input always uncontrolled in React?**

File inputs (`<input type="file">`) are always uncontrolled because their value is read-only for security reasons, JavaScript cannot set the value of a file input programmatically. You must always read the selected file via a `ref` or from the `onChange` event, even in an otherwise controlled form.

5, **How does React Hook Form relate to controlled and uncontrolled components?**

React Hook Form uses uncontrolled components internally by default. It reads input values from the DOM via refs at submit time rather than tracking every keystroke through state, which significantly reduces re-renders and improves performance for large forms. It exposes a controlled-like API (`register`, `watch`, `setValue`) without the re-render cost of true controlled inputs.

6, **What happens if I initialize a controlled input with undefined?**

If you pass `value={undefined}` to an input, React treats it as uncontrolled. If you later pass a real value, React will warn: "A component is changing an uncontrolled input to be controlled." Always initialize a controlled input with a defined value, use an empty string `""` as the default rather than `undefined` or `null`.

7, **Do controlled components affect performance?**

Controlled components re-render the component on every keystroke because each character triggers a state update. For most forms this has no noticeable impact. For forms with many fields or expensive render logic, the cumulative re-renders can affect performance. In those cases, consider React Hook Form, which avoids this by using uncontrolled inputs under the hood.
