Optimizing Images with Grunt

You may or may not be new to Grunt, and as you become more comfortable using it you’ll most likely desire diving into other aspects of the tool chain in order to expand upon tasks like code linting, pre-processor compiling and especially image optimization which is the task we’ll be discussing today.

The Grunt plugin called grunt-contrib-imagemin optimizes .png and .jpg files on the fly. The following lines below for fig 1.0 are to be placed within your Gruntfile.js and based on this awesome introduction post by @integralist.

To install grunt-contrib-imagemin run the following command from the terminal within your project’s root directory.

npm install grunt-contrib-imagemin --save-dev

Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:



// http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
  imagemin: {
    png: {
      options: {
        optimizationLevel: 7
      files: [
          // Set to true to enable the following options…
          expand: true,
          // cwd is 'current working directory'
          cwd: 'project-directory/img/',
          src: ['**/*.png'],
          // Could also match cwd line above. i.e. project-directory/img/
          dest: 'project-directory/img/compressed/',
          ext: '.png'
    jpg: {
      options: {
        progressive: true
      files: [
          // Set to true to enable the following options…
          expand: true,
          // cwd is 'current working directory'
          cwd: 'project-directory/img/',
          src: ['**/*.jpg'],
          // Could also match cwd. i.e. project-directory/img/
          dest: 'project-directory/img/compressed/',
          ext: '.jpg'

grunt.registerTask('imagemin', ['imagemin']);
Fig. 1.0 | Image Minification Task for Gruntfile.js

Now that we have our task in place we can now harness the functionality of this plugin and include it into our workflow. For you Codekit users out there you may or may not be aware that as of this writing Codekit bloats image file sizes randomly when optimizing .jpg or .png files.

grunt-contrib-imagemin Output

grayghostvisuals ✭ GrayGhostVisualsMacBookAir.local  ~/Sites/my-project on master*
$ grunt imagemin
Running "imagemin:png" (imagemin) task
✔ img/arrow.png (saved 37.00B)
✔ img/arrow-large.png (saved 37.00B)
✔ img/subnav-bgblocks.png (saved 37.00B)
✔ img/menu-icon.png (saved 37.00B)
✔ img/social-icons.png (saved 38.00B)
✔ img/menu-download.png (saved 39.00B)
✔ img/mobile-logo.png (saved 60.00B)
✔ img/logo.png (saved 37.00B)
✔ img/cow.png (saved 37.00B)
✔ img/skateboarder.png (saved 37.00B)
✔ img/yeah-dude.png (saved 37.00B)
✔ img/farmer-john.png (saved 37.00B)
✔ img/community-plane.png (saved 38.00B)
✔ img/environment.png (saved 37.00B)
✔ img/little-piglet.png (saved 37.00B)
✔ img/noise.png (saved 6.66Kb)
✔ img/right-on-dude.png (saved 37.00B)
✔ img/badge-keep-it-local.png (saved 1.06KB)
✔ img/button-sprite.png (saved 6.66Kb)
✔ img/badge-sprite.png (saved 6.66Kb)
✔ img/badge-bon-appetite.png (saved 7.20Kb)
✔ img/badge-food-to-love.png (saved 7.20Kb)
✔ img/sprites.png (saved 7.29Kb)

Running "imagemin:jpg" (imagemin) task
✔ img/radio-checked.jpg (already optimized)
✔ img/radio-off.jpg (already optimized)
✔ img/home-grown.jpg (saved 26.00B)
✔ img/nav-bg.jpg (saved 6.06Kb)
✔ img/girl-smiling.jpg (already optimized)
✔ img/location-vha.jpg (already optimized)
✔ img/hero-our-story.jpg (saved 4.16Kb)
✔ img/hero-food-for-all.jpg (saved 4.95Kb)
✔ img/noise-crease.jpg (saved 5.89KB)
✔ img/hero-real-food.jpg (already optimized)
✔ img/hero-community.jpg (saved 17.00B)
✔ img/beer-pour.jpg (already optimized)
✔ img/hero-slide1.jpg (saved 9.41KB)
Fig. 1.1 | image optimization output with grunt-contrib-imagemin

As you can see from the output above we not only get to optimize .png files, but we also get to optimize our .jpg files at the same time! If you wanna run this task on just your .png or .jpg files you can customize your grunt.registerTask like so…

grunt.registerTask('imagemin', ['imagemin']); // execute on both .png and .jpg
grunt.registerTask('imagepng', ['imagemin:png']); // only .png files
grunt.registerTask('imagejpg', ['imagemin:jpg']);// only .jpg files
Fig. 1.2 | Customizing Grunt tasks for .png and .jpg files

With the above tasks registered we can now execute the tasks we’ve defined above from our Terminal like so…

# execute on only png files
$ grunt imagepng

# execute on only jpg files
$ grunt imagejpg
Fig. 1.3 | Executing our custom Grunt tasks.

Since using Grunt involves the use of Node and NPM why not take a look at my other article “Keeping Node, NPM, Ruby Updated”, learn other image saving techniques like an “Intro to jpegoptim and optipng” or learn how to lazy load images with Flexslider. See you on the internetz!


Dennis Gaebel

Design Technologist passionate for Open Source, SVG, Typography, Web Animation, Interaction Development & Pattern Based Design. http://droidpinkman.io.
  1. […] Grunt image optimization […]

    1. ❧ mia shouted:
      2014/09/27 • 12:33 pm

      I cannot see compression report for individual files, it only says: ‘Minified 58 images (saved 11.05 kB)’. Why? Is there an option to set for that?
      And just a small thing, I would recommend setting the output path for compressed files outside the directory with original images – this way when running task again files will not be duplicated 🙂

      1. Good point Mia. Definitely the best choice to keep images that are optimized in a separate directory when you run the task. I’ve also experienced my original image files become completely blank after I optimized.

        As far as your compression report I would raise an issue in the tracker in order to get a better idea. I’m not aware of any settings from what I have posted to date.

  2. Since the Roots WP Starter Theme integrated Grunt Workflow I can’t live without it anymore.

  3. Hi,

    This is really a great module. But I have really strange bugs with it. Most of my images compress adequately but some of them have a big black line at the bottom of them. This is no error message when running grunt server. I can’t understand why I have this. Any idea ?

    1. Hey Simon,

      Unfortunately I have no clue without seeing the images and your settings. I highly suggest filing an issue on the repo’s issue tracker.

  4. ❧ ron shouted:
    2013/12/22 • 3:00 am

    I was getting “Warning: Maximum call stack size exceeded grunt imagemin” everytime I tried. (I’m new to using grunt). I did some poking around and found that this line:

    grunt.registerTask('imagemin', ['imagemin']);

    Was causing the problem. If I rename the task to be different ie: (‘default’,[imagemin’]) or (‘imageminAll’,[imagemin’]) the error went away.

    Thanks for the great tutorial.


  5. ❧ MoLo shouted:
    2014/09/11 • 10:19 pm

    Thanks Dennis for this tutorial. Very helpful

    Got everything set-up and working, just wondering how to automate the process with a watch sub-task, so that I don’t have to run the grunt command to minify. Can’t quite get that part working.

    Any advice would be greatly appreciated.

    1. If you’re asking how to create a watch task for your image compression task I would suggest not doing so. Watching your image files and compressing as you work is gonna slow things down for you tremendously. Instead, I suggest making a task called “build” and include all your final build steps like image compression, combining JS files, compressing CSS, etc. If I missed your question completely let me know.

  6. ❧ Chris House shouted:
    2014/09/23 • 3:10 pm

    Hi Dennis,

    I’m needing clarification on the optimizationLevel option. My assumption was that changing this value will increase/decrease the amount of compression, thus changing the file size of the output .png file, but no matter what I set it at I end up with the same end file size. Am I doing something wrong, or am I misunderstanding the purpose of this option?

    1. Via http://optipng.sourceforge.net
      “Filtering does not compress or otherwise reduce the size of the data, but it makes the data more compressible”

      Not sure if this is the answer and quite frankly the explanation on the GitHub docs for this Grunt task is not stated in a clear manner to understand fully. Might be a good issue to raise in the tracker in order to get a response from the community.

Leave a Reply

Your email address will not be published. Required fields are marked *

show formatting examples
<pre class="language-[markup | sass | css | php | javascript | ruby | clike | bash]"><code>
…code example goes here…

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Comment Preview

  1. John Doe shouted this comment preview: