Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 28 of the Intermediate React: Hooks, State & Data Patterns course
ReactJune 26, 20263 min read

Controlled vs Uncontrolled Components: React Form Mastery

Master form handling in React by understanding the trade-offs between controlled components and uncontrolled components. Learn when to use state versus refs.

Reactformsstate managementhookscontrolled componentsjavascriptfrontend

Previously in this course, we covered Asynchronous Data Lifecycle to handle API states. Now that our dashboard displays live data, we need to allow users to interact with it via forms. Choosing between controlled components and uncontrolled components is the fundamental decision that dictates how your form state flows.

Understanding Form State from First Principles

In React, the "source of truth" for your data is almost always state. However, forms are a unique beast because the DOM itself maintains its own internal state for inputs.

Controlled components delegate that responsibility to React. You bind the value of an input to a piece of state and update it via an onChange handler. This makes the input "controlled" by React’s state machine.

Uncontrolled components let the DOM keep the state. You use a ref (as we practiced in Mastering useRef for DOM Access) to "pull" the value out of the input only when you actually need it, such as during a form submission.

When to Use Controlled Components

Use controlled components when you need real-time validation, dynamic UI updates based on input, or conditional disabling of buttons. If the input state needs to drive other parts of the application, it must be controlled.

JSX
import { useState } from CE9178">'react';

function ControlledInput() {
  const [email, setEmail] = useState(CE9178">'');

  return (
    <input 
      type="email" 
      value={email} 
      onChange={(e) => setEmail(e.target.value)} 
    />
  );
}

Because email is in our state, we can easily display an error message if the user types an invalid domain or disable a "Save" button if the field is empty.

When to Use Uncontrolled Components

Uncontrolled components are ideal for simple forms where you don't care about the input until the moment the user clicks "Submit." They are more performant because they don't trigger a re-render on every keystroke.

JSX
import { useRef } from CE9178">'react';

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = () => {
    alert(CE9178">`Submitted value: ${inputRef.current.value}`);
  };

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

Choosing the Right Tool: A Decision Framework

In our dashboard project, we are building a settings page. Here is how to decide:

  1. Controlled: Use for inputs requiring real-time feedback (e.g., "Username available" checks or character counters).
  2. Uncontrolled: Use for file inputs (which are inherently uncontrolled in browsers), one-off login fields, or large forms where re-rendering on every keystroke causes perceptible lag.

Hands-on Exercise: Refining the Dashboard Search

In our current dashboard, we have a search bar. Let's refactor it.

  1. If you want the search results to filter as the user types, convert the search input to a controlled component and sync it with your filter state.
  2. If you only want the search to trigger when the user clicks a "Search" button, use an uncontrolled component with a ref to grab the value only on the button click.

Try implementing the uncontrolled version first to see how much code you save, then switch to controlled to feel the difference in reactivity.

Common Pitfalls

  • Mixing Controlled and Uncontrolled: Never try to set both value and defaultValue on the same input. React will throw a warning because it doesn't know who owns the state.
  • Stale Refs: If you use an uncontrolled input, ensure you aren't trying to access ref.current during the initial render before the component has mounted.
  • Over-Controlling: Don't put every single input into global state if it doesn't need to be there. Local state is often sufficient and cleaner.

Recap

We’ve learned that controlled components provide maximum control via React state, while uncontrolled components offer a lightweight alternative using useRef. By selecting the right approach, you keep your dashboard performant and your code maintainable.

Up next: Real-time Form Validation where we will add robust error handling to these inputs.

Previous lessonError Handling and Loading UINext lesson Real-time Form Validation
Back to Blog

Similar Posts

ReactJune 25, 20263 min read

Handling Loading States in React: Improving UX and Performance

Learn to master loading state in React to improve UX. Discover how to conditionally render spinners while your API fetches data for your movie app.

Read more
ReactJune 26, 20263 min read

Refactoring for Scalability: Professionalizing Your React Architecture

Learn to master scalability through expert refactoring techniques. We'll extract reusable UI, optimize folder structures, and decouple logic for cleaner code.

Part of the course

Intermediate React: Hooks, State & Data Patterns

intermediate · Lesson 28 of 48

  1. 1

    Mastering useRef for DOM Access

    4 min
  2. 2

    Persistent Mutable Values with useRef

    4 min
  3. 3

    Memoizing Expensive Calculations with useMemo

    3 min
Read more
ReactJune 26, 20263 min read

Mastering Programmatic Navigation with useNavigate in React

Learn how to use the useNavigate hook in React Router to trigger navigation on events, handle redirects, and manage complex user flows in your dashboard.

Read more
4

Optimizing Function References with useCallback

3 min
  • 5

    Introduction to Custom Hooks

    4 min
  • 6

    Building a useLocalStorage Hook

    4 min
  • 7

    Refactoring Dashboard Settings

    4 min
  • 8

    Complex State with useReducer

    3 min
  • 9

    Managing Object-Based State

    3 min
  • 10

    Introduction to Context API

    3 min
  • 11

    Architecting Global State with Context and Reducer

    3 min
  • 12

    Implementing Theme Context

    4 min
  • 13

    Structuring State for Performance

    3 min
  • 14

    Handling Authentication State

    3 min
  • 15

    Integrating Reducers with Auth State

    3 min
  • 16

    Introduction to React Router

    3 min
  • 17

    Dynamic Routing with URL Parameters

    3 min
  • 18

    Nested Routes and Layouts

    4 min
  • 19

    Protected Routes for Authenticated Views

    3 min
  • 20

    Programmatic Navigation

    3 min
  • 21

    Building the Dashboard Navigation Structure

    3 min
  • 22

    Asynchronous Data Lifecycle

    3 min
  • 23

    Caching Strategies with React Query

    4 min
  • 24

    Mutations and Data Updates

    4 min
  • 25

    Synchronizing Client and Server State

    3 min
  • 26

    Integrating Live Data into the Dashboard

    3 min
  • 27

    Error Handling and Loading UI

    3 min
  • 28

    Controlled vs Uncontrolled Components

    3 min
  • 29

    Real-time Form Validation

    3 min
  • 30

    Schema-based Validation with Zod

    3 min
  • 31

    Handling Multi-step Forms

    3 min
  • 32

    Optimizing Form Submissions

    3 min
  • 33

    Performance Profiling with React DevTools

    3 min
  • 34

    Refactoring for Scalability

    3 min
  • 35

    Finalizing Dashboard Data Flow

    Coming soon
  • 36

    Deploying the Application

    Coming soon
  • 37

    Advanced Hook Composition

    Coming soon
  • 38

    Implementing Middleware for State

    Coming soon
  • 39

    Advanced Context Patterns

    Coming soon
  • 40

    Router Loaders and Data Prefetching

    Coming soon
  • 41

    Complex Route Guards

    Coming soon
  • 42

    Handling Large Datasets in UI

    Coming soon
  • 43

    Testing Hooks and Components

    Coming soon
  • 44

    Managing Global Modals

    Coming soon
  • 45

    Implementing Keyboard Shortcuts

    Coming soon
  • 46

    Optimizing Asset Loading

    Coming soon
  • 47

    Internationalization Basics

    Coming soon
  • 48

    Managing WebSocket Connections

    Coming soon
  • View full course