Speed Up Your Grunt Builds with grunt‑newer: Incremental Tasks Explained
When a front‑end build takes minutes, grunt‑newer can cut that time by filtering tasks to only changed files. Learn how to add it to your Gruntfile, run a concrete uglify example, and understand the trade‑offs that come with incremental builds.
16 Oct 2026, 10:02 UTC

Why Incremental Builds Matter
When a front‑end project grows to dozens of source files, a full Grunt run can take minutes. Every time you tweak a single CSS or JS file, you pay the price of re‑processing the entire bundle. This is especially painful in continuous‑integration pipelines or local developer workflows.
Enter grunt‑newer
grunt‑newer is a lightweight Grunt plugin that tracks file modification times and runs tasks only on the files that have changed since the last run. It works by maintaining a cache of timestamps in a hidden .grunt-newer directory. When you invoke a task with the newer: prefix, the plugin filters the source list before the underlying Grunt task starts.
How It Works Under the Hood
- On first run, grunt‑newer records the
mtime(last modified time) of every source file. - Subsequent runs compare current
mtimevalues against the cached ones. - Only files that are newer than the cached timestamp are passed to the actual task.
- After the task completes, the cache is updated with the new timestamps.
Because the plugin is essentially a thin wrapper, it is compatible with any Grunt task that consumes a file list—uglify, concat, sass, and many others.
Integrating grunt‑newer Into Your Gruntfile
Below is a minimal example that shows how to add grunt‑newer to a project that already uses grunt-contrib-uglify. The steps are the same for most plugins.
module.exports = function(grunt) {
// Load npm tasks
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-newer');
grunt.initConfig({
uglify: {
options: {
mangle: true
},
build: {
files: {
'dist/app.min.js': ['src/**/*.js']
}
}
}
});
// Register a wrapper task that uses newer
grunt.registerTask('build', ['newer:uglify:build']);
};
Key points:
- Install the plugin with
npm install --save-dev grunt-newer. - Prefix the task name with
newer:and optionally the target name. - You can chain multiple tasks:
grunt.registerTask('default', ['newer:uglify', 'newer:concat']);
Concrete Example: Uglify a Large Bundle
Assume the baseline grunt uglify takes 30 seconds to process a 1 MB bundle. After adding grunt‑newer:
- Run
grunt uglifyonce to establish the cache. - Modify
src/utils/helpers.jsand save. - Execute
grunt newer:uglify.
You should see log output similar to:
[grunt-newer] helpers.js is newer, will be processed
[uglify] Processing 1 file(s)
[uglify] 1 file(s) written
Only helpers.js is re‑uglified, and the total runtime drops to roughly 2 seconds—a 93 % reduction.
Trade‑Offs & Limitations
- Stale Output Risk: If a task depends on external data (e.g., a JSON config file) that isn’t part of the source list, changes to that data won’t trigger a rebuild. Always include any non‑source dependencies in the source array.
- Timestamp Granularity: Filesystems with coarse timestamp resolution (e.g., 1 second on Windows) can cause missed rebuilds after rapid edits. A quick
rm -rf .grunt-newerfollowed by a full run can reset the cache. - Side‑Effects: Tasks that perform network requests or write to external services are not automatically re‑executed, as grunt‑newer only filters by file changes.
- Cache Corruption: Corrupted cache files may lead to incorrect incremental behavior. Deleting the
.grunt-newerdirectory forces a clean rebuild.
Verifying Correctness
Use the following checklist to confirm grunt‑newer is working as expected:
- Check installed versions:
grunt --versionandgrunt-newer --version. - Run a baseline task without
newerto measure total time. - Make a trivial change to a source file and run the
newertask. - Verify the log shows only the changed file and that runtime is reduced.
- Delete
.grunt-newerand run again to ensure a full rebuild occurs. - Run
grunt --verboseto see the exact file list processed during an incremental run.
Actionable Takeaway
For projects with large build steps that are deterministic and file‑based, adding grunt‑newer can cut build times dramatically. To get started:
- Add
grunt-newerto yourdevDependenciesand load it in yourGruntfile.js. - Wrap each long‑running task with the
newer:prefix. - Run
grunt --verboseafter the first incremental build to confirm the correct files are being processed. - Keep the
.grunt-newercache in version control only if you have a reason to share incremental state across CI agents; otherwise, ignore it.
With these steps, you’ll experience faster feedback loops without sacrificing build correctness.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.