gaze vs chokidar vs node-watch vs nodemon vs watch
File System Watching Strategies in Node.js
gazechokidarnode-watchnodemonwatchSimilar Packages:

File System Watching Strategies in Node.js

This comparison evaluates five Node.js packages used for monitoring file system changes. chokidar, gaze, node-watch, and watch are libraries designed to detect file modifications, creations, and deletions, often used in build tools or hot-reloading systems. nodemon is distinct as it is primarily a CLI tool that watches files to automatically restart Node.js applications during development. Understanding the differences helps developers select the right tool for build pipelines, development servers, or production file monitoring tasks.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
gaze2,417,4221,154-688 years agoMIT
chokidar012,08082.1 kB375 months agoMIT
node-watch034226.1 kB83 years agoMIT
nodemon026,697219 kB122 months agoMIT
watch01,281-599 years agoApache-2.0

File System Watching Strategies in Node.js: Architecture and API Compared

When building build tools, hot-module replacement systems, or development servers, reliable file watching is critical. The Node.js ecosystem offers several packages for this, but they differ significantly in maintenance status, API design, and intended use cases. This analysis breaks down chokidar, gaze, node-watch, nodemon, and watch to help you make an informed architectural decision.

🏗️ Initialization and Setup

The way you initialize a watcher varies between libraries. Some return event emitters immediately, while others rely on callback patterns or CLI configurations.

chokidar uses a straightforward function call that returns a watcher instance. It is promise-friendly and integrates well with modern async code.

// chokidar: Initialize watcher
const chokidar = require('chokidar');
const watcher = chokidar.watch('src/**/*.js', {
  ignored: /node_modules/
});

gaze uses a constructor pattern or a function callback. It is heavily oriented around glob patterns from the start.

// gaze: Initialize with callback
const { Gaze } = require('gaze');
const gaze = new Gaze('src/**/*.js', { nodir: true }, function (err) {
  if (err) throw err;
  console.log('Ready');
});

node-watch exports a function that accepts a path and options. It focuses on simplicity and native behavior.

// node-watch: Initialize watcher
const watch = require('node-watch');
const watcher = watch('src', {
  recursive: true,
  filter: (file, skip) => file.endsWith('.js')
});

nodemon is primarily a CLI tool, but it can be required programmatically. Note that programmatic usage is less common and often discouraged in favor of CLI.

// nodemon: Programmatic usage (CLI is preferred)
const nodemon = require('nodemon');
nodemon({
  script: 'app.js',
  ext: 'js json'
});

watch (deprecated) used a tree-based monitoring approach. It is included here for legacy reference only.

// watch: Initialize monitor (Deprecated)
const watch = require('watch');
watch.watchTree('src', function (f, curr, prev) {
  console.log('File changed');
});

🔔 Event Handling Models

How each library notifies you of changes is crucial for integrating with build pipelines or live reload servers.

chokidar emits standard Node.js stream events like add, change, and unlink. This consistency makes it easy to wire into other tools.

// chokidar: Event listeners
watcher.on('add', (path) => console.log(`File added: ${path}`));
watcher.on('change', (path) => console.log(`File changed: ${path}`));
watcher.on('unlink', (path) => console.log(`File removed: ${path}`));

gaze uses similar event names but prefixes some with all or uses specific naming like added instead of add.

// gaze: Event listeners
gaze.on('added', (file) => console.log(`File added: ${file}`));
gaze.on('changed', (file) => console.log(`File changed: ${file}`));
gaze.on('deleted', (file) => console.log(`File removed: ${file}`));

node-watch emits an update event for all changes, passing the event type as an argument. This consolidates handling into one callback.

// node-watch: Single update event
watcher.on('update', function (event, file) {
  console.log(`Event: ${event}, File: ${file}`);
});

nodemon emits lifecycle events related to the process restart, such as restart or crash, rather than raw file events.

// nodemon: Lifecycle events
nodemon.on('restart', function (files) {
  console.log('App restarted due to:', files);
});

watch passed details to the callback function directly during initialization, lacking a flexible event emitter interface.

// watch: Callback based (Deprecated)
watch.watchTree('src', function (f, curr, prev) {
  if (typeof f == "object" && f === null) return;
  console.log('File modified');
});

🌐 Globbing and Pattern Matching

Developers often need to watch specific file types while ignoring others like node_modules.

chokidar supports glob patterns natively in the watch call. It handles complex patterns efficiently.

// chokidar: Native glob support
chokidar.watch('src/**/*.{js,ts}', {
  ignored: ['**/node_modules/**', '**/*.test.js']
});

gaze was built specifically for globbing and handles complex arrays of patterns well.

// gaze: Array of patterns
new Gaze(['src/**/*.js', 'test/**/*.js'], { nodir: true });

node-watch requires a filter function for specific file types rather than glob strings in the path argument.

// node-watch: Filter function
watch('src', {
  recursive: true,
  filter: (file) => /\.js$/.test(file)
});

nodemon uses an ext configuration to specify extensions rather than full glob patterns.

// nodemon: Extension config
nodemon({
  script: 'app.js',
  ext: 'js,json,css'
});

watch had limited glob support and often required external libraries to handle pattern matching effectively.

// watch: Limited pattern support (Deprecated)
// Often required manual filtering inside the callback

⚠️ Maintenance and Deprecation Status

Long-term stability is vital for infrastructure code. Some of these packages are no longer safe for new development.

chokidar is actively maintained and widely adopted in major tools like Vite, Webpack, and Jest. It receives regular security updates.

gaze sees minimal updates. While stable, it is considered legacy. New projects should treat it as technical debt.

node-watch is maintained but has a smaller footprint. It is safe to use but lacks the ecosystem support of chokidar.

nodemon is actively maintained for its specific purpose (dev server restarts). It is not deprecated but should not be used as a general library.

watch is officially deprecated. The repository is archived, and npm marks it as legacy. Using it introduces security and compatibility risks.

📊 Summary Comparison

Featurechokidargazenode-watchnodemonwatch
Primary UseLibraryLibraryLibraryCLI ToolLibrary
Status✅ Active⚠️ Legacy✅ Active✅ Active❌ Deprecated
Glob SupportNativeNativeFilter FuncExtensionsLimited
Eventsadd/change/unlinkadded/changed/deletedupdaterestart/crashCallback
RecursiveYesYesYesYesYes
Cross-PlatformHighMediumHighHighLow

💡 Final Recommendation

For any new project requiring file monitoring logic, chokidar is the clear choice. It balances performance, API clarity, and maintenance reliability better than any other option. Use nodemon strictly for running development servers via the command line, not as a dependency in your code. Avoid watch entirely due to deprecation, and limit gaze to legacy maintenance tasks. If you need a lightweight alternative and can handle filter functions, node-watch is a valid secondary option.

How to Choose: gaze vs chokidar vs node-watch vs nodemon vs watch

  • gaze:

    Choose gaze only if you are maintaining legacy build systems tied to older Grunt or Gulp configurations. It provides robust globbing support but is considered legacy technology with lower activity compared to modern alternatives. New projects should avoid it in favor of more actively maintained libraries like chokidar.

  • chokidar:

    Choose chokidar for production-grade file watching in modern applications. It is the industry standard, offering cross-platform consistency, high performance, and active maintenance. It handles native file system inconsistencies gracefully and supports glob patterns out of the box. This is the safest choice for new projects requiring reliable file monitoring.

  • node-watch:

    Choose node-watch if you need a lightweight wrapper around native file system events without heavy dependencies. It offers recursive watching and works well for simple use cases where chokidar might be overkill. However, it has a smaller community and fewer features for complex globbing scenarios.

  • nodemon:

    Choose nodemon specifically for development environments where you need to automatically restart your Node.js server when code changes. It is not designed as a library to import into your application logic but rather as a command-line utility. Do not use it for general file watching tasks within your app code.

  • watch:

    Avoid using watch for new projects as it is officially deprecated and no longer maintained. It was one of the original file watching packages but has been superseded by more robust solutions. Existing projects should migrate to chokidar to ensure security updates and compatibility with modern Node.js versions.

README for gaze

gaze Build Status Build status

A globbing fs.watch wrapper built from the best parts of other fine watch libs.
Compatible with Node.js >= 4.x, Windows, macOS, and Linux.

gaze

NPM

Usage

Install the module with: npm install gaze or place into your package.json and run npm install.

var gaze = require('gaze');

// Watch all .js files/dirs in process.cwd()
gaze('**/*.js', function(err, watcher) {
  // Files have all started watching
  // watcher === this

  // Get all watched files
  var watched = this.watched();

  // On file changed
  this.on('changed', function(filepath) {
    console.log(filepath + ' was changed');
  });

  // On file added
  this.on('added', function(filepath) {
    console.log(filepath + ' was added');
  });

  // On file deleted
  this.on('deleted', function(filepath) {
    console.log(filepath + ' was deleted');
  });

  // On changed/added/deleted
  this.on('all', function(event, filepath) {
    console.log(filepath + ' was ' + event);
  });

  // Get watched files with relative paths
  var files = this.relative();
});

// Also accepts an array of patterns
gaze(['stylesheets/*.css', 'images/**/*.png'], function() {
  // Add more patterns later to be watched
  this.add(['js/*.js']);
});

Alternate Interface

var Gaze = require('gaze').Gaze;

var gaze = new Gaze('**/*');

// Files have all started watching
gaze.on('ready', function(watcher) { });

// A file has been added/changed/deleted has occurred
gaze.on('all', function(event, filepath) { });

Errors

gaze('**/*', function(error, watcher) {
  if (error) {
    // Handle error if it occurred while starting up
  }
});

// Or with the alternative interface
var gaze = new Gaze();
gaze.on('error', function(error) {
  // Handle error here
});
gaze.add('**/*');

Minimatch / Glob

See isaacs's minimatch for more information on glob patterns.

Documentation

gaze([patterns, options, callback])

  • patterns {String|Array} File patterns to be matched
  • options {Object}
  • callback {Function}
    • err {Error | null}
    • watcher {Object} Instance of the Gaze watcher

Class: gaze.Gaze

Create a Gaze object by instancing the gaze.Gaze class.

var Gaze = require('gaze').Gaze;
var gaze = new Gaze(pattern, options, callback);

Properties

  • options The options object passed in.
    • interval {integer} Interval to pass to fs.watchFile
    • debounceDelay {integer} Delay for events called in succession for the same file/event in milliseconds
    • mode {string} Force the watch mode. Either 'auto' (default), 'watch' (force native events), or 'poll' (force stat polling).
    • cwd {string} The current working directory to base file patterns from. Default is process.cwd().

Events

  • ready(watcher) When files have been globbed and watching has begun.
  • all(event, filepath) When an added, changed, renamed, or deleted event occurs.
  • added(filepath) When a file has been added to a watch directory.
  • changed(filepath) When a file has been changed.
  • deleted(filepath) When a file has been deleted.
  • renamed(newPath, oldPath) When a file has been renamed.
  • end() When the watcher is closed and watches have been removed.
  • error(err) When an error occurs.
  • nomatch When no files have been matched.

Methods

  • emit(event, [...]) Wrapper for EventEmitter.emit. added|changed|renamed|deleted events will also trigger the all event.
  • close() Unwatch all files and reset the watch instance.
  • add(patterns, callback) Adds file(s) patterns to be watched.
  • remove(filepath) Removes a file or directory from being watched. Does not recurse directories.
  • watched() Returns the currently watched files.
  • relative([dir, unixify]) Returns the currently watched files with relative paths.
    • dir {string} Only return relative files for this directory.
    • unixify {boolean} Return paths with / instead of \\ if on Windows.

Similar Projects

Other great watch libraries to try are:

Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using grunt.

Release History

  • 1.1.3 - Fix for Node 10 support (@aredridel). Officially dropping support for Node < 4.
  • 1.1.2 - Prevent more ENOENT errors from escaping (@alexgorbatchev).
  • 1.1.1 - Prevent fs.watch errors from escaping error handler (@rosen-vladimirov). Fix _addToWatched without path.sep (@wyicwx).
  • 1.1.0 - Update to globule@1.0.0 with minimatch >= 3.0.0.
  • 1.0.0 - Revert back to 0.5.2. Drop support for Node.js v0.8. Fix for maxListeners. Update globule to 0.2.0.
  • 0.6.4 - Catch and emit error from readdir (@oconnore). Fix for 0 maxListeners. Use graceful-fs to avoid EMFILE errors in other places fs is used. Better method to determine if pathwatcher was built. Fix keeping process alive too much, only init pathwatcher if a file is being watched. Set min required to Windows Vista when building on Windows (@pvolok).
  • 0.6.3 - Add support for Node.js v0.11
  • 0.6.2 - Fix argument error with watched(). Fix for erroneous added events on folders. Ignore msvs build error 4244.
  • 0.6.1 - Fix for absolute paths.
  • 0.6.0 - Uses native OS events (fork of pathwatcher) but can fall back to stat polling. Everything is async to avoid blocking, including relative() and watched(). Better error handling. Update to globule@0.2.0. No longer watches cwd by default. Added mode option. Better EMFILE message. Avoids ENOENT errors with symlinks. All constructor arguments are optional.
  • 0.5.2 - Fix for ENOENT error with non-existent symlinks [BACKPORTED].
  • 0.5.1 - Use setImmediate (process.nextTick for Node.js v0.8) to defer ready/nomatch events (@amasad).
  • 0.5.0 - Process is now kept alive while watching files. Emits a nomatch event when no files are matching.
  • 0.4.3 - Track file additions in newly created folders (@brett-shwom).
  • 0.4.2 - Fix .remove() method to remove a single file in a directory (@kaelzhang). Fixing “Cannot call method 'call' of undefined” (@krasimir). Track new file additions within folders (@brett-shwom).
  • 0.4.1 - Fix watchDir not respecting close in race condition (@chrisirhc).
  • 0.4.0 - Drop support for Node.js v0.6. Use globule for file matching. Avoid Node.js v0.10 path.resolve/join errors. Register new files when added to non-existent folder. Multiple instances can now poll the same files (@jpommerening).
  • 0.3.4 - Code clean up. Fix “path must be strings” errors (@groner). Fix incorrect added events (@groner).
  • 0.3.3 - Fix for multiple patterns with negate.
  • 0.3.2 - Emit end before removeAllListeners.
  • 0.3.1 - Fix added events within subfolder patterns.
  • 0.3.0 - Handle safewrite events, forceWatchMethod option removed, bug fixes and watch optimizations (@rgaskill).
  • 0.2.2 - Fix issue where subsequent add calls dont get watched (@samcday). removeAllListeners on close.
  • 0.2.1 - Fix issue with invalid added events in current working dir.
  • 0.2.0 - Support and mark folders with path.sep. Add forceWatchMethod option. Support renamed events.
  • 0.1.6 - Recognize the cwd option properly
  • 0.1.5 - Catch “too many open file” errors
  • 0.1.4 - Really fix the race condition with 2 watches
  • 0.1.3 - Fix race condition with 2 watches
  • 0.1.2 - Read triggering changed event fix
  • 0.1.1 - Minor fixes
  • 0.1.0 - Initial release

License

Copyright (c) 2018 Kyle Robinson Young
Licensed under the MIT license.