Auditing a Third-Party React Component for Accessibility
"Accessible" is the most common word in a component library's README and one of the least reliable. It can mean the component follows the full ARIA pattern with keyboard support and tested announcements, or that someone added role="button" to a <div> two years ago. Since adopting a component is a decision you will be living with for years, twenty minutes of evaluation up front is one of the highest-leverage checks available.
This guide is the script for those twenty minutes. It extends Accessible Component Libraries in React.
WCAG Success Criteria Addressed (what the audit checks):
2.1.1 Keyboard2.4.3 Focus Order2.4.7 Focus Visible4.1.2 Name, Role, Value
Prerequisites
- A sandbox where you can render the candidate component in isolation.
- A screen reader you can drive for a few minutes — see testing with NVDA and VoiceOver in a dev workflow.
- The list of components you actually need. Auditing a whole library is unnecessary; audit the three components you will use.
Start With the Keyboard, Because It Is Fastest
Five minutes of keyboard testing eliminates more candidates than every other check combined, and it needs no tooling.
Implementation Guidelines:
- Tab to the component. If you cannot reach it, stop here.
- Exercise the pattern's expected keys: arrows in a listbox or tab list,
Escapein an overlay,Home/Endin a long list,Spaceon any button. - Watch the focus ring the entire time. A widget that moves selection without moving a visible indicator will strand sighted keyboard users.
- Check what happens after an interaction completes: does focus return to the trigger after a dialog closes, or fall to the top of the page?
Scan the Library's Own Demo
The demo page is the library's best foot forward. Violations there are informative in a way violations in your code are not.
Implementation Guidelines:
- Run axe DevTools on the component's documentation page. Note the rule names as much as the count.
- Treat
button-name,aria-required-childrenandaria-valid-attr-valuefailures as serious: they mean the pattern's structure is wrong, not that a demo was sloppy. - Ignore contrast findings on a marketing theme; you will supply your own colours.
- Check the demo's markup in DevTools. A
<div role="button" tabindex="0">where a<button>would do is a signal about how the whole library is built.
Listen for Name, Role and State
Automated tools confirm attributes exist. A screen reader confirms they mean something.
Implementation Guidelines:
- Tab through every control the component renders and note the announcement verbatim.
- Change state and listen again. A state that never updates is worse than none, because it actively misinforms.
- Trigger the component's dynamic behaviour — open, filter, select — and check whether anything is announced at all.
- Compare against the ARIA Authoring Practices pattern the component claims to implement. Deviations are not automatically wrong, but they should be deliberate.
Judge the Escape Hatches
You will need to fix something. What matters is whether the API lets you.
Implementation Guidelines:
- Check whether arbitrary props are forwarded to the underlying element.
...restspreading is the difference between "we can add anaria-label" and "we cannot". - Look for
refforwarding, without which you cannot move focus into or out of the component yourself. - Check whether ids are configurable, or generated internally and unreachable — the latter blocks every
aria-labelledbyrelationship you might need. - Prefer headless libraries when the audit is close. Owning the markup means owning the fix.
// The three questions in code form.
<Widget
aria-label="Filter results" // 1. does it forward aria-* props?
ref={widgetRef} // 2. does it forward refs?
id="results-filter" // 3. can I control the id it references?
/>
When the Audit Fails
A failed audit is not automatically a rejection — it is a cost estimate.
Implementation Guidelines:
- If the component is open and the gap is small, wrap it: add the missing name, manage focus around it, and document the wrapper as the only entry point.
- If the gap is structural — wrong roles, no keyboard model — do not wrap it. A wrapper cannot add a keyboard contract the component does not have.
- File the issue upstream with a transcript, whatever you decide. It is how libraries improve, and the response tells you whether to stay.
- Record the decision where the next person will find it: an ADR, or a comment in the wrapper explaining what was fixed and why.
How to Verify
- Re-run the script after adoption. Libraries regress; a component that passed at version 3.1 may not at 4.0.
- Add a regression test. Assert the accessible name and keyboard behaviour of the wrapped component in your own test suite — see testing React components with jest-axe.
- Scan in your own theme. Contrast findings that did not apply on the demo may apply with your palette.
- Check the dark theme. Third-party components frequently assume a light background.
- Test with your data. Long labels, right-to-left text and empty states expose problems demos never show.
- Render it inside a form and inside a dialog. Components often behave differently once nested, particularly around focus containment and id generation, and both contexts are common in real applications.
- Try it with a long label and a short one. Truncation, wrapping and overflow frequently break the accessible name or clip the focus ring in ways a demo with tidy content never shows.
Common Accessibility Mistakes
- Trusting the README. "Accessible" and "WCAG compliant" are marketing claims with no shared definition.
- Auditing the whole library. Audit the components you will use; the others are noise.
- Testing only with a scanner. It cannot hear that a name is meaningless or that a state never updates.
- Adopting a closed component and planning to fix it later. Without prop forwarding and refs, later never comes.
- Ignoring the issue tracker. How a project handles accessibility reports predicts your next two years better than its current code.
- Skipping the re-audit on upgrade. Accessibility regressions are rarely mentioned in changelogs.
Conclusion
Twenty minutes of structured evaluation — keyboard, scan, screen reader, escape hatches, issue tracker — is enough to separate components that implement a pattern from components that mention one. Do it before adoption, when the cost of choosing differently is a paragraph in a decision record rather than a migration. And write down what you found: the next person to evaluate the same library will otherwise start from the README, which is exactly where you started.
Keep the audit results somewhere durable. A short table in the repository — component, version audited, verdict, known gaps, wrapper location — pays for itself the first time someone proposes the same library again, and it turns an upgrade into a diff against a known baseline rather than a fresh investigation. It also gives the accessibility conversation a shared reference point during procurement, which is usually when the decision is actually made and rarely when anyone has twenty minutes to spend on a keyboard.
Frequently Asked Questions
Are headless libraries always more accessible? Not automatically, but they usually fail more honestly: they ship the behaviour and leave the markup to you, so a gap in your markup is your gap to fix. The risk with a fully-styled library is inheriting decisions you cannot reach.
How much should a published VPAT or accessibility statement weigh? It is evidence of intent and effort, not a guarantee. Read what it actually claims — often "supports with exceptions" — and check whether the exceptions cover the component you need. It never replaces the twenty-minute script.
What if the component is already in production and fails? Assess the severity first: an unreachable control is urgent, a missing state announcement is important, a suboptimal announcement can be scheduled. Wrap what you can, file upstream, and put a replacement on the roadmap if the gap is structural.
Should I test every component in a design system I am adopting? Test the ones you will ship, then re-test as you adopt more. A dozen components audited over six months is realistic; a whole library audited in one week produces a document nobody reads.
Is it worth contributing the fix upstream? Often yes — accessibility fixes are usually small, well-scoped and welcomed, and the alternative is maintaining a patch forever. How the project responds to the first pull request also answers the sustainability question better than any README.
Related guides
- Accessible Component Libraries in React — the parent guide on choosing and wrapping libraries.
- Building Accessible Tabs in React Without Radix UI — what implementing a pattern yourself actually costs.
- Testing with NVDA & VoiceOver in a Dev Workflow — the listening half of the audit.
- Component Testing with jest-axe — locking the audit's findings into a regression test.