ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 33

  1. Blog

chokidar vs @parcel/watcher: 603 ms vs 61 ms to Ready

Aditya Challa·5 October 2026·8 min read

Hands-on
On this page
  1. What I tested
  2. How fast does each watcher get ready?
  3. How quickly do change events arrive?
  4. Do they report the same events?
  5. What does each one cost to install?
  6. Which one should you pick?
  7. Sources
  8. Related

I pointed both watchers at the same 7,800-file repo copy on this box: @parcel/watcher 2.6.0 was ready in about 61 ms holding 1,319 inotify watches, while chokidar 5.0.0 took about 603 ms and held 9,119. Flip to latency and the result reverses: chokidar reported a single saved file in about 1.3 ms, @parcel/watcher in about 50.7 ms, because it batches events on purpose.

Short answer: pick @parcel/watcher for big trees, monorepos and anything that must survive restarts, and pick chokidar for small, scoped watches where you want per-event callbacks and a pure-JS install. No affiliate links in this post.

What I tested

  • Machine: 8 vCPU Intel Xeon / 15 GB RAM shared Linux cloud box, tested 6 Oct 2026 about 03:25 IST. fs.inotify.max_user_watches on this box is 127,940; Watchman is not installed, so @parcel/watcher used its inotify backend.
  • Versions: chokidar 5.0.0 and @parcel/watcher 2.6.0 (npm latest that day), Node 20.19.2, esbuild 0.28.2 for bundle sizes.
  • Fixture: a copied Jest/Vitest test project, 7,800 files in 1,318 directories including node_modules. Outside node_modules it is 505 files in 33 directories. Two modes: node_modules ignored (chokidar with an ignored function, @parcel/watcher with ignore set to node_modules) and the whole tree.
  • Per run, in a fresh Node process: time from the watch call to ready, inotify watches counted from /proc/self/fdinfo, RSS after ready, 30 single-file writes timed to their event, a burst of 1,000 new files, an editor-style atomic save (write a temp file, rename it over the original) and a recursive delete. Five rounds per mode with the library order alternated; I report medians. Scripts and JSON are in /workspace/bench-chokidar-parcel-watcher/.
  • Not tested: macOS FSEvents, Windows, the Watchman backend, polling mode, network or Docker bind mounts, chokidar 3.6 (the version Vite still ships), and trees large enough to hit the inotify limit.

How fast does each watcher get ready?

Median of 5 fresh runschokidar 5.0.0@parcel/watcher 2.6.0
Ready, node_modules ignored54.4 ms (52.6–80.3)23.0 ms (22.6–31.7)
Ready, whole tree603.4 ms (586.1–767.0)60.7 ms (55.4–66.0)
inotify watches, node_modules ignored53934
inotify watches, whole tree9,1191,319
RSS after ready, node_modules ignored67.3 MB57.9 MB
RSS after ready, whole tree138.7 MB61.7 MB

The watch counts explain most of the table. chokidar's numbers line up with one inotify watch per file plus one per directory (505 + 33 + 1 = 539 and 7,800 + 1,318 + 1 = 9,119), while @parcel/watcher holds one per directory. An empty Node 20 process on this box sits at about 51.0 MB RSS, so on the whole tree chokidar added roughly 88 MB against roughly 11 MB. The 127,940-watch limit here would fit about 14 chokidar watchers on this repo with node_modules included, and chokidar's own README lists ENOSPC as the error you get when that runs out. The file-descriptor cousin of that failure, EMFILE from a low soft limit, is measured in ulimit soft vs hard. The initial crawl itself is a directory walk; the raw cost of reading directory entries is what scandir vs listdir times.

How quickly do change events arrive?

Median of 5 runschokidar 5.0.0@parcel/watcher 2.6.0
One file write to its event, median of 30 (ignored mode)1.30 ms50.65 ms
Same, p901.72 ms50.79 ms
One file write, whole tree1.37 ms50.66 ms
1,000 new files: all delivered?1,000 of 1,0001,000 of 1,000
1,000 new files: last event after first write92.3 ms84.9 ms

The 50 ms floor is a design choice: the @parcel/watcher README says events are throttled and coalesced in C++ and handed to JavaScript as one batch, so the JS thread is not flooded during a git checkout or npm install. That shows up in the burst row, where it finished slightly ahead even though every single event is slower. If you are wrapping either watcher in your own debounce before a rebuild, the extra 50 ms is usually already inside your window; the helper sizes are compared in perfect-debounce vs lodash. For the kernel side of waiting on many descriptors, epoll vs select has a Python toy that shows why per-wait cost stays flat.

Do they report the same events?

Actionchokidar 5.0.0@parcel/watcher 2.6.0
Atomic save: write atomic.txt.tmp, rename over atomic.txtchange atomic.txtcreate atomic.txt.tmp, create atomic.txt, delete atomic.txt.tmp
Recursive delete of a folder with 1,031 files1,031 unlink + 1 unlinkDir1,032 delete
Ignore optionfunction, regex or exact path (globs removed in v4)paths or glob patterns (parsed with picomatch)
Events missed while the process was downnot availablegetEventsSince with a snapshot file
Module format / NodeESM only, Node 20.19+CommonJS wrapper plus native addon

The atomic save row is the one that bites in practice. Editors that save by writing a temp file and renaming it over the original look like a plain change to chokidar, because its atomic option merges the unlink and add. @parcel/watcher shows the raw sequence, and the overwrite arrives as create rather than update, so a handler that only listens for update will miss it. Why editors save that way is covered in atomic rename vs overwrite. The snapshot feature is the part chokidar has no answer for: writeSnapshot took a median 39.3 ms and wrote a 1,135,617-byte file for this tree, and getEventsSince took a median 50.5 ms and returned the one file I created while nothing was watching.

What does each one cost to install?

PackagePackages installednode_modulesBundled JS (min / gzip)
chokidar 5.0.02 (chokidar, readdirp)152 KB22,565 B / 8,325 B, 4 inputs
@parcel/watcher 2.6.08 incl. glibc and musl binaries2,008 KB31,897 B / 11,682 B, 15 inputs, plus a 523,152-byte watcher.node

The native addon cannot go into a JS bundle, so a bundled CLI that uses @parcel/watcher still needs the platform package installed next to it. If you track the size of your published tool, wire the JS part into size-limit vs bundlesize and list the binary separately.

Which one should you pick?

SituationMy pick
Watching a large tree or a whole monorepo@parcel/watcher
Need changes made while your tool was not running@parcel/watcher (snapshots)
Small, scoped watch where each save should fire within a few mschokidar
Pure-JS install, no native binaries in CI or containerschokidar
Watching config files next to a dev serverchokidar, which is what Vite uses

For context, Vite's server.watch docs say its options are passed to chokidar 3.6 and that it skips node_modules and .git by default, which is the ignored mode in my tables; the dev-server comparison itself is in Vite vs Parcel, and Parcel is the project that maintains @parcel/watcher.

Bottom line: on this box, with the whole 7,800-file tree, @parcel/watcher 2.6.0 was ready in 60.7 ms with 1,319 watches and 61.7 MB RSS, against 603.4 ms, 9,119 watches and 138.7 MB for chokidar 5.0.0. chokidar answered a single save in 1.3 ms against 50.7 ms. Who should not switch to @parcel/watcher: small watchers where per-event speed and a pure-JS install matter more than memory. Who should not stay on chokidar: tools that watch big trees, run many watchers per machine, or need to know what changed while they were off.

How this was made: I installed both packages on the ShopperCove box, copied a real test project as the fixture, ran each watcher five times per mode in fresh processes with the order alternated, counted inotify watches from /proc, and checked atomic-save, delete and snapshot behavior on the same tree. The JSON results sit next to the scripts. The write-up was drafted with AI help and checked against that output.

Sources

  • https://github.com/paulmillr/chokidar
  • https://github.com/parcel-bundler/watcher
  • https://www.npmjs.com/package/chokidar
  • https://www.npmjs.com/package/@parcel/watcher
  • https://vite.dev/config/server-options
  • https://man7.org/linux/man-pages/man7/inotify.7.html

Related

  • https://www.shoppercove.com/blog/ulimit-soft-vs-hard-emfile-lab
  • https://www.shoppercove.com/blog/scandir-vs-listdir-localhost-lab
  • https://www.shoppercove.com/blog/perfect-debounce-vs-lodash
  • https://www.shoppercove.com/blog/epoll-vs-select-fd-setsize-localhost-lab
  • https://www.shoppercove.com/blog/atomic-rename-vs-overwrite-localhost-lab
  • https://www.shoppercove.com/blog/size-limit-vs-bundlesize
  • https://www.shoppercove.com/blog/vite-vs-parcel
  • https://www.shoppercove.com/blog/tsup-vs-unbuild
chokidarparcel-watcherinotifyfile system watcherperformance benchmarknode.jslatency

Lab evidence

What I found running this

Hands-on on ShopperCove box 6 Oct 2026 ~03:25 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 20.19.2, max_user_watches 127,940, no Watchman so @parcel/watcher used inotify). chokidar 5.0.0 vs @parcel/watcher 2.6.0. Fixture: copied Jest/Vitest project, 7,800 files / 1,318 dirs (505 files / 33 dirs outside node_modules). Fresh process per run, 5 rounds per mode, order alternated, medians: whole tree ready 603.4 ms (586.1-767.0) vs 60.7 (55.4-66.0); inotify watches 9,119 vs 1,319 (chokidar = one per file + dir); RSS 138.7 vs 61.7 MB (empty Node 51.0 MB). node_modules ignored: 54.4 vs 23.0 ms, 539 vs 34 watches, 67.3 vs 57.9 MB. Single write to event median of 30: 1.30 ms (p90 1.72) vs 50.65 ms (p90 50.79; README says events are throttled/coalesced in C++). Burst 1,000 files: all delivered, last event 92.3 vs 84.9 ms. Atomic save (tmp + rename): chokidar 1 change; parcel create tmp, create target, delete tmp. Recursive delete 1,031 files: 1,031 unlink + 1 unlinkDir vs 1,032 delete. writeSnapshot 39.3 ms (1,135,617 B), getEventsSince 50.5 ms, caught offline create. Install 2 pkgs 152 KB vs 8 pkgs 2,008 KB (523,152 B watcher.node); esbuild JS gzip 8,325 B vs 11,682 B. Not tested: macOS FSEvents, Windows, Watchman, polling, Docker/network mounts, chokidar 3.6 (Vite), inotify-limit exhaustion. No affiliate.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 27

    undici vs node-fetch: 31k vs 7.4k Req/s on Node 22

    5 Oct 2026

  • Plate 91

    tinypool vs piscina: 5.4 KB vs 10 KB Gzip

    5 Oct 2026

  • Plate 26

    Picocolors vs Chalk: 1.0 KB vs 3.4 KB Gzip

    5 Oct 2026

On this page

  1. What I tested
  2. How fast does each watcher get ready?
  3. How quickly do change events arrive?
  4. Do they report the same events?
  5. What does each one cost to install?
  6. Which one should you pick?
  7. Sources
  8. Related
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove