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

  1. Add an `onChange` handler to make it a working controlled input
  2. Add `readOnly` when the value is intentionally static and displayed
  3. Switch to `defaultChecked` for uncontrolled initial state
  4. 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

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


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/8fe3fa079a52a1dc. Report an issue: GitHub.