"A test that re-runs on every save is the difference between a feedback loop and a ceremony."
Watch Mode Is the Default
Plain vitest (no arguments) starts in watch mode. It runs the relevant tests once, then sits there listening for file changes. When you save a file, Vitest figures out which tests depend on the change (via its import graph) and re-runs only those. The full suite stays cold until you ask for it.
The reason this matters: 200 unit tests at 30 ms each is six seconds. Running the full suite on every save is fine. But Vitest's smart re-run usually finishes in 200-500 ms — fast enough that you forget the tests are running. The loop becomes: change code, glance at the terminal, see the result, repeat.
The Watch-Mode Keys You'll Actually Use
While watch mode is running, single-key shortcuts let you filter and re-run without leaving the terminal:
a— re-run all tests (forces a full pass)f— re-run only failed tests (sweet for chasing a regression)p— filter by filename pattern (live regex)t— filter by test-name patternq— quitr— manually re-run all
You don't need to memorize them. Vitest prints the menu when you press h for help. The two you'll use daily are p (drill into one file) and f (chase the failures).
CI Mode — vitest run
vitest run runs the suite once and exits with a non-zero code on failure. This is the CI shape — no watching, no UI, no interactive controls. The output is plain text suitable for log aggregation. Most projects have both: npm test aliased to vitest (watch) for local, npm run test:ci aliased to vitest run --reporter=verbose for pipelines.
UI Mode — the Browser Dashboard
vitest --ui opens a local web UI alongside the watch-mode runner. The UI shows the full test tree, surfaces the last failure with its diff inline, exposes a per-file re-run button, and overlays coverage on source files if you've enabled it. The UI is most useful for two things: hunting flakes (you see the run history visually) and reviewing coverage gaps (the source overlay is easier to read than a terminal coverage report).
It's not a replacement for the terminal — the terminal is faster for the inner loop. But when something weird is happening (one test passes locally, fails in CI), the UI's history makes the divergence visible.
p + filename), let it cycle in milliseconds, fix, see the green. The fastest debug loop you have is the one where the test runs in under a second.