npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

chokidar

v6.0.0

Published

Minimal and efficient cross-platform file watching library

Readme

Chokidar Weekly downloads

Minimal and efficient cross-platform file watching library

Why?

Raw fs.watch / fs.watchFile are useless: fs.watch reports many changes as an non-transparent rename, can fire twice for one change, and recursive watching differs across platforms. Chokidar normalizes all of that:

  • Proper add / change / unlink (and addDir / unlinkDir) events, verified, deduplicated, with paths on every platform.
  • Native fs.watch by default, which keeps CPU usage down; stat-based polling is available for network and other unusual filesystems.
  • Recursive watching everywhere, with an optional depth limit, path filtering, and symlink support.
  • Editor "atomic writes" (atomic) and chunked writes of large files (awaitWriteFinish) are handled.

Chokidar watches everything under the paths you give it, so scope them (and use ignored / depth) rather than watching more than you need.

Made for Brunch in 2012, it is now used in 30+ million projects and has proven itself in production environments. The current major is v6 (Oct 2026) - with lots of major improvements.

Getting started

npm install chokidar
import chokidar from 'chokidar';
// or: import { watch } from 'chokidar';
// or: const chokidar = require('chokidar');

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

A fuller example:

import chokidar from 'chokidar';

const watcher = chokidar.watch('src', {
  // strings are exact paths (not globs), regexes test the whole path, functions get (path, stats?)
  ignored: [
    /(^|\/)\../, // dotfiles
    (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only .js files
  ],
  ignoreInitial: true, // don't emit add/addDir for files that already exist
});

watcher
  .on('add', (path, stats) => console.log('added', path, stats?.size))
  .on('change', (path) => console.log('changed', path))
  .on('unlink', (path) => console.log('removed', path))
  .on('ready', () => console.log('initial scan done'))
  .on('error', (err) => console.error(err));

watcher.add(['lib', 'index.js']); // add more paths later
watcher.unwatch('lib'); // synchronous
console.log(watcher.getWatched()); // { '/abs': ['src'], '/abs/src': ['a.js', 'sub'], ... }
await watcher.close(); // async and terminal: create a new watcher to resume

Recipes:

// Large or chunked writes, and editors that save via temp file + rename
chokidar.watch('uploads', {
  awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 100 }, // wait for size to settle
  atomic: 100, // unlink + add within 100 ms becomes one change
});

// Network or otherwise unusual filesystems where fs.watch is unreliable
chokidar.watch('/mnt/nfs/data', { backend: 'polling', pollingInterval: 500 });

API

chokidar.watch(paths, [options]) returns an FSWatcher. paths is a string or an array of strings; files are watched, directories are watched recursively. All options with their defaults:

chokidar.watch('dir-or-file', {
  // Filtering
  ignored: undefined, // matcher or array of matchers, see below
  ignoreInitial: false,
  followSymlinks: true,
  cwd: undefined,
  depth: undefined, // unlimited
  // Backend
  backend: 'auto', // 'auto' | 'native' | 'native-recursive' | 'polling'
  pollingInterval: 100, // polling backend only
  pollingBinaryInterval: 300, // polling backend only
  // Event timing
  atomic: true, // false with backend: 'polling'; or a number of ms
  awaitWriteFinish: false, // or true, or { stabilityThreshold: 2000, pollInterval: 100 }
  alwaysStat: false,
  // Errors and lifecycle
  ignorePermissionErrors: false,
  persistent: true,
});

Filtering

  • ignored (default: none). One matcher or an array of matchers. The whole path is tested (as passed to watch(), joined with cwd if set), not just the basename. Ignoring a directory ignores everything inside it. Directories above a path passed to watch() / add() are never ignored, so a missing path is still watched through its parent; their other contents are still filtered. A matcher is one of:
    • a string: an exact path. Relative strings are resolved against cwd (or the process cwd). Globs are not supported; see upgrading for the replacement pattern.
    • a RegExp: tested against the whole path with forward slashes, on Windows too, e.g. /(^|\/)\../ for dotfiles or /\/node_modules\//.
    • a function (path, stats?) => boolean: may be called twice per path, first with the path only, then with the path and its fs.Stats, so guard stat-based checks with stats?.isFile().
    • an object { path, recursive?: boolean }: an exact path, plus its subtree when recursive is true.
  • ignoreInitial (default: false). When true, add / addDir are not emitted for paths discovered during the initial scan (before ready).
  • followSymlinks (default: true). When false, symlinks are watched as themselves rather than followed; retargeting a link emits change.
  • cwd (no default). Base directory that paths are resolved against; emitted paths are relative to it.
  • depth (default: undefined, unlimited). Maximum number of subdirectory levels to traverse. Infinity means unlimited.

Backend

  • backend (default: auto). Selects the filesystem observation backend:
    • auto uses native recursive watching on macOS and Windows and native per-directory watching elsewhere.
    • native uses one fs.watch subscription per directory.
    • native-recursive prefers fs.watch({ recursive: true }) and falls back to per-directory watching where recursive watching is unavailable. A finite depth also uses per-directory watching so nothing beyond the requested tree is subscribed.
    • polling uses Chokidar's stat-based polling scheduler. Useful for network and other non-standard filesystems; uses more CPU.
  • pollingInterval (default: 100) and pollingBinaryInterval (default: 300, for recognized binary file extensions): polling periods in milliseconds, polling backend only. Polling compares size, modification time, existence, and (except on Windows) inode between polls; an in-place write that changes none of these is not detected.
  • Environment overrides: CHOKIDAR_USEPOLLING forces polling when truthy and disables it when falsy, even if backend: 'polling' is set in code; CHOKIDAR_INTERVAL overrides pollingInterval.
  • Deprecated aliases kept for compatibility: usePolling: true means backend: 'polling'; interval / binaryInterval mean pollingInterval / pollingBinaryInterval (the new spellings win when both are given).

Event timing

  • atomic (default: true with native backends, false with polling; or a number of milliseconds). Editors that save through a temp file and rename produce unlink then add; within the window (100 ms by default) Chokidar emits a single change instead. It also ignores common editor temp files (.swp / .swx, ~ backups, Sublime .subl*.tmp).
  • awaitWriteFinish (default: false). By default add / change fire as soon as a file appears or changes, which for large or chunked writes can be before the write is finished. Set to true (or { stabilityThreshold: 2000, pollInterval: 100 }) to hold add / change until the file size has stayed constant for stabilityThreshold ms, checked every pollInterval ms. The right threshold depends on the OS and hardware: higher is safer and less responsive. Events from the initial scan are not held.
  • alwaysStat (default: false). Always pass an fs.Stats object with add / addDir / change, issuing an extra stat when the backend did not provide one.

Errors and lifecycle

  • ignorePermissionErrors (default: false). When true, EPERM / EACCES errors from unreadable paths are suppressed instead of emitted.
  • persistent (default: true). Whether the watcher keeps the process alive while it is watching.

Methods

  • .add(paths): start watching more files or directories (string or array).
  • .unwatch(paths): stop watching files or directories (string or array). Synchronous; the paths stay ignored until they are .add()ed again.
  • .close(): async and terminal. Removes all listeners and stops all owned work. Repeated calls return the same Promise; once closing has started, .add() throws and a new FSWatcher is required.
  • .getWatched(): an object whose keys are the watched directories (absolute unless cwd is set) and whose values are arrays of the entry names inside each.
  • .on(event, listener): FSWatcher is an EventEmitter; see events below.

Events

| Event | Listener arguments | | ------------------------- | ------------------------------------------------------------------ | | add, addDir, change | (path, stats?); stats when available, always with alwaysStat | | unlink, unlinkDir | (path) | | all | (event, path, stats?) for each of the five events above | | ready | none; the initial scan is complete | | error | (error) | | raw | (event, path, details) from the backend; unstable, use with care |

Troubleshooting

  • ENOSPC: System limit for number of file watchers reached (Linux): the inotify watch limit is exhausted. Raise it with echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p, or watch less: narrow paths, add ignored (for example node_modules), set depth.
  • EMFILE: too many open files: the process file-descriptor limit is exhausted. Watch less as above, raise the limit (ulimit -n 65536), or use backend: 'polling', which holds no watch handles at the cost of CPU.
  • fsevents-related install errors (WARN optional dep failed, fsevents is not a constructor) are solved by upgrading to v4+.

Changelog

  • v6 (Oct 2026): complete rewrite; new fs.watch(recursive) backend on macos + windows; requires Node.js 22+
  • v5 (Nov 2025): ESM-only; requires Node.js 20+
  • v4 (Sep 2024): remove glob support & fsevents dependency, decrease dep count from 13 to 1; requires Node.js 14+
  • v3 (Apr 2019): massive CPU & RAM consumption improvements; 17x decrease in pkg size + deps; requires Node.js 8.16+
  • v2 (Dec 2017): posix-style globs; bugfixes
  • v1 (Apr 2015): glob support, symlink support, tons of bugfixes; requires Node.js 0.8+
  • v0.1 (Apr 2012): extracted from Brunch

Upgrading

Version 6 requires Node.js 22.22 or newer. An FSWatcher cannot be reopened after .close() starts; construct a new watcher instead. Polling now defaults atomic to false, while an explicit atomic value is preserved. Use pollingInterval and pollingBinaryInterval for polling configuration; the v5 interval and binaryInterval spellings remain as deprecated aliases.

Globs were removed in v4. To replicate them:

// v3
chokidar.watch('**/*.js');
chokidar.watch('./directory/**/*');

// v4+: filter instead
chokidar.watch('.', {
  ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
});
chokidar.watch('./directory');

// or expand the glob yourself
import { glob } from 'node:fs/promises';
const watcher = chokidar.watch(await Array.fromAsync(glob('**/*.js')));

// unwatching
watcher.unwatch('**/*.js'); // v3
watcher.unwatch(await Array.fromAsync(glob('**/*.js'))); // v4+

Contributing

Run npm ci && npm run build && npm test (tests import the built files, so build first). Internals and design requirements are documented in docs/architecture.md; the cross-platform CI setup is in docs/vm-testing.md.

Also

Why was chokidar named this way? What's the meaning behind it?

Chowkidar is a transliteration of a Hindi word meaning 'watchman, gatekeeper', चौकीदार. This ultimately comes from Sanskrit _ चतुष्क_ (crossway, quadrangle, consisting-of-four). This word is also used in other languages like Urdu as (چوکیدار) which is widely used in Pakistan and India.

License

MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.