Skip to content
Muhammet Şafak
tr
Tools & Technologies 4 min read

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.


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:

npm install -g gulp

Inside your project directory:

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:

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:

gulp sass

To compile all assets (for a production build):

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 YAML-like config becomes hard to manage once it grows.

I did run into one gotcha: gulp-uglify was mangling some ES6 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:

{
  "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.

Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

Related Posts

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind