react-doctor/no-noninteractive-tabindex
Reserve tabIndex for interactive elements or interactive roles; remove it from non-interactive ones.
- Category: Accessibility
- Severity: warn
- Source:
oxlint-plugin-react-doctor - Framework: global
- Enabled when: always
- Documentation: https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/no-noninteractive-tabindex
Validation prompt
Use this to decide whether a fired diagnostic is real or a false positive.
Fires when a native HTML element that is neither inherently interactive nor carrying an interactive/allowed role has a tabIndex resolving to a non-negative integer (tabIndex="0", tabIndex={0}, or positive) — e.g. <div tabIndex="0" /> or <article tabIndex={0} />. Negative values (tabIndex="-1"), role="tabpanel" or interactive roles (button, menu, grid, listbox…), interactive tags, and custom components are exempt; non-HTML element names are skipped entirely. False positive: by default allowExpressionValues is true, so a dynamic value like <div tabIndex={someVar} /> or role={cond ? "button" : "link"} is NOT flagged — only configs that set allowExpressionValues:false make expression containers fire.
Fix prompt
Use this once validation confirms the diagnostic is real.
If the element is genuinely non-interactive, drop the tabIndex entirely so it stays out of the tab order; assistive tech already provides traversal. If you need it focusable only programmatically (e.g. to move focus after an action), use tabIndex={-1} instead of 0. If it is actually meant to be operable, give it a real interactive role plus keyboard handlers — <div role="button" tabIndex={0} onClick={…} onKeyDown={…} /> — or use a native <button>. See https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/no-noninteractive-tabindex
Related rules
More Accessibility rules from the rules reference:
react-doctor/no-outline-none: Use `:focus-visible { outline: 2px solid var(--color-muted); outline-offset: 2px }` to show focus only for keyboard users while hiding it for mouse clicksreact-doctor/no-tiny-text: Use at least 12px for body content, 16px is ideal. Small text is hard to read, especially on high-DPI mobile screensreact-doctor/prefer-html-dialog: Replace the hand-rolled modal wrapper with a native <dialog> opened via dialog.showModal()react-doctor/prefer-tag-over-role: Replace role with the semantic HTML element when one exists.react-doctor/require-reduced-motion: Project ships a motion library but never gates animation on the user's reduced-motion preference — add `useReducedMotion()` / `<MotionConfig reducedMotion="user">` or a `@media (prefers-reduced-motion: reduce)` query