How to Use Tabindex for Better Keyboard Navigation: Complete Guide (2026)
What Is Tabindex?
The tabindex attribute controls whether — and in what order — an HTML element receives keyboard focus when a user presses Tab. Only three values are meaningful: tabindex="0" adds an element to the natural Tab order, tabindex="-1" makes it programmatically focusable but skipped by Tab, and any positive value (1, 2, …) overrides DOM order and is almost always a bug.
Key Facts (TL;DR)
tabindex="0": element is focusable in normal DOM order — use on custom interactive widgets like<div role="button">.tabindex="-1": programmatically focusable via.focus(), skipped by Tab — use for skip-link targets, modal containers, error summaries.tabindex="1"or higher: manually overrides Tab order. Almost always wrong. Violates WCAG 2.4.3 Focus Order (Level A) in most cases.- WCAG criteria: SC 2.1.1 Keyboard (A), SC 2.4.3 Focus Order (A), SC 2.4.7 Focus Visible (AA).
- Audit threshold: automated accessibility tooling flags any element with
tabindex >= 1as a defect. - Business impact: the WebAIM 2024 Million report found 96.3% of homepages had detectable WCAG 2 failures — broken Tab order is one of the most common, and it directly blocks roughly 1 in 4 users who rely on keyboard or assistive tech (CDC disability data, 2024).
Think of tabindex like seat numbers at a theatre. With no numbers, people naturally fill rows in order (DOM order — the right outcome). Add tabindex="0" and an extra chair joins the row. Add tabindex="1", "2", "3"and you've told three specific guests to sit first regardless of where they are — everyone else now starts after them, and any new chair you add throws the whole numbering out.
Why Tabindex Matters for Accessibility
Keyboard navigation isn't a niche concern. It's how users with motor impairments, blind users on screen readers, power users, and anyone with a temporarily injured hand interact with your site. tabindex is the attribute that decides whether they can use it at all.
- WCAG 2.1.1 Keyboard (Level A): every interactive element must be operable through a keyboard interface. A custom
<div>with a click handler and notabindexsilently excludes every keyboard user. - WCAG 2.4.3 Focus Order (Level A): focus order must preserve meaning and operability. Positive
tabindexvalues reorder elements in a way that almost always conflicts with the visual reading order — a direct failure. - WCAG 2.4.7 Focus Visible (Level AA): the focus indicator must be visible.
tabindex="-1"can hide elements from the natural sequence; if you focus them programmatically, you also need to make the focus ring visible. - Screen-reader navigation: screen readers usually traverse content in DOM order regardless of
tabindex. Positive values create a mismatch where keyboard-only users land somewhere different from screen-reader users — a deeply confusing experience. - Maintenance cost: a form with
tabindex="1"through"7"needs renumbering every time someone adds, removes, or reorders a field. DOM source order needs nothing.
How the Three Tabindex Values Actually Work
Most tabindex bugs come from misunderstanding what each value does. Here is the precise behaviour the browser applies.
tabindex="0" — Add to Natural Order
Adds a non-focusable element (like <div>, <span>, or <li>) to the keyboard Tab sequence at its DOM position. Use it whenever you build a custom interactive widget that the browser doesn't already make focusable. Don't add it to elements that are already focusable (like <button> or <a href>) — it is redundant and only adds noise.
tabindex="-1" — Programmatic Focus Only
Removes the element from the Tab sequence but allows JavaScript to call element.focus() on it. Use for: skip-link targets like <main id="main" tabindex="-1">, modal dialog containers that should receive focus on open, error summaries you want to scroll into view after form validation, and route-change focus targets in single-page apps.
tabindex="1" and Higher — Almost Always a Bug
Forces the element to come first (or n-th) in Tab order, ahead of every element with tabindex="0" or natural focusability. Three reasons it breaks accessibility: (1) it conflicts with visual and source order, violating WCAG 2.4.3; (2) once a user finishes the positive-tabindex group, focus jumps back to the start of natural order — confusing and disorienting; (3) screen readers ignore it and walk DOM order anyway, so keyboard-only and screen-reader users get different journeys.
<!-- BAD: positive tabindex creates a manual queue
that breaks DOM order and screen-reader parity. -->
<form>
<label>Name <input type="text" tabindex="3" /></label>
<label>Email <input type="email" tabindex="1" /></label>
<label>Phone <input type="tel" tabindex="2" /></label>
<button type="submit" tabindex="4">Submit</button>
</form>
<!-- GOOD: rely on DOM source order. tabindex="0"
only on the custom widget, tabindex="-1" only
on the programmatic focus target. -->
<form>
<label>Email <input type="email" /></label>
<label>Name <input type="text" /></label>
<label>Phone <input type="tel" /></label>
<!-- Custom widget: not focusable by default,
so it needs tabindex="0". -->
<div role="button" tabindex="0" onClick={submit}>
Submit
</div>
</form>
<!-- Skip-link target: focusable from JS but not
in the Tab order. -->
<main id="main-content" tabindex="-1">…</main>How to Check Tabindex Issues on Your Site
tabindex problems are usually invisible until a keyboard user tries to navigate. A combination of automated scanning and manual Tab-key testing surfaces them quickly.
- Greadme deep scan — flags every element with positive
tabindex, every interactive ARIA role missing keyboard focusability, and pairs each finding with an AI-generated fix or one-click GitHub PR. - Greadme crawler scan — runs the same checks across every indexable page, so you can spot which page templates carry the legacy positive-
tabindexpatterns. - Greadme AI visibility analyzer — tells you whether failing keyboard accessibility is dragging down rankings that AI search engines source from.
- Manual keyboard test: press Tab from the address bar and walk through every interactive element. Focus order should match visual reading order; nothing should be skipped, and no focus indicator should disappear.
- Open-source axe-core — the engine behind most accessibility scanners; it checks
tabindexrules and ARIA focusability locally during builds. - Screen reader walkthrough: NVDA, JAWS, or VoiceOver. If the screen-reader order and Tab order disagree, you almost certainly have a positive
tabindexsomewhere. - Chrome DevTools Accessibility pane — inspect each element's computed accessibility tree, including its tabindex value and whether it is reachable via keyboard.
10 Tabindex Best Practices
1. Prefer Native Interactive Elements
<button>, <a href>, <input>, <select>, and <textarea> are focusable by default. Reach for them before reaching for tabindex. You get keyboard, screen-reader, and form-semantics behaviour for free.
2. Never Use Positive Tabindex
If you find yourself wanting tabindex="1", the answer is to reorder the DOM instead. Visual order and source order should match — that is exactly what WCAG 2.4.3 asks for.
3. Use tabindex="0" Only on Custom Widgets
When you build a <div role="button">, <div role="tab">, or any other custom interactive component, add tabindex="0" so keyboard users can reach it. Never add it to a native <button>.
4. Use tabindex="-1" for Programmatic Focus Targets
Skip-link targets, modal containers, route-change landing points, and post-validation error summaries all need to be programmatically focusable but not in the Tab sequence. tabindex="-1" is the right fit.
5. Don't Add Tabindex to Non-Interactive Content
Adding tabindex="0" to a paragraph or heading creates a focus stop with nothing to do — confusing for keyboard users who expect each Tab press to land on something they can act on.
6. Pair tabindex="0" With Keyboard Event Handlers
A custom button needs more than focusability — it needs to respond to Enter and Space. Add onKeyDown handlers that trigger the same action as the click handler.
7. Manage Focus on Route Changes in SPAs
In single-page apps, browsers don't reset focus when the URL changes. Move focus programmatically to a tabindex="-1" heading or main region after each route transition so screen readers announce the new page.
8. Trap Focus Inside Modal Dialogs
When a modal opens, focus should move into it (use tabindex="-1" on the dialog and .focus() it), and Tab should cycle within it until the modal closes. On close, return focus to the element that opened it.
9. Keep Focus Visible (WCAG 2.4.7)
tabindex only matters if the user can see where focus has landed. Don't override the browser's focus ring with outline: none unless you replace it with an equally visible custom indicator.
10. Audit Before Each Release
Tab order regressions usually arrive with someone refactoring a layout. Run an automated tabindex check in CI plus a manual Tab walkthrough of any page that changed structure.
Common Tabindex Mistakes and Fixes
Problem: tabindex="1" Used to Make a Field "Focus First"
What's happening: a developer wanted the email field to receive focus before the name field, so they added tabindex="1". Now keyboard users land on email first, then jump backwards to name — and any new field added later inherits a confusing position.
Fix: reorder the DOM so the email field appears before the name field in source. Remove every positive tabindex. If a specific field should receive focus on page load, call .focus() on it once after mount.
Problem: Custom <div role="button"> Without Tabindex
What's happening: the element looks like a button and clicks like a button, but Tab skips over it. Keyboard and screen-reader users can't activate it at all — a silent WCAG 2.1.1 failure.
Fix: add tabindex="0" and an onKeyDown handler that triggers the action on Enter and Space. Better still, replace it with a real <button>.
Problem: Skip-Link Target Has No tabindex="-1"
What's happening: the skip link points to <main id="main">, but in some browsers focus does not move to a non-interactive element on hash navigation. Users press the skip link and nothing seems to happen.
Fix: add tabindex="-1" to the <main> element. This makes it programmatically focusable so the browser can place focus there reliably.
Problem: tabindex="0" on Decorative Content
What's happening: a static <p> or hero image carries tabindex="0", so keyboard users get a focus stop on something they can't do anything with. Tab order doubles in length and feels broken.
Fix: remove the attribute. tabindex="0" belongs only on elements that are genuinely interactive.
Tabindex Values Compared
A direct side-by-side reference for which value to reach for in each situation.
| Value | In Tab order? | Programmatically focusable? | Use it for |
|---|---|---|---|
| (no attribute) | Native interactive elements only | Same | Default — best choice for native HTML controls. |
tabindex="0" | Yes, in DOM order | Yes | Custom widgets like <div role="button">, custom tabs, custom listboxes. |
tabindex="-1" | No | Yes | Skip-link targets, modal containers, error summaries, SPA route-change focus targets. |
tabindex="1"+ | Yes, before all other focusable elements | Yes | Almost never. Violates WCAG 2.4.3 Focus Order in nearly all real-world cases. |
FAQ
What does tabindex="0" do?
It makes an element that is not normally focusable (like a <div>) part of the keyboard Tab sequence, at its DOM position. Use it on custom interactive widgets. It is redundant — and considered noise — on elements that are focusable by default such as <button> or <a href>.
What does tabindex="-1" do?
It removes the element from the Tab sequence but lets JavaScript move focus to it via element.focus(). The standard use cases are skip-link targets, modal containers, error-summary panels, and route-change landing points in single-page apps.
Why is positive tabindex considered an anti-pattern?
Three reasons. It almost always conflicts with visual and source order, which violates WCAG 2.4.3 Focus Order. Once a user passes the last positive value, focus jumps back to the start of the natural order — confusing and disorienting. And screen readers usually walk the DOM regardless, so keyboard-only and screen-reader users end up on different paths through the same page.
Does tabindex affect screen readers?
Mostly no — screen readers navigate by DOM order, headings, landmarks, and the accessibility tree, not by tabindex. Positive values are typically invisible to them, which is exactly why they create a mismatch with keyboard-only users.
Should I add tabindex="0" to every interactive element?
No. Add it only when the element is not already focusable. Native form controls, links, and buttons are focusable by default; adding tabindex="0"to them is redundant and clutters the markup. Reserve it for custom widgets that the browser doesn't recognise as interactive.
How does tabindex affect AI search engines like ChatGPT and Perplexity?
Indirectly but meaningfully. Generative search systems most often surface pages that already rank well in traditional search, and Google factors keyboard-accessibility signals into its quality and Page Experience evaluations. A page where positive tabindex creates broken Tab order is more likely to fail those checks, lose ranking, and never be sourced by AI Overviews or chat-based answer engines.
How do I fix a focus order that doesn't match visual order?
Reorder the DOM so source order matches the visual layout, then remove every positive tabindex. Use CSS Grid's orderproperty only when truly necessary, and verify with a manual Tab walkthrough that focus follows the same path as a sighted user's reading flow.
Conclusion
tabindex is a small attribute with outsized impact. Use 0 sparingly to add custom widgets to the natural Tab order, use -1 for programmatic focus targets like skip-link destinations and modals, and never use a positive value. Combined with semantic HTML and visible focus styles, those rules satisfy WCAG 2.1.1, 2.4.3, and 2.4.7 — and keep your site usable for the roughly 1 in 4 people who depend on keyboard or assistive technology.
Run a Greadme deep scan to surface every positive tabindex, every interactive ARIA role missing focusability, and every focus-order regression on your site — each paired with an AI-generated fix or a one-click GitHub PR.
