LearnContact
Lesson 2120 min read

Uncontrolled Components

Learn how uncontrolled components let the DOM manage form state directly, and when accessing values through refs is the better choice.

Introduction

In the last lesson you built controlled components, where React state is the single source of truth for every keystroke. That pattern is powerful, but it is not the only way to work with form inputs. React also supports uncontrolled components, where the DOM itself keeps track of the current value, and you only reach in and read it when you actually need it.

Uncontrolled components can feel like a step backward if you have just gotten comfortable with state-driven forms, but they exist for good reasons. Some inputs — like file pickers — must be uncontrolled. Others are simply overkill to wire up to state when all you need is the value at submit time. This lesson shows you how uncontrolled components work, how to read their values with useRef, and how to decide between the two approaches.

What You Will Learn
  • What an uncontrolled component is and how it differs from a controlled one.
  • How to use useRef to read a DOM input's value directly.
  • Why file inputs in React must always be uncontrolled.
  • When uncontrolled components are a better fit than controlled ones.
  • A side-by-side comparison to help you choose the right approach.

What is an Uncontrolled Component?

An uncontrolled component is a form element — usually an <input>, <textarea>, or <select> — that manages its own value internally, the same way it would in plain HTML. React does not set the value prop and does not re-render on every keystroke. Instead, the DOM node itself holds the current value, exactly like it always has since before React existed.

To get at that value, you attach a ref to the element. Whenever you need the current value — typically when the user submits a form — you read it directly off the DOM node through the ref, rather than from a piece of React state.

DOM Owns the Value

The input keeps track of its own text; React is not notified on every change.

Read On Demand

You pull the value out only when you need it, usually at submit time.

Less Code

No onChange handler or state variable is required for a simple field.

Familiar HTML Behavior

Uncontrolled inputs behave like plain HTML form elements you may already know.

Reading Values with useRef

The useRef Hook creates a mutable object with a .current property that persists across renders. When you pass a ref to an element's ref attribute, React sets ref.current to that actual DOM node once it is mounted. From there, you can read properties like .value the same way you would with plain JavaScript DOM APIs.

Reading an input value via ref
import { useRef } from 'react';

function NameField() {
  const nameRef = useRef(null);

  function handleClick() {
    alert(`Current value: ${nameRef.current.value}`);
  }

  return (
    <div>
      <input ref={nameRef} type="text" defaultValue="" />
      <button onClick={handleClick}>Show Value</button>
    </div>
  );
}

export default NameField;
Clicking "Show Value" after typing "Amol"
Current value: Amol
defaultValue, not value

Uncontrolled inputs use defaultValue (or defaultChecked for checkboxes) to set an initial value. Using value without an onChange handler would make React treat the field as controlled and read-only, which triggers a console warning.

When Uncontrolled Inputs Make Sense

Uncontrolled components are not "wrong" or old-fashioned — they are simply the right tool for specific situations. Reach for them when you do not need to react to every keystroke.

Simple Forms

A short contact form that only needs values on submit does not benefit from state on every field.

File Inputs

File inputs are read-only from JavaScript's perspective and must be uncontrolled — more on this below.

Integrating Non-React Code

Wrapping a third-party jQuery plugin or a legacy widget is easier when React is not fighting it for control of the value.

One-Off Reads

Grabbing a value only when a button is clicked avoids unnecessary re-renders on every character typed.

File Inputs Must Be Uncontrolled

An <input type="file"> is a special case: its value is set by the user through the operating system's file picker, and for security reasons the browser never lets JavaScript set that value programmatically. Because React cannot control the value, file inputs are always uncontrolled — you read the selected file(s) through a ref instead.

A file input read through a ref
import { useRef } from 'react';

function AvatarUploader() {
  const fileRef = useRef(null);

  function handleUpload() {
    const file = fileRef.current.files[0];
    if (file) {
      console.log(`Selected file: ${file.name}`);
    }
  }

  return (
    <div>
      <input type="file" ref={fileRef} accept="image/*" />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}

export default AvatarUploader;
Console after choosing "profile.png"
Selected file: profile.png

Controlled vs Uncontrolled

Both approaches are valid; the table below summarizes the key differences to help you decide which one fits a given field.

AspectControlledUncontrolled
Source of truthReact stateThe DOM element itself
Value accessRead directly from stateRead via ref.current.value
Re-rendersOn every keystrokeNone from typing alone
Validation as you typeEasy — check state on each changeHarder — must read the DOM manually
File inputsNot possibleRequired
BoilerplateMore (state + onChange)Less (just a ref)

Example: An Uncontrolled Form

Here is a small sign-up form where every field is uncontrolled. Values are only read once, when the form is submitted.

SignupForm.jsx
import { useRef } from 'react';

function SignupForm() {
  const emailRef = useRef(null);
  const passwordRef = useRef(null);

  function handleSubmit(e) {
    e.preventDefault();
    const formData = {
      email: emailRef.current.value,
      password: passwordRef.current.value,
    };
    console.log(formData);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input ref={emailRef} type="email" placeholder="Email" defaultValue="" />
      <input ref={passwordRef} type="password" placeholder="Password" defaultValue="" />
      <button type="submit">Sign Up</button>
    </form>
  );
}

export default SignupForm;
Console after submitting
{ email: 'amol@example.com', password: '••••••••' }

Common Mistakes

Avoid These Mistakes
  • Setting value instead of defaultValue on an uncontrolled input, which makes React treat it as read-only and log a warning.
  • Trying to set an <input type="file"> value with JavaScript — browsers block this entirely for security.
  • Reading ref.current.value before the component has mounted, which causes a null reference error.
  • Mixing controlled and uncontrolled patterns on the same field, such as passing both value and a ref that also tries to set it.

Best Practices

  • Use uncontrolled components for simple, low-interaction forms and file inputs.
  • Always initialize uncontrolled fields with defaultValue or defaultChecked, never value.
  • Reach for controlled components when you need live validation, conditional rendering, or to disable a submit button based on input.
  • Combine both patterns freely in the same form — a file input can be uncontrolled while a text field next to it is controlled.
  • Prefer a single ref per field rather than querying the DOM manually with document.getElementById.

Frequently Asked Questions

Are uncontrolled components considered bad practice in React?

No. They are a normal, supported pattern. Controlled components are recommended when you need to react to every change, but uncontrolled components are perfectly fine — and sometimes preferable — for simpler cases.

Can I mix controlled and uncontrolled fields in one form?

Yes. It is common to keep most fields controlled while leaving a file input, which has no choice but to be uncontrolled, alongside them.

Why does React warn about switching between controlled and uncontrolled?

If an input starts with value={undefined} and later receives a real value (or vice versa), React cannot tell which mode it is in, so it warns you to keep the behavior consistent across renders.

Do uncontrolled inputs support validation?

Yes, but you validate at read time — for example on submit — rather than on every keystroke, since React is not being notified of each change.

Is useRef only for uncontrolled components?

No, useRef has many other uses, like storing a mutable value that does not trigger re-renders or holding a reference to a DOM node for focusing or measuring it. Reading uncontrolled input values is just one common use case.

Key Takeaways

  • Uncontrolled components let the DOM manage an input's value instead of React state.
  • useRef gives you a handle to the DOM node so you can read its .value when needed.
  • File inputs must always be uncontrolled because browsers do not allow JavaScript to set their value.
  • Uncontrolled components mean less code but make live validation and conditional UI harder.
  • Controlled and uncontrolled fields can be mixed within the same form.

Summary

Uncontrolled components hand value-tracking back to the DOM, and you read the current value through a ref whenever you actually need it — typically on submit. This trades some of the fine-grained control of state-driven forms for simplicity, and it is required for inputs like file pickers.

You now know both major approaches to handling form input in React and when each one fits. Next, you will step away from forms and start exploring how to style your components, beginning with a survey of all the major styling approaches available in React.

Lesson 21 Completed
  • You understand what uncontrolled components are and how they differ from controlled ones.
  • You can read a DOM input's value using useRef.
  • You know why file inputs must always be uncontrolled.
  • You are ready to explore component styling.
Next Lesson →

Component Styling