# Building a Front-End Build Pipeline with Gulp

> How I set up a front-end workflow that automates Sass compilation, JavaScript bundling, and browser reloading with Gulp.

- Published: 2015-08-02
- Category: Tools & Technologies
- Tags: JavaScript, Gulp
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/building-a-front-end-build-pipeline-with-gulp/
- Language: en-US
- Author: Muhammet Şafak

---
For a while I was writing CSS by hand and linking it directly in HTML, and adding JavaScript files one by one with `<script>` tags. Then I learned Sass, and that introduced a compilation step. Next came the need to concatenate and minify JS files. After manually running a handful of commands on every change became genuinely tedious, I decided to automate the whole thing with **Gulp**.

**Gulp** is a task runner built on Node.js. You describe the work you want done in code, and Gulp watches for file changes and runs your defined tasks automatically.

## Installation

You'll need Node.js and npm installed. Then:

```bash
npm install -g gulp
```

Inside your project directory:

```bash
npm init
npm install --save-dev gulp gulp-sass gulp-concat gulp-uglify browser-sync
```

These commands create a `package.json` file and install the development dependencies.

## Gulpfile.js

All task definitions live in `gulpfile.js` at the project root:

```javascript
var gulp        = require('gulp');
var sass        = require('gulp-sass');
var concat      = require('gulp-concat');
var uglify      = require('gulp-uglify');
var browserSync = require('browser-sync').create();

// Source and destination paths
var paths = {
    scss:   'resources/sass/**/*.scss',
    js:     'resources/js/**/*.js',
    cssDst: 'public/css',
    jsDst:  'public/js'
};

// Sass compilation task
gulp.task('sass', function() {
    return gulp.src('resources/sass/app.scss')
        .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
        .pipe(gulp.dest(paths.cssDst))
        .pipe(browserSync.stream());
});

// JavaScript concatenation and minification task
gulp.task('scripts', function() {
    return gulp.src(paths.js)
        .pipe(concat('app.js'))
        .pipe(uglify())
        .pipe(gulp.dest(paths.jsDst));
});

// Browser sync and file watch task
gulp.task('watch', ['sass', 'scripts'], function() {
    browserSync.init({
        proxy: 'proje.app'
    });

    gulp.watch(paths.scss, ['sass']);
    gulp.watch(paths.js,   ['scripts']).on('change', browserSync.reload);
    gulp.watch('resources/views/**/*.blade.php').on('change', browserSync.reload);
});

// Default task
gulp.task('default', ['watch']);
```

Save this file, type `gulp` in the terminal, and here is what happens:

1. The `sass` and `scripts` tasks run once
2. BrowserSync starts and proxies `proje.app`
3. File watching begins — when a `.scss` file changes, CSS is compiled and the browser reloads the stylesheet in-place; when a `.js` or `.blade.php` file changes, the full page reloads

## Running tasks individually

To compile only Sass:

```bash
gulp sass
```

To compile all assets (for a production build):

```bash
gulp sass scripts
```

Without `watch`, Gulp simply compiles and exits.

## What does BrowserSync give you?

**BrowserSync** spins up a lightweight server in the background and reloads the browser whenever you save a file — or, if only CSS changed, it injects the updated styles without a full page reload. It can also keep multiple devices in sync simultaneously: open the same page on a tablet and a phone, and scrolling on one mirrors on the other.

This turns out to be genuinely useful when working on responsive designs. Instead of resizing a browser window back and forth, you open a real phone and a desktop browser side by side and see both at the same time — which makes tracking down breakpoint issues much faster.

## Why Gulp and not Grunt?

At the time there were two popular task runners: Grunt and Gulp. Grunt is configuration-based; Gulp is code-based. Gulp's pipe model felt more natural to me — the output of one step becomes the input of the next, with no intermediate files written to disk. That makes a noticeable difference in speed, especially when chaining several operations.

Because a Gulpfile is plain JavaScript, you can also use conditionals, loops, and variables freely. Grunt's declarative `initConfig` object becomes hard to manage once it grows.

I did run into one gotcha: `gulp-uglify` was mangling some [ES6](/en/blog/getting-started-with-es6-let-const-and-arrow-functions/) syntax because it was using an older JavaScript engine under the hood. Minifying directly without a transpile step is safe for older browsers, but you have to be careful when using modern syntax. I solved it by wiring in Babel, though that made the Gulpfile start to balloon — which eventually lit the fuse for moving to Webpack.

## A practical tip

Adding tasks to the `scripts` section of `package.json` saves you from memorizing commands:

```json
{
  "scripts": {
    "dev":   "gulp watch",
    "build": "gulp sass scripts"
  }
}
```

You can then run `npm run dev` or `npm run build`. This also means anyone who does not have Gulp installed globally can still use the tasks through `npm run`.

Once you have Gulp configured, seeing the browser update the instant you save a file changes your rhythm. You stop thinking about the compilation step entirely and stay focused on the code.
