Gulp for Beginners

How to set up Gulp 4 for Sass and JS

Looking for a way to improve your front-end workflow? 

If you haven’t used Gulp yet, I highly recommend that you check it out. You can use Gulp to run a lot of helpful tasks, such as compiling your Sass files to CSS, processing JavaScript files, and optimizing your files.

If you’d like even more in-depth information on how to set up Gulp, check out my course, Gulp 4 for Beginners!

Here are the basic steps to install and run Gulp, that we’ll be covering in this tutorial:

  1. Install the gulp-cli on your command line by running npm install gulp-cli -g.
  2. Install Gulp by running npm install gulp.
  3. Install other npm packages for your Gulp workflow.
  4. Create a gulpfile.js file in your project root.
  5. Import your npm packages as modules in your gulpfile.
  6. Add your tasks to the gulpfile to compile your SCSS/JS files and run a watch task.
  7. Run the gulp command to run all your tasks.

What is Gulp and what does it do?

Gulp is a tool that will run various tasks for you in your web development workflow. It might be called a bundler, build tool, or a task runner. They all mean roughly the same thing.

Similar tools are Webpack and Grunt (although Grunt is quickly becoming obsolete).

Here’s what we’re going to have Gulp do for us:

  1. Compile our Sass files to CSS
  2. Add vendor prefixes to our CSS
  3. Minify our final CSS file
  4. Concatenate (i.e. combine) our JS files
  5. Uglify our final JS file
  6. Move the final CSS/JS files into our /dist folder.

Pretty cool, right?!

So the way it works is, all your settings and tasks are stored in a gulpfile.js file. And you run Gulp on your command line.

The great part about that is that once you have your gulpfile all set up, you can easily reuse it for other projects. So it’s a huge time-saver!

Let’s move on to installing and setting up Gulp on your computer.

Installing Gulp, using a working demo project

Before you can run Gulp, you will need to install a couple of things:

Once you have Gulp working, check out a demo project I built that uses Gulp!

It’s a front-end boilerplate project that’s meant to be a quick way for me to get started with any simple front-end website.

(I also have plenty of code snippets in the rest of this tutorial, so you can just refer to those as well!)

To set the front-end boilerplate project up:

  • Clone or download the Git repo of this project onto your computer.
  • Open the project, and in the root project folder, go to your command line and run npm install. This will install the npm packages listed in the package.json file, particularly Gulp 4.

You should now have the project files set up, and all the npm packages installed that we’ll be using to run Gulp tasks.

Since the files from the repo are ready to go, if you type in gulp in the command line, you should see an output like this:

> gulp
[22:29:48] Using gulpfile ~\Documents\GitHub\frontend-boilerplate\gulpfile.js
[22:29:48] Starting 'default'...
[22:29:48] Starting 'scssTask'...
[22:29:48] Starting 'cacheBustTask'...
[22:29:48] Finished 'cacheBustTask' after 39 ms
[22:29:48] Starting 'jsTask'...
[22:29:48] Finished 'jsTask' after 340 ms
[22:29:48] Finished 'scssTask' after 347 ms
[22:29:48] Starting 'watchTask'...

And you’ve run Gulp!

What’s happening in the project when you run Gulp?

All right, you have the project successfully working! Now let’s get into more detail on what’s in the project and how it works.

First, here’s a quick rundown of the file structure of our project:

  • index.html — your main HTML file
  • package.json — contains all the npm packages to install when you run npm install.
  • gulpfile.js — contains the config and runs all the Gulp tasks
  • /app — working folder, you will edit SCSS/JS files in here
  • /dist — Gulp will output files here, don’t edit these files

In your workflow, you will edit the HTML, SCSS, and JS files. Gulp will then detect any changes and compile everything. Then you will load your final CSS/JS files from the /dist folder in your index.html file.

Now that we have Gulp running, let’s take a closer look at how Gulp works.

What exactly is in the gulpfile.js?

Here’s an in-depth explanation of each section of the gulpfile, and what they do:

Step 1: Initialize npm modules

At the very top of the gulpfile.js, we have a whole bunch of constants that import the npm packages we installed earlier, using the require() function.

Here’s what our packages do:

Gulp package:

  • gulp — runs everything in the gulpfile.js. We’re exporting just the specific gulp functions that we will be using, like srcdestwatch, and others. This allows us to call those functions directly without the gulp, for example we can just type in src() instead of gulp.src().

CSS packages:

  • gulp-sourcemaps — maps the CSS styles back to the original SCSS file in your browser dev tools
  • gulp-sass — compiles SCSS to CSS
  • gulp-postcss — runs autoprefixer and cssnano (see below)
  • autoprefixer — adds vendor prefixes to CSS
  • cssnano — minifies CSS

If you’ve used Gulp before, you might be wondering why I’m using straight-up autoprefixer and cssnano, instead of gulp-autoprefixer and gulp-cssnano. For some reason, using them will work, but will prevent sourcemaps from working. Read more about that issue here.

JS packages:

  • gulp-concat — concatenates multiple JS files into one file
  • gulp-uglify — minifies JS

We’re also using a string replace to do some cache busting on the CSS and JS file references in the index.html. This ensures that when you make file changes, the website will load the newest copy of those files.

String replace package:

  • gulp-replace — add a string parameter to CSS/JS references for cache bust

Now that we have our modules added, let’s put them to work!

Step 2: Use the modules to run your Gulp tasks

A “task” in Gulp is basically a function that performs a specific purpose.

We’re creating a few utility tasks to compile our SCSS and JS files, and also to watch those files for any changes. Then those utility tasks will be run in our default Gulp task when we type gulpon the command line.

Creating constants for file paths

Before writing our tasks, though, we have a few lines of code that save our file paths as constants. This is optional, but I like using path variables so that we don’t have to manually retype them each time:

This code creates scssPath and jsPath constants that we will use to tell Gulp where files are found.

Sass task

Here’s the code for our Sass task:

function scssTask(){    
    return src(files.scssPath)
        .pipe(postcss([ autoprefixer(), cssnano() ]))

Our Sass task, called scssTask(), is doing several things. In Gulp, you can chain multiple functions by using the Gulp function pipe() after the first function.

In our task, Gulp is first running src() to load the source directory of the SCSS files. It’s using the constant we created earlier, files.scssPath.

Then after src() we’re piping everything else into the build process. You can think about it like adding more and more sections of pipe onto an existing pipe.

The functions it’s running are:

  • sourcemaps.init() — sourcemaps needs to be added first after src()
  • sass() does the compiling of all the SCSS files to one CSS file
  • postcss() runs two other plugins:
    • autoprefixer() to add vendor prefixes to the CSS
    • cssnano() to minify the CSS file
  • sourcemaps.write() creates the sourcemaps file in the same directory.
  • dest() tells Gulp to put the final CSS file and CSS sourcemaps file in the /dist folder.

JS task

Here’s the code for our JavaScript task:

function jsTask(){
    return src([files.jsPath])

Our JavaScript task, called jsTask(), is also running multiple functions:

  • src() to load the JS files from files.jsPath.
  • concat() to concatenate all the JS files into one JS file.
  • uglify() to uglify/minify the JS file.
  • dest() to move the final JS file into the /dist folder.

Cache bust task

Here’s the code for the cache bust task:

var cbString = new Date().getTime();
function cacheBustTask(){
    return src(['index.html'])
        .pipe(replace(/cb=\d+/, 'cb=' + cbString))

In the index.html file, I’m using the following references for CSS and JS files:

<link rel="stylesheet" href="dist/style.css?cb=123">

<script src="dist/all.js?cb=123"></script>

The reason that cache bust is helpful is because your browser and the web host server will often cache, or save, a copy of asset files like CSS and JS files. So when you load the same page again, it’s quicker for the browser to just load up those locally cached copies instead of having to re-download them from the server.

The problem is that sometimes you don’t want to load the old saved version. When you’ve made changes to your CSS or JS file and you redeploy to the server, you want to force people to re-download those files.

So one way to force the re-download is to add a little querystring
 ?cb=123 to the end of those file references. If the browser reloads the page and the querystring is different from the last time, it will refresh the file.

I’m using the Gulp replace() function to look for strings containing “cb=” and any number. And replace that number with a new number made up of the current time converted to milliseconds since 1970.

That way every time Gulp runs, the querystring will be different and you’ll make sure that the new CSS and JS files get loaded in the user’s browser

Watch task

The Gulp watch() function is a super useful feature. When you run it, it will run continuously, watching your files to detect any changes. When it does detect changes, it will run any number of tasks you tell it to.

This is great because then you don’t have to keep manually running gulp after every code change.

Here’s the code for our Gulp watch task:

function watchTask(){
        [files.scssPath, files.jsPath],
        parallel(scssTask, jsTask)

The watch() function takes three parameters, but we’re just using two:

  • globs, meaning the file path strings,
  • options (not used), and
  • tasks, meaning which tasks we want to run.

What we’re having our watch task do is watch the files in our scssPath and jsPath directories. Then if any changes are made in either, run the scssTask and jsTask tasks simultaneously.

Now that we’ve gotten our utility tasks set up, we need to set up our main Gulp task.

Default Gulp task

This is the main Gulp task, which will automatically run if you type in gulp on the command line.

exports.default = series(
    parallel(scssTask, jsTask), 

Gulp will automatically search for a default task in your gulpfile.js when you use the gulpcommand. So you must export the default task in order for it to work.

Our default task will do the following:

  • Run the scssTask and jsTask simultaneously using parallel()
  • Then run the watchTask

You’ll also notice that we are putting all those tasks inside the series() function.

This is one of the major new changes in Gulp 4 for how it handles tasks– you are required to wrap all tasks (even single ones) either in series() or parallel().

Changes in Gulp 4

If you’ve been using the tasks() function to run everything, you may have noticed that there’s a difference now.

Instead of using gulp.task() to contain all your task functions, we’re creating actual functions like scssTask() and watchTask().

This is to follow Gulp’s official guidelines for creating tasks.

The Gulp team recommends using exports rather than tasks():

“In the past, task() was used to register your functions as tasks. While that API is still available, exporting should be the primary registration mechanism, except in edge cases where exports won’t work.” [Gulp Documentation]

So, while they still let you use the task() function, it’s more current to create JS functions for each task and then export them. If you can, I’d recommend updating your syntax to reflect this, as it’s possible that Gulp will deprecate task() at some point.

Common error: “The following tasks did not complete… Did you forget to signal async completion?”

When working in Gulp 4, you may come across this error. It is caused because in Gulp 4 all tasks are automatically asynchronous.

Synchronous functions execute one after the other, and each function must wait until the previous one is completed before themselves running. Asynchronous functions, on the other hand, can start running while still waiting for the result of previous functions.

This makes processes more efficient since they aren’t blocked. But in order to know when a function has completed, we need them to return explicit signifiers that they are done. Most Gulp tasks like src() or dest() will return a Node stream, so you won’t have to worry about it.

However, if you have a custom function that isn’t returning a stream, you need to add a callback function at the end of it. Otherwise you will get that error message.

To fix the error, you will need to add a callback function like in this example:

function helloWorld(cb){
    console.log('Hellooooo world!');

In this helloWorld function, we’ve added the cb() callback function that we set as a parameter, and then invoke at the end of the function. Adding this will signal async completion, and should fix that error happening.

Problems Migrating from Gulp 3?

If you’ve been using Gulp 3 and all you want is to get the dang thing working with Gulp 4, you’re in luck!

The main breaking change involves how you run tasks.

Gulp 4 introduces two new functions to run tasks: series() and parallel(). It gives you the option of running multiple tasks concurrently, or one after the other.

Before, in Gulp 3, you could simply list a single function or multiple functions in an array. However, in Gulp 4, doing so without wrapping them in either series() or parallel() will throw an error now.

Something like:

AssertionError [ERR_ASSERTION]: Task function must be specified

Here’s how you can quickly fix that:

Tasks in (old) Gulp 3 syntax

In Gulp 3, you might have run tasks this way:

gulp.task('default', ['sass', 'js', 'watch']);'app/scss/*.scss', ['sass']);

Tasks in Gulp 4 syntax

Put those tasks into a series() function like this:

gulp.task('default', gulp.series('sass', 'js', 'watch'));'app/scss/*.scss', gulp.series('sass'));

And that will fix the task function error with the smallest change possible! πŸ™‚

Project files download

All the code I’ve displayed here is from a GitHub repository I have for front-end boilerplate. It’s meant as a quick starter kit for any simple front-end website project.

You’re welcome to check it out, customize, and use it for your own projects!

Check out the GitHub repository here.

In closing

I hope that you’ve found this walk-through of how to get Gulp 4 running helpful!

If you enjoyed this post or have a question, feel free to leave a comment below πŸ™‚

Want to learn how to build a website?

I'm making a course that will teach you how to build a real-world responsive website from scratch!

Learn more


Apr 4, 2019 at 8:25 am

Thank's a lot u made my day :)

Jessica Chan
Apr 4, 2019 at 9:22 am

Thanks for reading!

Apr 5, 2019 at 2:59 pm

good, no actually 'very good'!

Jessica Chan
Apr 5, 2019 at 4:56 pm

Thanks for reading!

Marcelo Melo
Apr 8, 2019 at 2:10 pm

Hey Jessica,

I'm just passing by to say that you really helped me out!

I was having trouble with my Gulp file (cause I was coming from an old "gulp 3" one), and your article gave me the quickest answer that I needed!

Thank you so much!

Jessica Chan
Apr 9, 2019 at 4:36 pm

Hi Marcelo, I'm so glad that my post helped you out with setting up Gulp! Thanks for your kind words :)

Apr 23, 2019 at 3:57 am

Hello Jessica, i'm disappointed. Sourcemaps, now in Gulp 4, is in core! Not needed gulp-sourcemaps

Jessica Chan
Apr 24, 2019 at 2:14 pm

I actually did try to get the core sourcemaps working but it didn't seem to be compatible with the other plugins, and wasn't generating anything. If you have a working demo I'd be happy to see how you do it!

Feb 10, 2020 at 1:47 am


function sassTask() {
return src(files.sassPath, { sourcemaps: true })
//.pipe(sourcemaps.init()) // initialize sourcemaps first
.pipe(sass().on('error', sass.logError)) // compile SASS to CSS
.pipe(postcss([autoprefixer(), cssnano()])) // PostCSS plugins
//.pipe(sourcemaps.write('.')) // write sourcemaps file in same css dir
.pipe(dest(files.css, { sourcemaps: '.' }))
.pipe(reload({ stream: true }));

David J. Morse
Apr 24, 2019 at 7:32 am

Excellent article and explanation on upgrading to v4. Only 2 things missing here IMO, browsersync (I gotta have) and image minification.

Jessica Chan
Apr 24, 2019 at 2:13 pm

Thanks! Those are definitely good features to add, I'll have to update the article soon :)

Bruce C
Sep 16, 2019 at 1:35 pm

Jessica, great article. I also have a similar issue similar to David Morse's. When running parallel(), where can we add browserSync.reload? This is important to know. I look forward to your updating this article for that as soon as possible.

Jessica Chan
Sep 20, 2019 at 9:10 am

Hi, thanks for the note. I'll work on adding Browsersync to this.

Apr 24, 2019 at 8:24 am

Thank you for this post , fantastic read , and of great help.

Apr 26, 2019 at 3:57 pm

Hello Jessica - thanks for the great article. Am I missing something to where I can have all my scss/* files concatenated into one styles.css ? as when I attempt the above, it compiles per the name of the scss file, leaving me with many css files...

May 8, 2019 at 12:00 pm

This is a fantastic tutorial! I struggled for ages to get the core sourcemaps working with SASS, cssnano etc with no luck - it's just broken. I've now got everything set up and running perfectly. I've added in BrowserSync and image minification. I think adding browserSync into this tutorial would be a great help to many as it can be a little tricky if you are new to the world of Gulp etc :)

Jessica Chan
May 9, 2019 at 6:10 pm

Hi Andy, thanks for your kind words! I've gotten a couple requests for Browsersync, so I'll be updating this article to incorporate it in the future :)

Jules Webb
May 27, 2019 at 11:49 am

Hi Jessica

Thanks for the great tutorial. Really helped me out. I've followed a few pre Gulp 4 tutorials that I couldn't get to work since I am using Gulp 4

I've been reading a lot about dev's moving away from SASS and just using POSTCSS. I'd love to hear your take on that and why you chose to keep using SASS.

Jessica Chan
May 28, 2019 at 10:20 am

Hi Jules, thanks for your question! I'm glad this tutorial helped you.

I haven't used PostCSS a ton, but basically it is an extremely customizable way of handling your frontend workflow, because you can use and even write your own plugins. For example, I am using PostCSS plugins in my Gulp workflow to run autoprefixer and cssnano. That said, there are innumerable ways of compiling your frontend files-- Gulp/Webpack/Grunt are tools that will do it, but other people simply write their own npm scripts using various packages, to do the same thing without those tools.

At the moment I think Sass (and Gulp) is still super useful. PostCSS is just one other way of handling CSS, and you have to customize it yourself. Some people like having more control, but I find it easier to use Sass to write my CSS without having to worry about which plugins to use.

Alem Kahrovic
May 28, 2019 at 6:56 am

Hey Jessica, i downloaded your boilerplate from github and when i type gulp in the command it gives me this error:

Using gulpfile ~/frontend-boilerplate-master/gulpfile.js
Task never defined: default
To list available tasks, try running: gulp --tasks,

so i run gulp --tasks and this is what i get:

Tasks for ~/frontend-boilerplate-master/gulpfile.js
[13:39:46] └─┬
[13:39:46] └─┬
[13:39:46] β”œβ”€β”¬
[13:39:46] β”‚ β”œβ”€β”€ scssTask
[13:39:46] β”‚ └── jsTask
[13:39:46] β”œβ”€β”€ cacheBustTask
[13:39:46] └── watchTask

any idea how to fix this?

Jessica Chan
May 28, 2019 at 10:13 am

Hi Alem, sorry to hear you're having trouble with this. Did you run "npm install" first? Also, what versions of Gulp and Node do you have installed? I'm running Node v10.15.0, Gulp CLI 2.1.0, and Gulp local 4.0.0.

Let me know if you are able to run Gulp successfully.

Jun 17, 2019 at 2:27 pm

I am also having this issue. I'm not sure how to fix it. I'm running:

Node: v12.4.0
Gulp CLI version: 2.2.0
Gulp Local version: 4.0.0

Error is: Task never defined: default

Jun 17, 2019 at 2:33 pm

I found a fix for this issue "Task never defined: default". In the package.json, you should update:

"gulp": "4.0.0"


"gulp": "^4.0.2"

I think the later version of Gulp must have this issue fixed.

Jessica Chan
Jun 17, 2019 at 9:06 pm

Thank you! I've updated the GitHub repo to install newer minor versions of gulp 4, so this shouldn't happen anymore.

May 28, 2019 at 11:07 am

I'm looking for BrowserSync integration as well, specifically on .html files :) Great tutorial though, thanks!

Atul Rawat
May 31, 2019 at 1:25 am

Thanks for getting brief details about gulp v-04 :)

Krishna Prasad
May 31, 2019 at 2:29 am

Alem Kahrovic - Install undertaker by running $ npm i undertaker to resolve the issue. Great tutorial Jessica Chan.

DΓ©sirΓ©e Melusine Mostajo Blanchard
Jun 9, 2019 at 3:49 pm

Amazing explanation! thank you very much!

Jun 17, 2019 at 12:22 pm

i type gulp in the cmd and put: task never defined: default

Jun 25, 2019 at 9:21 am

Hi Jessica,
Thank you for this amazing tutorial.
I have a question in regards to the cache busting function, this function is running as part of the default function and it's not dependant on whether or not there is a js/css update, is there a way to make it as part of the watch? thank you! Shani

Jessica Chan
Jun 30, 2019 at 4:11 pm

Hi Shani, thanks for watching the tutorial! You can add the cacheBust task to the watch task by wrapping the cacheBustTask and the parallel(scssTask, jsTask) in a series() function like this:

function watchTask(){
    watch([files.scssPath, files.jsPath], 
            parallel(scssTask, jsTask),
Lloyd Apter
Jun 26, 2019 at 9:20 am

Very clear, thorough and professional. Perfect!
Thank you Jessica.

Jul 14, 2019 at 10:49 am


I have been trying unsuccessfully to get PurgeCSS to work on Gulp 4.

Can you give me any pointers on this.

Not sure the code that the give on the site is for Gulp 4.

Thank You,

Jessica Chan
Aug 20, 2019 at 9:39 am

Hi, the code is for Gulp 4-- I haven't tried adding PurgeCSS to the Gulp workflow, but it seems possible, from the PurgeCSS website.

delton clark galolo
Oct 6, 2019 at 7:14 am

hi good evening., I just want to ask if why is my css in my src file wont update after I edit the scss file.. but when i look my dest file it's you know what's the cause of this.. im using gulp-sass

Jessica Chan
Nov 25, 2019 at 9:59 pm

Check what the dest() is of your Sass task-- is it in the same folder or different than your Sass files?

Oct 10, 2019 at 5:43 am

Hello Jessica,

Thank you for the great job!
But in my own case, I have 2 html files, including the index.html (in the root) and the other one in template folder. also, I have so many js files arranged in different sub-folders. i.e:
|_app (containing app.js, services.js and controller.js)
|_imgs (containing subfolders of different images files)
|_scripts (containing subfolders of various js files)
|_style(containing style.css, bootstrap.min.css)
|_templates (containing the main html file)
|_index.html file

Please how do I adapt the "Git repo" to work for me?

Jessica Chan
Nov 25, 2019 at 9:57 pm

You can run the Gulp tasks on multiple files, if you include an array in the src() like this: src(['app/*.js', 'scripts/*.js'])

Clint Batte
Nov 13, 2019 at 1:28 pm

Great article. I would like to integrate a Live Reload/BrowserSync option to watch changes to an Express.js application. I integrated your gulp files to update my scss/js and that works great. any thoughts on how to do this?

Jessica Chan
Nov 25, 2019 at 9:46 pm

I'm working on a Gulp course and tutorial on Browsersync, hopefully will be out soon!

Dec 19, 2019 at 2:58 pm

Hi, How can I add babel to this so i can use es6 import/exports?

Dec 30, 2019 at 5:01 am

Hi Gerd, Great Artical, I face one issue gulp, my Folder Structur like
"assets/scss/compnent/layout.scss) . i have import this file in (assest/scss/main.scss) file. once i run a gulp then gulp run perfectly, but i have change in layout.scss file any css, the browser in not catch a css. but i have write any css in main.scss gulp is work fine. could u help me for solve this issue.

Jan 10, 2020 at 9:57 am

I really like this. I watched your video and felt that you delivered the content in a clear and concise way. Your voice definitely was pleasing the ears. That may sound cheesy but it definitely also helps when trying to learn. Better than a lot of the people that have been out there for sometime doing tutorials.

Ok, so I have been developing for sometime but never really felt confident about it. I seem to always go back to the basics because I may not be using js for a few weeks. I might be using PHP. I seem to bounce around like that in my job. It's really frustrating. Plus I do design on top of that. UGH. Sorry for going off on a tangent.

How does one overcome the 'coding' self-esteem issue. Lastly, cant webpack do the same as gulp? Seems like everyones been on the webpack kick. Pretty typical in this industry. Which is another issue in itself.

Nubraj Pudasaini
Jan 13, 2020 at 1:39 am

Seems like the update from gulp 3 to gulp 4 is pretty easy. Thanks
for this article it is so helpful.

Gregory L Bown
Jan 27, 2020 at 7:50 pm

Thank you Jessica, I was blocked by a legacy build from upgrading to the latest LTS version of node. Your code examples were simple and clear, thank you again for taking the time to share.

Feb 10, 2020 at 2:04 am

Good article indeed!

Can I ask you 2 questions?

1. How do we know which task should be serial or the others?
2. How can we use that generated file?

I've some demo for browser-sync here.

# add constant
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;

# Initiate browser-sync in watchTask()
server: {
baseDir: "../dist" // change on your preferred
watch(files.c_html).on('change', reload); // change to your html file.

# Reload brower when files change
# go to the last line in scssTask() before } and add this line

.pipe(reload({ stream: true }));

That's it.

Feb 10, 2020 at 8:12 am

OK, I understand how to use *.map file, we can see it only when browsing and using gulp in the same time.


Write a Comment