Uglifying JavaScript
Install and configure the grunt-contrib-uglify plugin to minify JavaScript — reducing file size for faster production downloads.
Uglifying JavaScript
In the previous episodes, we concatenated JavaScript files into a single bundle and compiled SASS into CSS. Now it is time to uglify (minify) the JavaScript for production. Uglification reduces file size by removing whitespace, shortening variable names, and stripping comments — making your code download and execute faster.
What Does Uglify Do?
| Technique | Before | After |
|---|---|---|
| Remove whitespace | function add( a, b ) { | function add(a,b){ |
| Shorten variables | var message = 'hello'; | var a='hello'; |
| Remove comments | // This adds two numbers | (removed entirely) |
| Remove dead code | if (false) { ... } | (removed entirely) |
| Simplify expressions | var x = true; | var x=!0; |
A typical uglified file is 40-70% smaller than the original. For large applications, this saves significant bandwidth and load time.
Step 1: Install the Plugin
npm install grunt-contrib-uglify --save-dev
Step 2: Configure the Task
Add the uglify task to your Gruntfile.js initConfig:
uglify: {
options: {
banner: '/* <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */\n',
},
build: {
src: 'dist/js/bundle.js',
dest: 'dist/js/bundle.min.js',
},
},
Understanding the Configuration
| Property | Value | Purpose |
|---|---|---|
options.banner | Comment string | A preserved comment prepended to the output — useful for copyright notices |
build.src | 'dist/js/bundle.js' | The concatenated file from the previous step |
build.dest | 'dist/js/bundle.min.js' | The minified output file |
Notice the source is the output of the concat step — we concatenate first, then uglify the result. This is the correct pipeline order: concat → uglify.
The grunt.template.today Helper
<%= grunt.template.today("yyyy-mm-dd") %>
This is a built-in Grunt helper that outputs the current date. Useful for build banners so you know when the file was last built.
Step 3: Load the Plugin and Update Tasks
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['concat', 'sass:dev']);
grunt.registerTask('build', ['concat', 'uglify', 'sass:prod']);
The build task now runs all three steps in order: concatenate JavaScript, uglify the result, and compile SASS in compressed mode. The default task skips uglification since you want readable code during development.
Step 4: Run It
# Development (no uglification)
grunt
# Production (with uglification)
grunt build
# Just uglify
grunt uglify
The Output
The original dist/js/bundle.js might look like:
/* grunt-project - v1.0.0 */
// Module 1: Utility functions
function greet(name) {
return 'Hello, ' + name + '!';
}
function add(a, b) {
return a + b;
}
...
After uglification, dist/js/bundle.min.js looks like:
/* grunt-project - v1.0.0 - 2024-01-15 */
function greet(n){return"Hello, "+n+"!"}function add(n,t){return n+t}...
Uglify Options
| Option | What It Does |
|---|---|
mangle | Shorten variable and function names (default: true) |
compress | Apply code transformations to reduce size (default: true) |
beautify | Format output with indentation (default: false) |
banner | Prepend a string to the output (not removed by minification) |
sourceMap | Generate a source map for debugging minified code |
preserveComments | Keep some or all comments (e.g., 'some' keeps comments with !) |
Customizing Uglify Behavior
uglify: {
options: {
mangle: true,
compress: {
drop_console: true, // Remove console.log statements
dead_code: true, // Remove unreachable code
},
banner: '/* © <%= pkg.name %> */\n',
sourceMap: true,
},
build: {
src: 'dist/js/bundle.js',
dest: 'dist/js/bundle.min.js',
},
},
The drop_console: true option is particularly useful — it automatically removes all console.log, console.warn, and console.error statements from your production build so debugging code never reaches users.
Source Maps
uglify: {
options: {
sourceMap: true,
sourceMapName: 'dist/js/bundle.map',
},
build: {
src: 'dist/js/bundle.js',
dest: 'dist/js/bundle.min.js',
},
},
A source map is a file that maps minified code back to the original source. When you open browser DevTools, the debugger shows your original readable code instead of the minified version. This makes it possible to debug production code.
The Complete Gruntfile
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';\n',
banner: '/* <%= pkg.name %> - v<%= pkg.version %> */\n',
},
dist: {
src: ['src/js/*.js'],
dest: 'dist/js/bundle.js',
},
},
uglify: {
options: {
banner: '/* <%= pkg.name %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n',
},
build: {
src: 'dist/js/bundle.js',
dest: 'dist/js/bundle.min.js',
},
},
sass: {
dev: {
options: { style: 'expanded' },
files: {
'dist/css/styles.css': 'src/scss/styles.scss',
},
},
prod: {
options: { style: 'compressed' },
files: {
'dist/css/styles.min.css': 'src/scss/styles.scss',
},
},
},
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-sass');
// Development: concat + readable CSS
grunt.registerTask('default', ['concat', 'sass:dev']);
// Production: concat + uglify + compressed CSS
grunt.registerTask('build', ['concat', 'uglify', 'sass:prod']);
};
The Build Pipeline
| Step | Task | Input | Output |
|---|---|---|---|
| 1 | concat | src/js/*.js (multiple files) | dist/js/bundle.js (one file) |
| 2 | uglify | dist/js/bundle.js (readable) | dist/js/bundle.min.js (minified) |
| 3 | sass | src/scss/styles.scss | dist/css/styles.css or .min.css |
Key Takeaways
grunt-contrib-uglifyminifies JavaScript by removing whitespace, shortening names, and stripping comments- Uglification typically reduces file size by 40-70%
- The build pipeline runs in order:
concat→uglify→sass - The
banneroption preserves a copyright comment at the top of the minified file drop_console: trueremoves all console statements from production builds- Source maps allow you to debug minified code by mapping it back to the original source
- Use
defaultfor development (readable) andbuildfor production (minified) workflows