Testing a SPA
How Nitpick handles client-side-routed single-page apps.
Most 2026 apps are SPAs React Router, Vue Router, SvelteKit, Next.js App Router. Traditional <a>-tag BFS crawlers find 0 pages on them.
Nitpick’s crawler uses a three-pass strategy designed for SPAs.
Pass 1: ARIA inventory
After loading each page, Nitpick reads the accessibility tree and collects all navigable candidates:
- Every
<a href>(server-rendered) - Every
role="link",role="menuitem",role="tab" - Every
<button>inside<nav>,<header>,<aside>, or[role="navigation"]
This catches custom nav components that don’t use <a> tags.
Pass 2: Menu expansion
Many SPAs hide navigation behind dropdowns or hamburger menus. Nitpick finds elements with aria-haspopup, aria-expanded="false", or aria-controls, clicks them, re-inventories, then presses Escape to close.
Pass 3: Click-and-observe
For candidates that aren’t <a> tags (buttons, menuitems), Nitpick uses a click-and-observe pattern:
- Click the candidate
- Wait up to 2.5s for
window.location.hrefto change (via Playwright’swaitForFunction) - If the URL changed, record the new page and the edge
- Navigate back to the starting URL via
page.goto()(more reliable thangoBack()for SPAs)
This handles history.pushState() navigation that React Router et al. use without full-page reloads.
What Nitpick’s SPA mode doesn’t handle
- Intercepted navigation “Are you sure you want to leave?” modals. Nitpick will click Cancel, but the crawl may abort.
- Infinite scroll: the crawler doesn’t understand”load more” as navigation. Those are treated as in-page actions.
- Auth-gated routes that require specific headers: Playwright storage state handles cookies but not custom headers.
For apps with very custom navigation patterns, fall back to:
nitpick run --scope targeted --url /page-a --url /page-b
Performance
SPA crawling is slower than <a>-tag crawling because each candidate is click-probed. Default limit is 30 probes per page. Expect 20–40 seconds per page for crawl in SPA mode vs 2–5 seconds for simple server-rendered apps.