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:

  1. Click the candidate
  2. Wait up to 2.5s for window.location.href to change (via Playwright’s waitForFunction)
  3. If the URL changed, record the new page and the edge
  4. Navigate back to the starting URL via page.goto() (more reliable than goBack() 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.