Setting up gulp 4 for automatic Sass compilation and CSS injection

About a month ago I stumbled upon gulpjs, a task runner which makes automation easy and understandable. Even though Gulp is easy, it still took me a bit longer than expected to figure out its usage, especially so for gulp 4.0 (which still happens to be in beta)

The Basics

What is gulp? gulp describes itself as a toolkit which helps automating tasks in your development workflow. If you’re interested to read more about gulp, check out the documentation for gulp 4.0 here.

Setting up gulp4

To set up gulp 4 we should install the latest gulp-cli and the gulp4 beta, we can install these using npm (or yarn) by installing directly from the repo (gulpjs/).

We should install gulp-cli globally and gulp itself as a development dependency. This way you can call the gulp command anywhere (thanks to gulp-cli) but gulp-cli will always check the project folder for the installed gulp version.

$ npm install --global gulpjs/gulp-cli
$ npm install --save-dev gulpjs/gulp#4.0    

Note — if you’re using zsh you will get an error saying there are no matches found for this gulp version. Try putting gulpjs/gulp#4.0 inside quotation marks like so

$ npm install --save-dev "gulpjs/gulp#4.0"    

Step 1: Sass/Scss compilation

We’ve already installed the gulp 4 dependency in our project folder but for sass compilation we should add gulp-sass as well. Note — if you want to use scss, gulp-sass compiles scss files as well!

$ npm install --save-dev gulp-sass    

Now that we’ve got everything installed, the next thing we have to do is create our gulpfile.js. This file should be at the root of your project. At the top of the file we include our dependencies, in this case gulp and gulp-sass.

// gulpfile.js
var gulp = require("gulp");
var sass = require("gulp-sass");

The gulp-cli uses this file to use the dependencies installed locally (rather than globally — gulp4 for example). Having required our dependencies we can run the command below to see if gulp4 is installed correctly.

$ gulp -v
    
[22:51:03] CLI version 1.4.0
[22:51:03] Local version 4.0.0-alpha.2

Good, looks like everything is working fine! This means we can add our sass compilation task, we already included the gulp-sass dependency for this earlier.

// Define tasks after requiring dependencies
function style() {
	// Where should gulp look for the sass files?
	// My .sass files are stored in the styles folder
	// (If you want to use scss files, simply look for *.scss files instead)
	return (
		gulp
			.src("styles/*.sass")

			// Use sass with the files found, and log any errors
			.pipe(sass())
			.on("error", sass.logError)

			// What is the destination for the compiled file?
			.pipe(gulp.dest("styles"))
	);
}

// Expose the task by exporting it
// This allows you to run it from the commandline using
// $ gulp style
exports.style = style;

Now run the task in your terminal and if all goes well your sass file will be compiled (granted you have a sass file at the specified locatoin).

$ gulp style

Obviously we don’t want to have to run this command everytime we want to compile our sass, we can automate this and let gulp do all the dirty work.

Step 2: Automating tasks

Automating tasks is incredibly easy, after all that’s gulp’s purpose. We want our sass files to be compiled anytime it’s changed (and saved), to do this we need gulp to watch these files for changes.

Let’s add a task which watches our sass files and runs the compile task we made earlier in step 1.

function watch(){
    // gulp.watch takes in the location of the files to watch for changes
    // and the name of the function we want to run on change
    gulp.watch('styles/*.sass', style)
}
    
// Don't forget to expose the task!
exports.watch = watch

Now we can run the watch task and it should start watching our sass files and compile on every change.

$ gulp watch
    
[23:17:55] Using gulpfile /www/gulp-guide/gulpfile.js
[23:17:55] Starting 'watch'...

Step 3: Make it DRYer

In step 2 we defined the same thing twice, the location of the sass files. Obviously we don’t want to change both strings everytime we change our project structure, luckily the file is javascript.

// Put this after including our dependencies
var paths = {
	styles: {
		// By using styles/**/*.sass we're telling gulp to check all folders for any sass file
		src: "styles/**/*.sass",
		// Compiled files will end up in whichever folder it's found in (partials are not compiled)
		dest: "styles"
	}

	// Easily add additional paths
	// ,html: {
	//  src: '...',
	//  dest: '...'
	// }
};

Now we can replace the strings in our tasks with the object.

// ...
function style() {
	return gulp
		.src(paths.styles.src)
		.pipe(sass())
		.on("error", sass.logError)
		.pipe(gulp.dest(paths.styles.dest));
}

function watch() {
	//I usually run the compile task when the watch task starts as well
	style();

	gulp.watch(paths.styles.src, style);
}
// ...

Step 4: Let’s add sourcemaps, autoprefixer and css minification

Sure, so far our gulpfile works, it compiles sass whenever our file changes. But we can add more cool stuff like sourcemapsautoprefixer using postcss and css minification using cssnano.

Let’s install the dependencies to be able to use these features. We will need gulp-postcss, autoprefixer, cssnano and gulp-sourcemaps.

$ npm install --save-dev gulp-postcss autoprefixer cssnano gulp-sourcemaps

Then let’s add these to the gulpfile.js

// I don't feel like writing var everytime
var gulp = require("gulp"),
	sass = require("gulp-sass"),
	postcss = require("gulp-postcss"),
	autoprefixer = require("autoprefixer"),
	cssnano = require("cssnano"),
	sourcemaps = require("gulp-sourcemaps");

Now to make use of these, we should edit our style task.

function style() {
	return (
		gulp
			.src("styles/*.sass")
			// Initialize sourcemaps before compilation starts
			.pipe(sourcemaps.init())
			.pipe(sass())
			.on("error", sass.logError)
			// Use postcss with autoprefixer and compress the compiled file using cssnano
			.pipe(postcss([autoprefixer(), cssnano()]))
			// Now add/write the sourcemaps
			.pipe(sourcemaps.write())
			.pipe(gulp.dest("styles"))
	);
}

Now run the gulp check task again and you will see the compiled version is minified!

Step 5: Inject CSS on compilation and refresh on html change

Since we’re already making our lifes easier, let’s go a step further. We can add another package called browser-sync to inject the css changes we make on save. That’s right, no more reloading or refreshing, changes will be visible instantly.

As always we will have to install the package as a development dependency first.

$ npm install --save-dev browser-sync

Then include it at the top of our gulpfile, but this time there is a small difference however. Instead of just including the dependency, we create a browserSync instance.

// ... other includes
var browserSync = require("browser-sync").create();
// ...

function style() {
	return (
		gulp
			.src(paths.styles.src)
			.pipe(sourcemaps.init())
			.pipe(sass())
			.on("error", sass.logError)
			.pipe(postcss([autoprefixer(), cssnano()]))
			.pipe(sourcemaps.write())
			.pipe(gulp.dest(paths.styles.dest))
			// Add browsersync stream pipe after compilation
			.pipe(browserSync.stream())
	);
}

// Add browsersync initialization at the start of the watch task
function watch() {
	browserSync.init({
		// You can tell browserSync to use this directory and serve it as a mini-server
		server: {
			baseDir: "./"
		}
		// If you are already serving your website locally using something like apache
		// You can use the proxy setting to proxy that instead
		// proxy: "yourlocal.dev"
	});
	gulp.watch("styles/*.sass", style);
}

Now if you run the gulp watch task your default browser should open with the index file opened of your project folder. Whenever sass is compiled the css should be injected, a message shows up in the top right of your website whenever this happens.

Now we’ve got that working we still want to automatically refresh our page when our html changes. We will have to add another task to make this happen!

// A simple task to reload the page
function reload() {
	browserSync.reload();
}

function watch() {
	browserSync.init({
		server: {
			baseDir: "./"
		}
	});
	gulp.watch(paths.styles.src, style);
	// We should tell gulp which files to watch to trigger the reload
	// This can be html or whatever you're using to develop your website
	// Note -- you can obviously add the path to the Paths object
	gulp.watch("path/to/html/*.html", reload);
}

// We don't have to expose the reload function
// It's currently only useful in other functions

That’s about it to get started with gulp 4 for web development!

Related articles