The difference between controlled & uncontrolled inputs in React

I am sure you must be thinking, “Why would we need to have these concepts at all when we can just write good-looking, semantic HTML like below?”


When you follow the vanilla JS way of doing things, you select the class name of the input element above and do our job of getting its value from the target property inside the event.

However, it is good to know how important forms are for websites as, today, almost every website has a call-to-action form.

How to write a form in React

As you might have seen, React has all its HTML inside its body, and it respects the writing language of the web. Hence, it gives us two ways in which we can build forms, the conventional way is known as Uncontrolled Inputs, and the powerful way is called Controlled Inputs.

ProTip: It is easier to understand if you open your React dev tools alongside this code so that you can always refer to it to know what current state is.

Uncontrolled inputs

This is where we get to keep the traditional way of writing HTML inputs and, guess what, it’s pretty much the same as the code snippet above (refer below):

class Box extends React.Component (
    	render() {
    		return (
    			<label>
    				<input type="text" /> I 😍 HTML
    			</label>
    		);
    	}
    )

But there is one catch – how will we access the input? Don’t worry, React has given us a dope way of doing that too!

Introducing refs

Refs provide a way to access DOM nodes or React elements created in the render method.

For more information, go to the React documentation

So, ref is nothing but an attribute, like we have in HTML, and it provides a way for us to get access to the DOM so that we can get the user-typed data from the input. Let’s see how:

  class Box extends React.Component {
    	fluffy = React.createRef()

    	letsChange = () => {
    		console.log(this.fluffy.current.value) // input value gets logged
    	}

    	render() {
    		return (
    			<label>
    				<input type="text" ref={this.fluffy} onChange={this.letsChange} />
    				I 😍 HTML
    			</label>
    		)
    	}

Code

In the code below, click the output tab and enter your name in the input tag. You can see the changes in the console tab.

import React from 'react';

import ReactDOM from 'react-dom';
import Box from './app.js';

ReactDOM.render(
  <Box />, 
  document.getElementById('root')
);
  • To use Ref in your input, you need to first initialize the ref method in your class by calling React.createRef(). We have named it fluffy here. We’ll mention that inside our ref attribute inside the input tag, like above.
  • Then, comes our evergreen method, onChange, which is required whenever there is some kind of change.
  • And lastly, inside letsChange, which is called when there are some changes in input, we get the input value by this.fluffly.current.value. This gives us the user-typed text that we can then choose to use however we want.

Controlled inputs

This gives all the power to your plain old input forms. This is the de facto standard of creating forms in React. It’s called controlled in the first place because we are controlling its state ourselves. We need to store its value inside the state object and keep it updated in real-time as well as the user types. So, let’s get our hands dirty.

import React from 'react';


import ReactDOM from 'react-dom';
import Box from './app.js';

ReactDOM.render(
  <Box />, 
  document.getElementById('root')
);

We’re unstoppable! Now ,let’s understand the flow of the process above.

  • As mentioned earlier, we store the text in our state itself, so we create a state object and store an empty key named fluffy, which will store the user input as they type.
  • Now what about change? We pass an onChange attribute to input that calls letsChange. This is the callback that first occurs when the user makes the tiniest of changes.
  • Inside letsChange we are passing our all-time favorite argument, event, which is used to set the state so that we can see it on the screen.
  • It’s time to render what we stored in fluffy to the screen, so we create a value attribute in the input tag, as per HTML guidelines, and store the fluffy value inside it.

The above process takes place every time any change is made by the user by mutating the state object.

Benefits of using controlled inputs

This method gives us enormous control over the state, which, in turn, gives us power over the input. To see this, you can remove or comment out the letsChange function and try typing in the box. You’ll see that nothing gets input! Why is that?

The reason for this is that the input box directly renders the text stored inside state which comes through the letsChange function. Hence, this system allows us to filter the values provided by the user before displaying it on the screen itself. Let’s say you want to do some sort of custom validation for the user data, you can easily put the code into the letsChange function and see the magic.

// ...

letsChange = event => {
  let input = event.target.value;
  input = input.replace(/[0-9]/g, "");
  this.setState({ fluffy: input });
};

// ...

You won’t be able to input any numbers in the input because the letsChange function replaces them with empty strings as soon as you type something. You can also have a button that can be activated only if specific conditions are satisfied. The possibilities are endless.

If that doesn’t make sense, let me give you another example of this type.

Let’s say we need a dropdown menu of various flowers for the user to choose from and we write it as:

class Multiple extends React.Component {
  state = {
    flower: "",
  };

  letsChange = event => {
    this.setState({ flower: event.target.value });
  };

  render() {
    return (
      <label>
        Your Favourite Flower: 🌸
        <select value={this.state.flower} onChange={this.letsChange}>
          <option value="rose">Rose 🌹</option>
          <option value="sunflower">Sunflower 🌻</option>
          <option value="tulip">Tulip 🌷</option>
          <option value="hibiscus">Hibiscus 🌺</option>
        </select>
      </label>
    );
  }
}

In the above example, if you try putting any of the four values that we gave in option in place of flower inside state, then you’ll see that the default item selected will be that flower. You can manipulate the selection from the letsChange function as well.

P.S.: There’s this file input tag whose value is only read-only, so it is by default an uncontrolled component in React. Further reading on this is mentioned below. Rest assured, you’re good to go.

Further Reading

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved