oxc-project/oxc · warning · OxcDiagnostic
`checked` should be used with either `onChange` or `readOnly
Error message
`checked` should be used with either `onChange` or `readOnly`.
What it means
Diagnostic from the oxlint rule `react/checked-requires-onchange-or-readonly` (plugin `react`, category `pedantic`). It fires on `<input>` elements (element type resolved via `get_element_type`, so components mapping to `input` count; `createElement('input', ...)` too) that carry a `checked` attribute but no `onChange` and no `readOnly` attribute. This mirrors React's controlled-component contract: a `checked` input without `onChange` is a frozen controlled field, and React itself logs a dev warning for it. Only explicit JSX attributes named `checked`/`onChange`/`readOnly` satisfy the fold - spread attributes do not. The `ignoreMissingProperties` option disables this check.
Source
Thrown at crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs:19
use oxc_ast::{
AstKind,
ast::{Argument, JSXAttributeItem, ObjectPropertyKind},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use schemars::JsonSchema;
use serde::Deserialize;
use crate::{
AstNode,
context::LintContext,
rule::{DefaultRuleConfig, Rule},
utils::{get_element_type, get_jsx_attribute_name, is_create_element_call},
};
fn missing_property(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("`checked` should be used with either `onChange` or `readOnly`.")
.with_help("Add either `onChange` or `readOnly`.")
.with_label(span)
}
fn exclusive_checked_attribute(checked_span: Span, default_checked_span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Use either `checked` or `defaultChecked`, but not both.")
.with_help("Remove either `checked` or `defaultChecked`.")
.with_labels([checked_span, default_checked_span])
}
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct CheckedRequiresOnchangeOrReadonly {
/// Ignore the requirement to provide either `onChange` or `readOnly` when the `checked` prop is present.
ignore_missing_properties: bool,
/// Ignore the restriction that `checked` and `defaultChecked` should not be used together.
ignore_exclusive_checked_attribute: bool,
}View on GitHub (pinned to e1e7af627c)
Solutions
- Add an `onChange` handler to make it a working controlled input
- Add `readOnly` when the value is intentionally static and displayed
- Switch to `defaultChecked` for uncontrolled initial state
- If props are supplied via spread, add `ignoreMissingProperties: true` to the rule config
Example fix
// before
<input type="checkbox" checked={done} />
// after
<input type="checkbox" checked={done} onChange={toggleDone} /> Defensive patterns
Strategy: validation
Validate before calling
npx oxlint --react/checked-requires-onchange-or-readonly src/
Type guard
// For library authors validating props before rendering:
function isControlledInputComplete(props: {
checked?: boolean;
defaultChecked?: boolean;
onChange?: () => void;
readOnly?: boolean;
}): boolean {
return props.checked === undefined
|| Boolean(props.onChange)
|| Boolean(props.readOnly);
} Prevention
- Controlled input = `checked` + `onChange`; static display = `checked` + `readOnly`; uncontrolled = `defaultChecked`
- Watch React's own dev warning 'You provided a `checked` prop to a form field without an `onChange` handler' - same contract
- Note that spread props do not satisfy the rule - declare `onChange`/`readOnly` explicitly or set `ignoreMissingProperties`
When it happens
Trigger: `<input type="checkbox" checked />`; `<input type="checkbox" checked={done} />`; `React.createElement('input', { checked: true })`; checked radio inputs without handlers.
Common situations: Display-only checkboxes in admin tables; porting HTML prototypes where React semantics were not considered; forgetting that `defaultChecked` (not `checked`) is the uncontrolled variant.
Related errors
- Use either `checked` or `defaultChecked`, but not both.
- `button` elements must have an explicit `type` attribute.
- `button` elements must have a valid `type` attribute.
- Component definition is missing display name.
- Invalid handler name: {handler_name}
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/8fe3fa079a52a1dc.
Report an issue: GitHub.