This React warning appears when you provide a checked prop to a checkbox input without an onChange handler, creating a read-only field that can't respond to user interactions. Fix it by adding an onChange handler for controlled components or using defaultChecked for uncontrolled components.
This warning occurs when React detects that you've set the `checked` prop on a checkbox (or radio button) input element but haven't provided an `onChange` handler to manage state updates. In React's component model, form inputs can be either controlled (where React manages the state) or uncontrolled (where the DOM manages the state). When you provide a `checked` prop, React treats the input as a controlled component, meaning you're telling React "I want to control this input's value." However, without an `onChange` handler, React has no way to update the state when the user clicks the checkbox, effectively creating a read-only field that appears interactive but doesn't respond to clicks. This warning is React's way of preventing a common bug where developers accidentally create non-functional form inputs. The checkbox will render with the initial checked state but won't update when clicked, leading to poor user experience and potential confusion.
The recommended solution is to create a controlled component by adding an onChange handler that updates your state:
import { useState } from 'react';
function MyComponent() {
const [isChecked, setIsChecked] = useState(false);
return (
<input
type="checkbox"
checked={isChecked}
onChange={(e) => setIsChecked(e.target.checked)}
/>
);
}This creates a fully controlled checkbox where React manages the state and updates it based on user interactions.
If you only need to set an initial value and don't need to control the checkbox state, use defaultChecked instead of checked:
function MyComponent() {
return (
<input
type="checkbox"
defaultChecked={false}
/>
);
}This creates an uncontrolled component where the DOM manages the checkbox state. The value can change but React doesn't track it. Use this when you'll read the value on form submission rather than reacting to changes immediately.
If the checkbox is intentionally read-only (display-only, no user interaction), add the readOnly prop to suppress the warning:
function MyComponent({ isEnabled }) {
return (
<input
type="checkbox"
checked={isEnabled}
readOnly
/>
);
}This makes it explicit that the field is non-interactive by design. Note that readOnly checkboxes can still be focused and copied, but not changed by the user.
When working with multiple checkboxes, manage each checkbox's state properly:
function MultiCheckbox() {
const [checkedItems, setCheckedItems] = useState({
option1: false,
option2: true,
option3: false
});
const handleChange = (key) => {
setCheckedItems(prev => ({
...prev,
[key]: !prev[key]
}));
};
return (
<div>
<input
type="checkbox"
checked={checkedItems.option1}
onChange={() => handleChange('option1')}
/>
<input
type="checkbox"
checked={checkedItems.option2}
onChange={() => handleChange('option2')}
/>
<input
type="checkbox"
checked={checkedItems.option3}
onChange={() => handleChange('option3')}
/>
</div>
);
}For checkboxes used outside traditional forms (like toggle switches), you can use onClick with readOnly to avoid the warning:
function ToggleSwitch({ isOn, onToggle }) {
return (
<input
type="checkbox"
checked={isOn}
onClick={onToggle}
readOnly
/>
);
}However, note that onChange is still preferred for better accessibility and semantic HTML.
If you're using uncontrolled components and need to access the value (e.g., on form submission), use refs:
import { useRef } from 'react';
function FormWithCheckbox() {
const checkboxRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
const isChecked = checkboxRef.current.checked;
console.log('Checkbox value:', isChecked);
};
return (
<form onSubmit={handleSubmit}>
<input
type="checkbox"
ref={checkboxRef}
defaultChecked={false}
/>
<button type="submit">Submit</button>
</form>
);
}Ensure you're not mixing controlled and uncontrolled patterns, which can cause warnings and bugs:
❌ Wrong - Mixed pattern:
<input type="checkbox" checked={value} defaultChecked={false} />❌ Wrong - Switching from controlled to uncontrolled:
const [value, setValue] = useState(true);
// Later setting value to undefined/null makes it uncontrolled✅ Correct - Pick one pattern:
// Controlled
<input type="checkbox" checked={value} onChange={handleChange} />
// Or uncontrolled
<input type="checkbox" defaultChecked={false} />Controlled vs Uncontrolled Components: The fundamental choice between controlled and uncontrolled components depends on your use case. Controlled components give you more power - you can validate, transform, or react to changes immediately. They're ideal for complex forms, real-time validation, or when you need to coordinate multiple inputs. Uncontrolled components are simpler and can be more performant for large forms where you only need values on submission.
Performance Considerations: For forms with many checkboxes, controlled components can cause re-renders on every change. Consider using React.memo() or useMemo() to optimize performance, or use uncontrolled components with refs if you only need values on submission.
Accessibility Notes: Always include proper labels with checkboxes for accessibility. Use <label htmlFor="id"> or wrap the checkbox in a <label> tag. Screen readers need these associations to properly announce checkbox purposes.
Form Libraries: Consider using form libraries like React Hook Form or Formik for complex forms. These handle the controlled/uncontrolled complexity for you and provide built-in validation, error handling, and performance optimizations.
Testing: When testing controlled checkboxes, use userEvent.click() from @testing-library/user-event rather than fireEvent.change() for more realistic user interactions that properly trigger both onChange and onClick events.
TypeScript: When using TypeScript, type your onChange handler as: onChange: (e: React.ChangeEvent<HTMLInputElement>) => void for proper type safety.
React.FC expects children prop to be defined
React.FC no longer includes implicit children prop
Warning: You provided a `selected` prop to a form field without an `onChange` handler
You provided a 'selected' prop without an onChange handler
Failed to load source map from suspense chunk
How to fix "Failed to load source map from suspense chunk" in React
Prop spreading could cause security issues
Prop spreading could cause security issues
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency