Uncontrolled Component Pattern

Overview

Uncontrolled components in React are form elements that maintain their own internal state. Instead of using React state to track input values, uncontrolled components rely on refs to directly access the DOM element values when needed. This approach is similar to traditional HTML form handling.

Example

Uncontrolled Form

Uncontrolled Component Example

This form demonstrates uncontrolled components using refs to access input values directly from the DOM.

Code Implementation

// UncontrolledForm.tsx
import React, { useRef } from "react";

interface UncontrolledFormProps {
  onSubmit: (formData: {
    name: string;
    email: string;
    message: string;
  }) => void;
}

export const UncontrolledForm: React.FC<UncontrolledFormProps> = ({ onSubmit }) => {
  // Create refs for form elements
  const nameRef = useRef<HTMLInputElement>(null);
  const emailRef = useRef<HTMLInputElement>(null);
  const messageRef = useRef<HTMLTextAreaElement>(null);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    
    // Access current DOM values directly using refs
    if (nameRef.current && emailRef.current && messageRef.current) {
      const formData = {
        name: nameRef.current.value,
        email: emailRef.current.value,
        message: messageRef.current.value,
      };
      
      onSubmit(formData);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <div>
        <label htmlFor="name" className="block mb-1 text-sm font-medium">
          Name
        </label>
        <input
          type="text"
          id="name"
          ref={nameRef}
          defaultValue=""
          className="w-full p-2 border rounded focus:ring focus:ring-blue-200"
          required
        />
      </div>
      
      <div>
        <label htmlFor="email" className="block mb-1 text-sm font-medium">
          Email
        </label>
        <input
          type="email"
          id="email"
          ref={emailRef}
          defaultValue=""
          className="w-full p-2 border rounded focus:ring focus:ring-blue-200"
          required
        />
      </div>
      
      <div>
        <label htmlFor="message" className="block mb-1 text-sm font-medium">
          Message
        </label>
        <textarea
          id="message"
          ref={messageRef}
          defaultValue=""
          rows={4}
          className="w-full p-2 border rounded focus:ring focus:ring-blue-200"
          required
        />
      </div>
      
      <button
        type="submit"
        className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
      >
        Submit
      </button>
    </form>
  );
};

Key Benefits

  • Simpler implementation for basic forms
  • Lower overhead—no state updates on every keystroke
  • Direct integration with non-React code
  • Works well with file inputs and other native inputs
  • Useful for forms that need minimal interaction before submission
  • Better performance for large forms with many inputs