React, how to make a checked checkbox editable
By Flavio Copes
Learn how to make a React checkbox start checked but still editable by using the defaultChecked attribute instead of checked, which locks the input state.
To make a checkbox start checked but still editable in React, use the defaultChecked attribute instead of checked. Passing checked without an onChange handler locks the checkbox.
Here’s the full story. I had a checkbox in a React component:
<input name="enable" type="checkbox" />
and I wanted it to be checked by default, yet the user could change its value.
Using
<input name="enable" type="checkbox" checked="checked" />
didn’t work. The checkbox state could not be changed.
Why does checked freeze the checkbox?
In React, passing checked to an input makes it a controlled component. React takes ownership of its state: on every render, the checkbox shows exactly the value you passed.
Since I passed a fixed value and no onChange handler, React re-rendered the checkbox as checked after every click. React even warns you about this in the browser console: it tells you that you provided a checked prop to a form field without an onChange handler.
The fix: defaultChecked
The solution was to use the defaultChecked attribute:
<input name="enable" type="checkbox" defaultChecked={true} />
defaultChecked sets the initial state and then leaves the checkbox alone. The browser handles the clicks, and the user can toggle it freely. This is called an uncontrolled component.
If the checkbox needs to be checked depending if the value was checked in a variable (for example in an editing form when you are getting the actual value from the database) you can use
<input name="enable" type="checkbox" defaultChecked={existing_enable_value} />
When you need the value in state
If your component must react to the checkbox changing, use the controlled version with useState:
const [enabled, setEnabled] = useState(true)
<input
name="enable"
type="checkbox"
checked={enabled}
onChange={(event) => setEnabled(event.target.checked)}
/>
Now the checkbox is editable, and enabled always holds the current value.
One pitfall with defaultChecked
defaultChecked is only read on the first render. If the variable you pass changes later, the checkbox won’t update to match it.
So if you load the data after the component mounts and need the checkbox to follow it, switch to the controlled version above.