NVDA (NonVisual Desktop Access) is a free, open-source screen reader for Windows maintained by NV Access. It is the de facto baseline for web accessibility testing: it is free, has the largest user share among screen-reader users globally (WebAIM Screen Reader Survey), and reproduces most issues that VoiceOver and JAWS users encounter.
This how-to walks through installing NVDA, learning the minimum useful keys, and running a smoke test against an arbitrary page.
Install — portable mode
A portable NVDA install does not require admin rights on your machine, which matters if you are testing on a managed laptop:
- Download from nvaccess.org/download.
- Run the installer and choose “Create Portable Copy”.
- Place the portable folder somewhere persistent.
Run the portable executable. NVDA starts speaking immediately. To stop
speech: Ctrl. To enable Speech Viewer (a window that shows what NVDA
just said — invaluable for visual debugging): NVDA menu → Tools
→ Speech Viewer.
The keys you actually need
NVDA has a key called the NVDA modifier — defaults to Insert or
CapsLock (configurable). On laptops, set it to CapsLock in Preferences
→ Keyboard → “Use CapsLock as NVDA modifier key”.
| Action | Keys |
|---|---|
| Stop speaking | Ctrl |
| Pause / resume | Shift |
| Read all (continuous) | NVDA + ↓ |
| Read previous / next line | ↑ / ↓ |
| Read previous / next character | ← / → |
| Next heading / list / table | H / L / T |
| Next link | K |
| Next form control | F |
| Next landmark | D |
| Elements list (everything) | NVDA + F7 |
| Toggle browse / focus mode | NVDA + Space |
| Read accessible name + state | NVDA + Tab |
Browse mode is the default for static content; focus mode kicks in automatically on form controls. Toggling is needed mostly when a custom widget claims to be a textarea but is not.
A 10-minute smoke procedure
For each page you ship a meaningful change to, run this:
- Open the page in Firefox. Firefox has the most predictable NVDA pairing; Chrome works but occasionally misroutes ARIA live regions.
- Read top-to-bottom.
NVDA + ↓. Listen to the first 60 seconds. Stop on any utterance that lands as “button” or “link” with no further context, or that announces a visible heading as plain text. - Pull the elements list.
NVDA + F7. Switch tab to Headings. The hierarchy should make sense: an article should have oneh1, thenh2siblings, thenh3children. A flat list of nineteenh2s usually means someone styled paragraphs as headings. - Switch to Landmarks. Should include
banner,navigation,main,contentinfoat minimum. Missingmainis a common bug. - Tab through interactive elements. Each Tab stop should announce name + role + state. “Email address, edit, required, blank” is correct. “Edit, blank” is missing the name.
- Activate one form. Submit with errors. The error messages should be announced via a live region or by moving focus to the first error. If silence follows, you have a 4.1.3 failure.
- Open one dialog. Focus should move into the dialog; Escape should close it; focus should return to the trigger. NVDA should announce the dialog name and role on open.
If all seven steps pass, the page is in good shape for a v0.1 release on the screen-reader axis. If two or more fail, do not ship; fix.
Speech Viewer for documentation
When you find an issue, capture the announcement string from Speech Viewer and paste it into the bug report. “NVDA reads: ‘edit, blank’” is a complete, reproducible bug; “screen reader is broken” is not.
Browser pairings
- Firefox + NVDA — baseline. Strongest ARIA support, fewest surprises.
- Chrome + NVDA — works, with occasional live-region quirks.
- Edge + NVDA — equivalent to Chrome (Chromium engine).
For VoiceOver coverage on macOS/iOS, the canonical reference is Apple’s VoiceOver User Guide. For JAWS, the enterprise QA combination, key chords differ but the smoke procedure is portable.
When this is hard
- Custom comboboxes. Browse mode and focus mode disagree about who
owns arrow keys. The combobox should explicitly switch NVDA to focus
mode (
role="combobox"plusaria-expanded); if it does not, users cannot navigate options. - Dynamic page transitions. SPA route changes are silent unless you
manually announce them. Move focus to the new
<h1>on navigation, or usearia-live="polite"on a dedicated announcement container. - Heading-only navigation. Some users navigate exclusively by
H. If your headings do not summarise the section, those users get no table-of-contents.
Cross-links
- Automated complement: How to wire axe-core into CI — automation catches 30–50% of issues; this manual pass catches the rest.
- Reference: NV Access user guide.
- WAI-ARIA patterns: Authoring Practices Guide for canonical announcements per component.
- Adjacent: How to fix WCAG 4.1.2 name role value.
The single most useful habit: keep NVDA installed and run the 10-minute smoke before every release. The aggregate cost is small; the regression catch rate is large.