Skip to Content
Module 6: Professional Forms6.1 Controlled vs Uncontrolled

Module 6.1: Controlled vs Uncontrolled Components

Banner displaying the module title and a graphic representing code and data flow.

🎯 Learning Objectives

By the end of this lesson, you’ll have a solid grasp of:

  • The Essence of Forms in React: The core differences between React managing your form data versus the DOM handling it.
  • Controlled Components: Understanding the “React as Single Source of Truth” paradigm.
  • Uncontrolled Components: Leveraging useRef to directly access values from the DOM.
  • When to Use Which: Weighing the pros, cons, and real-world scenarios for each approach.

1. Controlled Components (React is in Charge)

In this model, the value of an input element is entirely managed by React’s state. Any modification to the input triggers an onChange handler, which then updates the state.

Diagram comparing controlled and uncontrolled component data flow.

1.1. Data Flow

  1. The user types a character.
  2. The onChange event fires.
  3. setState is called to update the new state.
  4. React re-renders the component with the updated value.
  5. The input displays the value from the state.

1.2. Code Example

import { useState } from 'react'; export default function ControlledForm() { const [name, setName] = useState<string>(''); const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { // React maintains control over every character input. const value = e.target.value.toLowerCase(); // Example: Always convert to lowercase. setName(value); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); alert(`Submit: ${name}`); }; return ( <form onSubmit={handleSubmit} className="p-4 border rounded"> <h3>Controlled Input</h3> <input type="text" value={name} // 1. Value sourced from state. onChange={handleChange} // 2. Updated via the handler function. className="border p-2 rounded" /> <p>Current State: {name}</p> </form> ); }

Pros:

  • You get immediate control over data as it’s typed (enabling validation and formatting).
  • Easily disable the Submit button if the form isn’t valid.
  • Keeps data in sync with other UI elements.

Cons:

  • The component re-renders with every keystroke, which might impact performance if the form is very complex.

2. Uncontrolled Components (DOM is in Charge)

Here, the data lives directly within the browser’s DOM node, just like in traditional HTML. React only accesses the value when needed (e.g., on form submission) using a ref.

Diagram illustrating the data flow for an uncontrolled component.

2.1. Data Flow

  1. The user types a character -> the input displays it immediately (browser default behavior).
  2. React does not re-render.
  3. When required (like on Submit), you fetch the value using ref.current.value.

2.2. Code Example

import { useRef } from 'react'; export default function UncontrolledForm() { const nameRef = useRef<HTMLInputElement>(null); // Create a ref typed for HTMLInputElement. const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // Retrieve the value directly from the DOM. if (nameRef.current) { const value = nameRef.current.value; alert(`Submit: ${value}`); } }; return ( <form onSubmit={handleSubmit} className="p-4 border rounded mt-4"> <h3>Uncontrolled Input</h3> <input type="text" ref={nameRef} // Attach the ref here. defaultValue="Default Value" // Use defaultValue instead of value. className="border p-2 rounded" /> <button type="submit" className="ml-2 bg-blue-500 text-white p-2 rounded"> Submit </button> </form> ); }

Pros:

  • Generally better performance (no re-renders on typing).
  • Easier integration with non-React code (like jQuery or third-party libraries).

Cons:

  • Real-time validation (e.g., showing an error as soon as a user types incorrectly) is more difficult.
  • Limited control over input formatting.

3. Hands-On Labs

Lab 1: Real-time Password Strength (Controlled)

Build a password input form that dynamically displays a strength meter.

  • Features:
    • A password input with an “eye icon” to toggle visibility.
    • A progress bar that changes color (Red/Yellow/Green) based on the password’s complexity.
    • A list of password requirements (uppercase, number, special character).
  • Techniques: Controlled Components, Regex Validation.

Screenshot of a real-time password strength meter UI.

💡 Hint:

  • Use a password state variable to store the input value.
  • Employ a requirements array of booleans to check against regex patterns every time the password changes.
  • The UI will involve an input group with a button to toggle the input’s type between "text" and "password".

Lab 2: Drag & Drop File Uploader (Uncontrolled)

Create a slick drag-and-drop file uploader that replaces the default, clunky file input.

  • Features:
    • Allow users to drag and drop files into a designated area or click to select.
    • Display an immediate image preview after a file is dropped.
    • Include a “Remove” button to clear the selected file.
  • Techniques: Uncontrolled Components (useRef for the hidden file input), Drag Events (onDragOver, onDrop).

Screenshot of a drag-and-drop file uploader interface.

💡 Hint:

  • The type="file" input should be hidden using display: none.
  • Use useRef to programmatically trigger a click on the hidden input when the user interacts with the drop zone.
  • Listen for onDrop events, grab the files from e.dataTransfer.files, and display previews using URL.createObjectURL.

4. Summary

CharacteristicControlledUncontrolled
Source of DataReact stateDOM
UpdatesRe-renders on each keystrokeNo re-renders
ValidationReal-time (immediate)On submit
Use CasesMost modern forms, complex validationSimple forms, File Inputs, extreme performance needs

Recommendation: Prioritize Controlled Components (in about 90% of cases) to harness React’s full power. Switch to Uncontrolled only when facing significant performance bottlenecks or when integrating with File Inputs or legacy code.


5. Further Reading

Last updated on