I promised myself I wouldn’t get involved in any more Gulp tutorials; task runners aren’t exactly the sexiest topic in the world, and chances are if you’ve made it to this blog, you’ve either solidified a CI/CD pipeline for going live with software, or you simply don’t need one. We’ll make an exception this time, because gulp-imagemin is particularly dope.
Imagemin is a standalone Node library which also comes with a CLI, and of course, a Gulp plugin. In short, imagemin compresses images in a given directory and is intelligent enough to recognize images it has already compressed. This is huge because it means we can recklessly tell imagemin to compress the same folder of images hundreds of times, and each image will only be compressed exactly once.
For this tutorial, we’ll be taking gulp-imagemin and creating a task to compress images in complex folder structures.
Using Imagemin on Complex Folder Structures
We’ve probably mentioned this once or twice before, but this blog is a theme running on a Ghost stack. The thing about Ghost (and probably any other blogging platform) is that it stores content in a date-based folder hierarchy. /images looks like this:
Imagemin does not work recursively, so we’ll need to handle looping through this file structure ourselves.
Starting our Gulpfile
Let’s get started by going through the barebones of the libraries required to make this happen:
gulp-imagemin is the core Gulp plugin we need to compress our images, but is actually useless on it’s own — we need to also import plugins-for-a-plugin; gulp-imagemin requires a separate plugin for each image type we need to express.
We’re also requiring fs and path here, which will let us walk through folder structures programmatically.
Imagemin Plugins
As mentioned imagemin itself has plugins per image type: only require the ones you think you’ll need:
For the sake of keeping this tutorial simple, we’ll limit our use case to JPGs.
A particular standout here worth mentioning here is WebP: a “next-gen” image compression for the web which supposedly offers the best image quality for the smallest file size available.
Let’s Get This Going
A common practice is to specify the filepaths to an app’s assets (such as styles, or images) as a single variable in their Gulpfile. This is even more relevant in the case of anybody using Ghost, where images are in a totally different file structure from where our Gulpfile lives.
let gulp = require('gulp'),
imagemin = require('gulp-imagemin'),
imageminJpegtran = require('imagemin-jpegtran'),
fs = require('fs'),
path = require('path');
let paths = {
styles: {
src: 'src/less/*.less',
dest: 'assets/css'
},
scripts: {
src: 'src/js/*.js',
dest: 'assets/js'
},
html: {
src: 'views/*.hbs',
dest: 'assets/'
},
images: {
src: 'src/images/',
dest: 'dist/images/'
}
};
Looping Through Subdirectories
We defined some key directories in our paths
variable, notably the two directories defined in paths.images
: the source directory (uncompressed) and the dist directory (for production-ready, compressed images). We need to step through our /src/images
folder and recursively fetch all subdirectories containing images.
We can accomplish this with a simple loop:
fs.readdir()
is a method that returns the contents of any directory. Our newly defined function optimize_images()
will loop through all subfolders in a target directory recursively, which allows us to fetch all images in a nested directory. Finally, we'll then call another function to compress the images (that comes next).
Compressing Images in Each Folder
optimize_images()
kicks us off by recursively looping through all subdirectories of src/images/
. We can use imagemin on a directory of images to find and compress every image of a certain type (or all types, but we're sticking to .jpgs for the sake of this tutorial).
To do this, we can write a new function to execute on each subdirectory we find. Lets name this function compress_images_in_directory()
, as we'll be executing it per subdirectory:
function compress_images_in_directory(directory) {
return gulp.src(folder_path + '/*.jpg')
.pipe(imagemin(
[imageminJpegtran({progressive: true})],
{verbose: true}
))
.pipe(gulp.dest(paths.images.dest));
}
While it looks simple, this function accomplishes a number of things for us:
- Looking for files ending in .jpg in each subdirectory of
src/images
. - Run imageminJpegtran to compress a copy of each JPG file.
- Write the new, compressed images to the destination directory of
dist/images
. - Print the results to the console via the verbose parameter (this prints as:
“Minified 10 images”
).
Put it All Together
We're left with a final gulpfile that ultimately looks for uncompressed images in a source folder, and then creates compressed versions of said images to a dist folder which maintaining the same file structure:
And there you have it; a Gulpfile which compresses your images without intruding requiring any sort of relinking.
If you’re interested in imagemin or further optimizing your site, I highly recommend Google’s recently announced beta of https://web.dev. This is an excellent resource for auditing your site for opportunities on speed, SEO, and more.