oxc-project/oxc · warning · OxcDiagnostic

Use either `checked` or `defaultChecked`, but not both.

Error message

Use either `checked` or `defaultChecked`, but not both.

What it means

Diagnostic from the oxlint rule `react/checked-requires-onchange-or-readonly` (plugin `react`, category `pedantic`). It fires when an `<input>` carries BOTH `checked` and `defaultChecked` attributes. The two are the controlled and uncontrolled ways to set checked state and are mutually exclusive; React ignores one of them and the component's behavior becomes unpredictable across renders. The diagnostic carries two labels - one on each attribute span - and the `ignoreExclusiveCheckedAttribute` option disables the check.

Source

Thrown at crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs:25

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,
}

declare_oxc_lint!(
    /// ### What it does
    ///
    /// This rule enforces `onChange` or `readOnly` attribute for checked property of input elements.
    /// It also warns when `checked` and `defaultChecked` properties are used together.

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Keep `checked` (controlled) and delete `defaultChecked`
  2. Keep `defaultChecked` (uncontrolled initial value) and delete `checked`
  3. If a wrapper may receive both, filter one out before spreading onto the input

Example fix

// before
<input type="checkbox" checked={done} defaultChecked={false} onChange={toggleDone} />

// 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

// Guard for wrapper components before spreading props:
const hasExclusiveCheckedConflict = (p: {
  checked?: unknown;
  defaultChecked?: unknown;
}): boolean => 'checked' in p && 'defaultChecked' in p;

Prevention

When it happens

Trigger: `<input type="checkbox" checked defaultChecked />`; `<input type="radio" checked={a} defaultChecked={b} />`; `createElement('input', { checked: true, defaultChecked: true })`.

Common situations: Merge of a default-initialization prop with a controlled prop; copy-paste between controlled and uncontrolled components; wrapper components forwarding both props to the inner input.

Related errors


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