# Chokidar: cross-platform file watching for Node.js, and what v5 changes

> Chokidar normalizes fs.watch and fs.watchFile into add, change and unlink events across platforms. Version 5 is ESM-only and requires Node.js 20.19.0 or newer, which is the first thing to check before upgrading.

**paulmillr/chokidar** — Minimal and efficient cross-platform file watching library

- Repository: https://github.com/paulmillr/chokidar
- Website: https://paulmillr.com
- Stars: 12,244 · Forks: 636
- Language: TypeScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/paulmillr-chokidar

## What Chokidar solves for Node.js file watching

Raw fs.watch is platform-dependent. The README lists the specific gaps: macOS events do not always report filenames, events can be reported twice, and changes arrive as a generic rename rather than add, change or unlink. Chokidar wraps the Node.js core fs module and normalizes what it receives, often checking the truth by getting file stats or directory contents.

The audience is narrow but real. Build tools, test runners, asset pipelines and editor integrations all need a stable event stream over a directory tree. Chokidar was made for Brunch in 2012 and the README states it is now used in roughly 30 million repositories, which is a distribution fact rather than a quality claim.

Two behaviors matter more than the event names. Recursive watching is always supported, with a depth option to cap it, whereas raw events are only partially recursive. And the API is event-based rather than callback-based, so a watcher instance can be extended with watcher.add() while it runs.

## How the default watcher normalizes events without polling

The default implementation is built on fs.watch, which avoids polling and keeps CPU usage down. Chokidar initiates watchers recursively for everything inside the paths you pass, so the resource cost scales with the scope you hand it, not with the number of events you listen for. The README is direct about this: be judicious about not wasting system resources by watching much more than needed.

When fs.watch cannot deliver a trustworthy answer, Chokidar falls back to fs.watchFile, which polls. Polling is more expensive, and the README states it is typically necessary to set usePolling to true to successfully watch files over a network. The interval option controls polling frequency in milliseconds and defaults to 100; binaryInterval defaults to 300. Both can be overridden with the CHOKIDAR_USEPOLLING and CHOKIDAR_INTERVAL environment variables.

Two options exist because filesystems do not write atomically from the reader's point of view. atomic handles editors that write to a temporary file and move it into place. awaitWriteFinish handles large files written in chunks, emitting a single event once the write settles. Both accept a boolean or an object with custom intervals, and the README notes that awaitWriteFinish can take stabilityThreshold and pollInterval in milliseconds.

Filtering happens through ignored, which accepts a function, a regex or a path, and tests the whole relative or absolute path rather than just the filename. When passed a function with two arguments it is called twice per path: once with the path alone, and once with the path plus an fs.Stats object. That second call is what lets you write a predicate such as ignoring everything that is a file and does not end in .js.

## Installing Chokidar and watching a directory

Chokidar installs from npm as a single package. The README gives this command:

```bash
npm install chokidar
```

The package declares one runtime dependency, readdirp, and requires Node.js 20.19.0 or newer. Version 5.0.0 is ESM-only, so the import form below is the one that works:

```javascript
import chokidar from 'chokidar';

chokidar.watch('.').on('all', (event, path) => {
  console.log(event, path);
});
```

Running that prints the event name and path for every add, change, unlink and directory event under the current directory. To narrow the scope and react to named events instead of the catch-all, the README shows this pattern:

```javascript
const watcher = chokidar.watch('file, dir, or array', {
  ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'),
  persistent: true,
});

watcher
  .on('add', (path) => console.log(`File ${path} has been added`))
  .on('change', (path) => console.log(`File ${path} has been changed`))
  .on('unlink', (path) => console.log(`File ${path} has been removed`));
```

One detail worth knowing before your first run: ignoreInitial defaults to false, so add and addDir events fire for every matching path Chokidar discovers during the initial scan, before the ready event. If you only want changes after startup, set ignoreInitial to true or wait for ready. The add, addDir and change handlers also receive an fs.Stats object as a second argument when one is available, which is how the README's example reads a file's size on change. Shutdown is asynchronous: await watcher.close() before the process exits.

## Where Chokidar is the wrong tool

The cost model is the main limitation. Every path in scope gets a watcher, and the README warns that watching much more than needed wastes system resources. Pointing Chokidar at a home directory or a repository containing node_modules is a common way to hit file descriptor limits on Linux, and the library does not stop you.

Network filesystems are the second boundary. If you need to watch files over a network, the README states you typically must set usePolling to true. That trades CPU for reliability, and at an interval of 100 ms across a large tree the polling load is yours to manage. Setting usePolling to true on macOS explicitly overrides the useFsEvents default, so you also give up the native event path there.

Version 5 removes a migration path that many projects still rely on. The package is ESM-only and the minimum Node.js requirement moved to 20.19.0, so a CommonJS codebase cannot require it. Version 4 had already removed glob support from the API and cut the dependency count from 13 to 1, which means code written against Chokidar 3 may need more than a version bump.

Finally, Chokidar reports events. It does not run commands, restart processes or reload browsers. If what you actually want is a process supervisor that restarts on change, a watcher library is the wrong layer.

## Chokidar compared with a CLI watcher like nodemon

The closest everyday alternative for the restart-on-change use case is a CLI tool such as nodemon, which wraps a process and restarts it when files change. The difference is the layer. Nodemon owns the process lifecycle: it spawns your command, watches the tree, and kills and restarts on an event. Chokidar owns only the event stream and leaves the reaction to you. If you want to run a linter, upload assets, rebuild a bundle or push a message to a queue, nodemon gives you nothing and Chokidar gives you the hook.

There is also a chokidar-cli package that people reach for to run a shell command on file events without writing JavaScript. That is a different project from this repository. The package.json here declares a single entry point, chokidar, plus a handler.js subpath export, and no bin field, so this repository does not ship a command line tool of its own.

The honest comparison against raw fs.watch is the one the README makes. Raw events are cheaper in the sense that nothing sits between you and the kernel, but you then own platform differences, duplicate events, the rename problem and recursion. Chokidar's value is that normalization, and it is worth the extra layer when your code has to run on more than one operating system.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-08-16. The most recent release is 5.0.0, published on 2025-11-25, following 4.0.3 in December 2024. The project is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a description of the licence text, not legal advice; run your own review if your organisation has a licence policy.

Upgrade cost is concentrated in two places. The ESM-only decision in v5 means any CommonJS consumer needs a build step or a dynamic import before it can move. The Node.js floor of 20.19.0 rules out older runtimes, and the engines field in package.json is the authority to check rather than a blog post.

The dependency surface is small, which keeps audit noise low: readdirp is the only runtime dependency, and the devDependencies are tooling (typescript, prettier, tinyspy, upath and the project's own build helper). The repository ships index.js, index.d.ts, handler.js and handler.d.ts, so TypeScript consumers get types without a separate @types package.

## Conclusion

Adopt Chokidar if you are building a Node.js tool that must react to file changes on macOS, Linux and Windows with one code path, and you are on Node.js 20.19.0 or newer. Do not adopt it for a one-off script on a single platform where raw fs.watch already behaves, and do not expect v5 to load from CommonJS. Before upgrading, verify your Node.js version against the engines field, confirm your build emits ESM, and decide whether you need usePolling for network mounts, since the README states polling is typically necessary there.

## FAQ

### What is chokidar used for?

It watches files and directories and reports changes as named events. The README describes it as a cross-platform file watching library that normalizes the events from the Node.js core fs module into add, change and unlink, and supports recursive watching plus filtering.

### How do you install chokidar?

The README gives npm install chokidar as the install command. The package requires Node.js 20.19.0 or newer and version 5 is ESM-only, so you import it rather than require it.

### What is chokidar npm?

Chokidar is published on npm under the name chokidar, currently at version 5.0.0. Its package.json declares one runtime dependency, readdirp, and exposes the main entry point plus a handler.js subpath export.

### What is CHOKIDAR_USEPOLLING?

It is an environment variable that overrides the usePolling option, set to 1 or 0. The README states polling is typically necessary to successfully watch files over a network, and that setting usePolling to true on macOS overrides the useFsEvents default.

### How does chokidar compare with fs.watch?

The README lists the differences: macOS events report filenames, events are not reported twice, changes come through as add, change or unlink instead of a generic rename, and recursive watching is always supported rather than partial. Chokidar still relies on the core fs module underneath and normalizes what it receives.

### What is a chokidar alternative for restarting a process on change?

A CLI tool such as nodemon owns the process lifecycle and restarts your command on file changes, while Chokidar only emits the events. The README does not document a restart feature, so process supervision is outside this library's scope.

## Sources

- [License: MIT](https://github.com/paulmillr/chokidar/blob/main/LICENSE)
- [paulmillr/chokidar on GitHub](https://github.com/paulmillr/chokidar)
- [Project website](https://paulmillr.com)
- [README](https://github.com/paulmillr/chokidar/blob/main/README.md)
- [Releases](https://github.com/paulmillr/chokidar/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/paulmillr-chokidar
