How to Implement Uncontrolled Components in React

Uncontrolled components in React offer a straightforward way to handle form data by letting the DOM manage the form state rather than keeping it in React state. This article explains the core concepts of uncontrolled components, demonstrates how to implement them using the useRef hook, and highlights the scenarios where they are most effective.

What are Uncontrolled Components?

In React, an uncontrolled component is a component where form data is handled by the DOM itself. Instead of writing an event handler for every state update (like you do with controlled components using useState), you use a ref to pull the values from the DOM when you need them—typically when the form is submitted.

This approach results in less code and can be easier to integrate with non-React libraries, though it offers less control over real-time validation and conditional formatting.

Implementing an Uncontrolled Component

To implement an uncontrolled component in React, you will use the useRef hook to access the input elements directly.

Here is a step-by-step implementation of a simple form using uncontrolled components:

import React, { useRef } from 'react';

function UncontrolledForm() {
  // 1. Create a ref to store the DOM element
  const nameInputRef = useRef(null);
  const emailInputRef = useRef(null);

  const handleSubmit = (event) => {
    event.preventDefault();
    
    // 2. Access the value directly from the DOM ref
    const nameValue = nameInputRef.current.value;
    const emailValue = emailInputRef.current.value;

    console.log('Submitted Name:', nameValue);
    console.log('Submitted Email:', emailValue);
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Name:</label>
        {/* 3. Attach the ref to the input element */}
        <input 
          type="text" 
          id="name" 
          ref={nameInputRef} 
          defaultValue="John Doe" 
        />
      </div>
      <div>
        <label htmlFor="email">Email:</label>
        <input 
          type="email" 
          id="email" 
          ref={emailInputRef} 
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

export default UncontrolledForm;

Key Steps in the Implementation:

  1. Create the Ref: Initialize a ref using useRef(null) for each input field you want to track.
  2. Assign the Ref: Pass the ref to the React element using the ref attribute (e.g., ref={nameInputRef}).
  3. Set Default Values: Use the defaultValue attribute instead of value to set an initial value. Using value on an uncontrolled input will make the field read-only and generate a console warning.
  4. Access the Value: Retrieve the input value during an event (like onSubmit) using refName.current.value.

Handling File Inputs

In React, file inputs (<input type="file" />) are always uncontrolled components because their value can only be set by a user, not programmatically by React.

Here is how you handle a file input using refs:

import React, { useRef } from 'react';

function FileUpload() {
  const fileInputRef = useRef(null);

  const handleSubmit = (event) => {
    event.preventDefault();
    const files = fileInputRef.current.files;
    if (files.length > 0) {
      console.log(`Selected file: ${files[0].name}`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="file" ref={fileInputRef} />
      <button type="submit">Upload</button>
    </form>
  );
}

When to Use Uncontrolled Components

While controlled components are recommended for most forms in React, uncontrolled components are highly useful in specific scenarios: