BlogReactJS

How to check and uncheck a checkbox in React (controlled vs uncontrolled)

Written by Codemzy on August 16th, 2023

Checkboxes are pretty common form elements, and in this blog post, I'll show you the two ways to check and uncheck a checkbox in ReactJS.

In this blog post, we will look at:

  • Uncontrolled checkbox components
  • Controlled checkbox components

I prefer to use controlled checkbox components - but to understand why, let's start with an uncontrolled checkbox.

Uncontrolled checkbox component

The first (and possibly simplest) way to check and uncheck a checkbox in React is with an uncontrolled checkbox component. And this simply means, React doesn't control it - the checkbox is handled by the DOM (like in the olden days!).

Because forms (and checkboxes) existed before React, you don't need React to handle them - if you don't want.

// checkbox
function Checkbox() {
  return (
    <div>
      <input type="checkbox" id="one" name="one" />
      <label for="one">Checkbox 1</label>
    </div>
  );
};

Go ahead, use the checkbox below, and click it a few times. It works!

<div id="app"></div>
// checkbox component
function Checkbox() {
  return (
    <div className="p-10 flex justify-center items-center h-screen">
      <input type="checkbox" id="one" name="one"/>
      <label for="one" className="ml-2 font-bold">Uncontrolled Checkbox</label>
    </div>
  );
};
// render react
ReactDOM.render(
  <Checkbox />,
  document.getElementById('app')
);

Yeah, ok that works, but what if I want the checkbox checked to start with (instead of unchecked)?

React checkbox with defaultChecked

No problem. Just pass a defaultChecked={true} prop.

But let's not hardcode that in, since we might want a couple of checkboxes, one checked and one unchecked to start us off.

You can pass defaultChecked as a prop to the component, and then decide what checkboxes you want checked.

And while we are at it, let's pass the id and name as props so we can reuse the Checkbox component for multiple checkboxes. And we can use the children prop for the label.

// checkbox
function Checkbox({ id, name, defaultChecked, children }) {
  return (
    <div>
      <input type="checkbox" id={id} name={name} defaultChecked={defaultChecked} />
      <label for={id}>{children}</label>
    </div>
  );
};

Now we can use our checkbox like this:

<Checkbox id="example-checked" name="checked-checkbox" defaultChecked={true}>Default Checked</Checkbox>
<Checkbox id="example-unchecked" name="unchecked-checkbox" defaultChecked={false}>Default Unchecked</Checkbox>

Make sure you give each Checkbox a unique id.

Is the uncontrolled checkbox checked or unchecked?

Since this checkbox is uncontrolled, once the user has clicked it a few times, how do we know if it is checked or unchecked?

Let's say a user clicked a submit button on a form, and we want to let the server know if the checkbox was checked or not - how do we do it?

const checkboxRef = React.useRef(null);
  
function onSubmit() {
  console.log(checkboxRef.current.checked);
};
  
return (
  <form onSubmit={onSubmit}>
    <input ref={checkboxRef} type="checkbox" id="one" name="one" defaultChecked={true} />
    <label for="one">Checkbox 1</label>
  </div>
);

Why might want a controlled checkbox instead

So far we have looked at examples of an uncontrolled checkbox in React. And it works!

But an uncontrolled checkbox in React does come with drawbacks. While this checkbox works, and it's all good like it should, it's not very well integrated with React.

  • What if you want something to happen when the user checks the box?
  • What if you want to know when the user checks the box?
  • What if you need to validate the form data (maybe with other form elements)?
  • What if you want to check the box for them?*

*You could do this with the ref and checkboxRef.current.click(); but I'd argue that state is a better option when combined with any of the other needs listed above.

So let's look at how we can useState to create a controlled checkbox component instead!

Controlled checkbox component

So we made our uncontrolled checkbox component, but we didn't know when the checkbox was checked, and when it was unchecked. Let's give React (and ourselves!) a little more control over things!

To create a controlled checkbox, we need to give it some state, and it's the state (not the DOM) that tells the checkbox if it's checked - or not.

// controlled checkbox
function ControlledCheckbox({ id, name, children, defaultChecked = false }) {
  // state
  const [checked, setChecked] = React.useState(defaultChecked);
  
  return (
    <div className="my-5">
      <input type="checkbox" id={id} name={name} checked={checked} onChange={() => setChecked(!checked)} />
      <label for={id} className="ml-1">{children}</label>
    </div>
  );
};

We now useState for our checked state. And pass both checked and an onChange handler to the checkbox input.

<input type="checkbox" 
  id={id} 
  name={name} 
  checked={checked} // if the checkbox is checked (or not)
  onChange={() => setChecked(!checked)} // toggles the checked state
/>

The onChange handler calls a function that will update the state to !checked. If checked is true, this will set it to false, and vice versa.

Notice that we don't pass defaultChecked to the input element anymore. Since the checked state handles if the checkbox is checked, we can set defaultChecked as the initial value for checked to achieve the same thing.

Here's the controlled checkbox in action:

<div id="app"></div>
// checkbox component
function Checkbox({ id, name, children, defaultChecked = false }) {
  // state
  const [checked, setChecked] = React.useState(defaultChecked);
  // the checkbox
  return (
    <div className="p-10 flex justify-center items-center h-screen">
      <input type="checkbox" id={id} name={name} checked={checked} onChange={() => setChecked(!checked)}/>
      <label for={id} className="ml-2 font-bold">{children}</label>
    </div>
  );
};
// render react
ReactDOM.render(
  <Checkbox id="controlled-one" name="controlled-checkbox" defaultChecked={true}>Controlled Checkbox</Checkbox>,
  document.getElementById('app')
);

Ok, so what's the benefit here? We could already check and uncheck the checkbox before with the uncontrolled component. So why do we need a controlled one?

Well, here's the magic. 🪄

Whenever the state updates, the component re-renders. And that means that React now knows when the component is checked, and can do things, like focus on a new element, show a hidden field, or just display a bit of text.

You can also do things like build a select all checkbox, which I've also written a blog about!

In this example, I'll just show a "checked" message at the end of the label with {checked && "(checked!)"} - try doing that with an uncontrolled checkbox!

// controlled checkbox
function ControlledCheckbox({ id, name, children, defaultChecked = false }) {
  // state
  const [checked, setChecked] = React.useState(defaultChecked);
  
  return (
    <div className="my-5">
      <input type="checkbox" id={id} name={name} checked={checked} onChange={() => setChecked(!checked)} />
      <label for={id} className="ml-1">{children} {checked && "(checked!)"}</label>
    </div>
  );
};

Or you could something, like make the label grey if the checkbox isn't checked.

<div id="app"></div>
// checkbox component
function Checkbox({ id, name, children, defaultChecked = false }) {
  // state
  const [checked, setChecked] = React.useState(defaultChecked);
  // the checkbox
  return (
    <div className="p-10 flex justify-center items-center h-screen">
      <input type="checkbox" id={id} name={name} checked={checked} onChange={() => setChecked(!checked)}/>
      <label for={id} className={`ml-2 font-bold ${checked ? "text-black" : "text-gray-500"}`}>{children}</label>
    </div>
  );
};
// render react
ReactDOM.render(
  <Checkbox id="controlled" name="controlled-checkbox">Controlled Checkbox</Checkbox>,
  document.getElementById('app')
);

Personally, I prefer using a controlled checkbox, because you get that instant knowledge when the checkbox is checked. Let's say, when a user ticks the checkbox, you want to run some action like signing them up to a mailing list or updating their preferences, you can run a useEffect when the checked value changes.

React.useEffect(() => {
  if (checked) {
    // do something
  }
}, [checked]);

Three cheers for controlled components!


If you're having any trouble getting your checkbox onChange to check your checkbox, or it needs two clicks - check out my blog post on React checkbox onChange not firing/updating the first time for problem-solving tips!