To make clickable elements accessible in React, use a real button or link instead of a div with an onClick handler. Native elements ship with keyboard support, focus, and a role, so keyboard and screen reader users get the same behavior without extra code.
Prefer a real button
A div with an onClick handler looks clickable but is not reachable by keyboard and has no role for assistive technology.
<div onClick={handleSave}>Save</div>Swap it for a button and the browser fills in the gaps. The button is focusable, activates on Space and Enter, and is announced as a button.
<button onClick={handleSave}>Save</button>A div would need a role, a tabIndex, and a key handler just to match what the plain button already does. Reaching for those attributes is usually a sign the wrong element was chosen.
Space and Enter come for free
Native buttons activate with Space and Enter, and mouse clicks already work. A div requires you to re-implement all three interactions, which is easy to get wrong and easy to forget when the component grows. Start from the button and you inherit the correct behavior.
Give it an accessible name
An icon-only button has no text, so a screen reader cannot announce its purpose. Add an aria-label that describes the action.
<button aria-label="Close dialog" onClick={handleClose}>X</button>The label replaces the letter X in the accessible name, so assistive technology reads "Close dialog button". Visible text is usually clearer than an aria-label, but an icon-only control needs the attribute to stay meaningful.
Buttons and links are different
A link navigates somewhere, and a button performs an action. Use an anchor for navigation and a button for submitting, opening a dialog, or deleting. Styling one to look like the other does not change what it does, and the wrong role misleads keyboard and screen reader users.
Toggle and disabled states
A toggle button announces its on or off state with aria-pressed.
function MuteButton() {
const [muted, setMuted] = useState(false);
return (
<button aria-pressed={muted} onClick={() => setMuted(!muted)}>
Mute
</button>
);
}The label stays "Mute" whether pressed or not, and the state is exposed separately. A button that is temporarily unavailable should use the disabled attribute so it leaves the tab order, not just a dimmed style.
Do not hide the focus outline
A visible focus ring tells keyboard users where they are. Removing it without an equally visible replacement makes the page unusable by keyboard. Style the outline to match the design, but keep it visible.
What to learn next
For custom controls that must handle their own keys, handling keyboard events covers onKeyDown. Larger widgets need keyboard navigation patterns to move focus inside them.
Rune AI
Key Insights
- Prefer a button over a div with onClick.
- Buttons activate with Space and Enter for free.
- Use links for navigation, buttons for actions.
- Label icon-only buttons with aria-label.
- Use aria-pressed for toggles and disabled for inactive buttons.
Frequently Asked Questions
Why is a div with onClick not accessible?
What should I use instead of a clickable div?
How do I label an icon-only button?
Conclusion
Use a real button for actions and a real link for navigation. Add an accessible name to icon-only controls, and use aria-pressed and disabled for state. Native elements already do the hard part.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.