How to Use Gulp.js to Minify CSS, JS and Compile Less/SCSS

Blog / JavaScript · June 12, 2014 · Updated June 10, 2026 · 7 min read
How to Use Gulp.js to Minify CSS, JS and Compile Less/SCSS

To use Gulp.js to minify CSS and JavaScript and compile Less or SCSS, install Gulp 5 plus a handful of plugins — gulp-terser for JavaScript, gulp-sass (running dart-sass) or gulp-less for stylesheets, and PostCSS with autoprefixer and cssnano for CSS — then wire them into tasks in a gulpfile.js and run them with gulp.series and gulp.parallel. That workflow still works in 2026.

The honest caveat first: for new projects, Vite and esbuild have largely replaced Gulp. Reach for Gulp mainly on legacy pipelines, WordPress/theme builds, or simple file-to-file automation — not as the build tool for a fresh React or Svelte app.

Key takeaways

  • Gulp 5 (released 2024) still works on Node 18+ and is great for simple, file-based task automation.
  • Use gulp-terser, not gulp-uglify — uglify only parses ES5 and chokes on modern JavaScript.
  • Compile styles with dart-sass via gulp-sass (node-sass is dead) or with gulp-less.
  • Minify and prefix CSS with PostCSS + autoprefixer + cssnano.
  • For new SPAs or SSR apps, prefer Vite or esbuild; keep Gulp for legacy and theme pipelines.

Is Gulp.js still worth using in 2026?

Gulp is a streaming task runner, not a bundler. It reads files with gulp.src(), pipes them through transforms (minify, compile, rename), and writes them out with gulp.dest(). That model is unchanged since the early days — but the ecosystem around it has moved on.

Here is the honest 2026 picture:

  • Gulp 5 shipped in 2024. It is maintained, requires Node 18+, switched to the modern glob package, and added native ES-module support. It is not abandoned, but it is niche.
  • For bundling application code, Vite (dev server plus Rollup builds) and esbuild are dramatically faster and need far less configuration. Framework toolchains — SvelteKit, Next.js, Nuxt, Astro — already ship a bundler, so you rarely add Gulp on top.
  • Gulp still earns its place for non-bundler chores: optimizing images, generating icon sprites or static files, processing many standalone CSS/JS files, and WordPress or legacy theme asset pipelines where there is no module graph to bundle.
  • Gulp has no hot module replacement and no module-graph awareness. If you want instant HMR for a component-driven app, that is a job for Vite, not Gulp.

In short: do not migrate a working Gulp pipeline just for fashion, but do not start a new SPA with Gulp either.

Gulp vs webpack vs Vite vs esbuild vs npm scripts

All five can produce minified CSS and JS, but they solve different problems. Use this to pick the right tool before writing a single task.

Tool Build speed Config style Dev server / HMR Best for in 2026
Gulp 5 Moderate Imperative JS tasks Only via browser-sync (manual) File-pipeline automation, WordPress/theme builds, legacy assets
webpack 5 Slower Declarative, verbose config Yes (webpack-dev-server) Complex apps with heavy module graphs, micro-frontends
Vite 6+ Very fast Minimal config Yes (native ESM HMR) New SPAs and SSR apps (React, Vue, Svelte)
esbuild Fastest Small API / CLI No (build only) Ultra-fast transpiling and bundling; powers other tools
npm scripts Depends on tools package.json one-liners Tool-dependent Tiny projects, gluing a few CLIs together

If your task is "compile and minify a pile of files," Gulp or npm scripts are fine. If it is "build a modern component app with fast feedback," choose Vite.

How do you set up Gulp 5?

You need Node 18 or newer. Install the Gulp command-line tool once globally, then install Gulp and its plugins locally in each project so versions stay pinned per project.

A few 2026 plugin choices matter:

  • gulp-terser over gulp-uglify. gulp-uglify wraps UglifyJS, which only parses ES5 — it throws on arrow functions, const/let, template literals, and optional chaining. gulp-terser wraps Terser and minifies modern ES2015+ correctly.
  • dart-sass, not node-sass. Install the sass package and hand it to gulp-sass explicitly. node-sass is deprecated and unmaintained.
  • PostCSS for CSS post-processing. autoprefixer adds vendor prefixes from your browserslist, and cssnano minifies the result.

Install everything in one go:

# Node 18+ is required for Gulp 5.
# Install the Gulp CLI once, globally:
npm install --global gulp-cli

# Then install Gulp 5 and the plugins locally in your project:
npm install --save-dev gulp@5 gulp-terser gulp-concat \
  gulp-sass sass gulp-less \
  gulp-postcss autoprefixer cssnano \
  browser-sync

A complete, modern gulpfile.js

Two things changed since older tutorials. First, Gulp 4 removed the array task syntaxgulp.task('default', ['a', 'b']) no longer works. You now export plain functions and compose them with gulp.series() (run in order) and gulp.parallel() (run concurrently). Second, sourcemaps are built in: pass { sourcemaps: true } to src() and { sourcemaps: '.' } to dest(), so you no longer need gulp-sourcemaps.

Here is a full gulpfile.js that concatenates and minifies JavaScript, compiles both SCSS and Less, autoprefixes and minifies the CSS, and serves everything with live reload:

// gulpfile.js — Gulp 5 (CommonJS)

const { src, dest, watch, series, parallel } = require('gulp');

const terser = require('gulp-terser'); // modern JS minifier (ES2015+); replaces gulp-uglify
const concat = require('gulp-concat');

const sass = require('gulp-sass')(require('sass')); // dart-sass, not the dead node-sass
const less = require('gulp-less');

const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');

const browserSync = require('browser-sync').create();

const paths = {
  scripts: 'src/js/**/*.js',
  scss: 'src/scss/**/*.scss',
  less: 'src/less/**/*.less',
  html: 'dist/**/*.html',
};

// Concatenate + minify JavaScript (sourcemaps via Gulp's built-in option)
function scripts() {
  return src(paths.scripts, { sourcemaps: true })
    .pipe(concat('bundle.js'))
    .pipe(terser())
    .pipe(dest('dist/js', { sourcemaps: '.' }))
    .pipe(browserSync.stream());
}

// Compile SCSS -> autoprefix -> minify
function styles() {
  return src(paths.scss, { sourcemaps: true })
    .pipe(sass().on('error', sass.logError))
    .pipe(postcss([autoprefixer(), cssnano()]))
    .pipe(dest('dist/css', { sourcemaps: '.' }))
    .pipe(browserSync.stream());
}

// Compile Less -> autoprefix -> minify
function lessStyles() {
  return src(paths.less, { sourcemaps: true })
    .pipe(less())
    .pipe(postcss([autoprefixer(), cssnano()]))
    .pipe(dest('dist/css', { sourcemaps: '.' }));
}

// Live-reload dev server with file watching
function serve() {
  browserSync.init({ server: { baseDir: './dist' } });
  watch(paths.scss, styles);
  watch(paths.less, lessStyles);
  watch(paths.scripts, scripts);
  watch(paths.html).on('change', browserSync.reload);
}

const build = parallel(styles, lessStyles, scripts);

exports.scripts = scripts;
exports.styles = styles;
exports.less = lessStyles;
exports.serve = series(build, serve);
exports.default = build;

How do you run and watch Gulp tasks?

With the functions exported above you get a default build plus named tasks and a live-reload dev server. Run them with the Gulp CLI, or with npx if you skipped the global install:

# Run the default build (styles, less, and scripts in parallel):
npx gulp

# Run a single named task:
npx gulp scripts

# Start the browser-sync dev server with file watching + live reload:
npx gulp serve

When should you still choose Gulp?

Pick Gulp when the work is task automation over files, not application bundling:

  • You maintain a WordPress, Shopify, or legacy theme that ships plain CSS/JS assets.
  • You need to batch-process files — image compression, SVG sprites, generating static config — where a module bundler adds nothing.
  • You already have a working Gulp pipeline that is stable; rewriting it to Vite buys little.

Pick Vite or esbuild instead when you are building a component-driven SPA or SSR app and want fast HMR and an optimized production bundle. If you are weighing bundling approaches, our walkthrough of Django Webpacker for bundling CSS and JS covers a server-side alternative, and minifying and prefixing assets remains one of the cheapest wins for improving your Google PageSpeed score.

At MicroPyramid we build and modernize web front ends with whatever fits — Gulp for legacy asset pipelines, and Vite, esbuild, or the framework's own bundler for new React and SPA work.

Frequently Asked Questions

Is Gulp still relevant in 2026?

Yes, but narrowly. Gulp 5 (released 2024) is still maintained and runs on Node 18+, and it remains a solid choice for file-pipeline automation, WordPress/theme builds, and legacy projects. For new single-page or server-rendered apps, Vite and esbuild are faster and need far less configuration.

Should I use gulp-uglify or gulp-terser?

Use gulp-terser. gulp-uglify wraps UglifyJS, which only understands ES5 and throws on modern syntax like arrow functions, const/let, and optional chaining. gulp-terser wraps Terser, which minifies ES2015+ correctly and is the effective drop-in replacement.

What replaced node-sass for compiling SCSS in Gulp?

dart-sass, shipped as the sass npm package. node-sass is deprecated and no longer updated. With current gulp-sass you pass the compiler explicitly — require('gulp-sass')(require('sass')) — which uses dart-sass and supports the latest Sass module syntax (@use and @forward).

Should I use Gulp or Vite for a new project?

For a new front-end app, choose Vite. It gives instant startup, native ES-module hot module replacement, and an optimized production build with almost no config. Gulp has no module graph or HMR; it pipes files through transforms. Pick Gulp only when you mainly need task automation rather than bundling.

How do I minify CSS with Gulp?

Pipe your compiled CSS through PostCSS with cssnano: .pipe(postcss([autoprefixer(), cssnano()])). cssnano handles minification — whitespace removal, deduplication, value compaction — while autoprefixer adds vendor prefixes based on your browserslist. This works the same after either a Sass or a Less compile step.

Can I write my gulpfile in ES modules?

Yes. Gulp 5 supports ESM gulpfiles — name the file gulpfile.mjs, or set "type": "module" in package.json, then use import/export instead of require/exports. The task functions and the series/parallel API are identical; only the import syntax changes.

Share this article