If you have been working on ASP.NET projects in the past years, you probably have heard or used quite a few client library management frameworks/tools. For example, Bower, npm, Gulp, Grunt, Webpack, Yarn, Parcel, Libman, etc. Before SPA became popular, the default ASP.NET (or ASP.NET Core) project templates use Bower as default client management tool and you need to bundle or minimize your client scripts or resources using other tools like Bundler & Minifier. This approach worked quite well for me except that:

  • I still use custom scripts to copy client resources, for example font files, to publish folder (wwwroot).
  • Bower doesn’t support bundle or minimisation. The project also is currently being maintained and it recommends using other frameworks/tools to manage client libs.

In this post, I’m going to show you how to migrate your projects from Bower to Gulp (with npm).


Gulp run on top of nodejs. Please make sure you have nodejs installed in your system.

Existing project

Client packages/libs

The following packages are used by my project (this website) as configured in bower.json file:

  "name": "kontext-v3.0.0-alpha.6",
  "private": true,
  "dependencies": {
    "bootstrap": "v4.3.1",
    "jquery": "3.4.0",
    "jquery-validation": "jquery-validate#1.19.0",
    "jquery-validation-unobtrusive": "v3.2.9",
    "popper.js": "v1.15.0",
    "font-awesome": "4.7.0",
    "Stickyfill": "2.1.0",
    "summernote": "v0.8.9"


The following bundling configurations are created:

// Configure bundling and minification for the project.
// More info at
    "outputFileName": "wwwroot/css/site.min.css",
    // An array of relative input file paths. Globbing patterns supported
    "inputFiles": [
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
    // Optionally specify minification options
    "minify": {
      "enabled": true,
      "renameLocals": true
    // Optionally generate .map file
    "sourceMap": false

Now let’s begin to migrate from Bower to Gulp.

Add npm configuration file package.json

First, add a npm Configuration File package.json.


And then add the dependencies for all the current used packages.

Please note some package name is different in npm compared with Bower. For example, Stickyfill is named stickyfilljs in npm instead of Stickyfill. There might be small changes to other packages too. You can go to each package’s official website to find out the package names.

  "version": "3.0.0",
  "name": "kontext",
  "private": true,
  "devDependencies": {
    "gulp": "^4.0.0",
    "gulp-concat": "2.6.1",
    "gulp-cssmin": "0.2.0",
    "gulp-uglify": "3.0.0",
    "rimraf": "2.6.1"
  "dependencies": {
    "bootstrap": "4.3.1",
    "jquery": "3.4.0",
    "jquery-validation": "1.19.0",
    "jquery-validation-unobtrusive": "3.2.9",
    "popper.js": "1.15.0",
    "font-awesome": "4.7.0",
    "stickyfilljs": "2.1.0",
    "summernote": "0.8.9"

I also added devDependencies for Gulp related packages which I’m going to use in the following sections.

After creating this package file, you can then run the following command to install the packages if it is not automatically done:

npm install

Create Gulp script file

Now we can create a JavaScript file named Gulpfile.js in your website project folder.

I’ve created the following JavaScript content to include the tasks I need to bundle and minimize client scripts.

"use strict";

const gulp = require("gulp"),
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

const paths = {
    webroot: "./wwwroot/",
    node_modules: "./node_modules/"

/*CSS files */
paths.cssFiles = [
    paths.webroot + "css/_imports.css",
    paths.node_modules + "bootstrap/dist/css/bootstrap.css",
    paths.node_modules + "font-awesome/css/font-awesome.css",
    paths.node_modules + "summernote/dist/summernote-bs4.css",
    paths.webroot + "css/site.css"
paths.concatCssDest = paths.webroot + "css/site.min.css";

/*Javascript files */
paths.jsFiles = [
    paths.node_modules + "jquery/dist/jquery.js",
    paths.node_modules + "popper.js/dist/umd/popper.js",
    paths.node_modules + "stickyfilljs/dist/stickyfill.js",
    paths.node_modules + "bootstrap/dist/js/bootstrap.js",
    paths.node_modules + "jquery-validation/dist/jquery.validate.js",
    paths.node_modules + "jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.js",
    paths.webroot + "js/site.js"];
paths.concatJsDest = paths.webroot + "js/site.min.js";

/*Editor js files */
paths.jsFileEditor = [paths.node_modules + "summernote/dist/summernote-bs4.js"];
paths.concatJsDestEditor = paths.webroot + "js/site.editor.min.js";

/*Clean tasks*/
gulp.task("clean:js", done => rimraf(paths.concatJsDest, done));
gulp.task("clean:js:editor", done => rimraf(paths.concatJsDestEditor, done));
gulp.task("clean:css", done => rimraf(paths.concatCssDest, done));
gulp.task("clean:fonts:fontawesome", done => rimraf(paths.webroot + "fonts/", done));
gulp.task("clean:fonts:summernote", done => rimraf(paths.webroot + "css/font/", done));
gulp.task("clean", gulp.series(["clean:js", "clean:js:editor", "clean:css", "clean:fonts:fontawesome", "clean:fonts:summernote"]));

gulp.task("min:css", () => {
    return gulp.src(paths.cssFiles, { base: "." })

gulp.task("min:js", () => {
    return gulp.src(paths.jsFiles, { base: "." })

gulp.task("min:js:editor", () => {
    return gulp.src(paths.jsFileEditor, { base: "." })

gulp.task("copy:font:fontawesome", () => {
    return gulp.src(paths.node_modules + "font-awesome/fonts/*")
        .pipe(gulp.dest(paths.webroot + "fonts/"));

gulp.task("copy:font:summernote", () => {
    return gulp.src(paths.node_modules + "summernote/dist/font/*")
        .pipe(gulp.dest(paths.webroot + "css/font/"));

gulp.task("min", gulp.series(["min:js", "min:js:editor", "min:css"]));

gulp.task("build", gulp.series(["min", "copy:font:fontawesome", "copy:font:summernote"]));

// A 'default' task is required by Gulp v4
gulp.task("default", gulp.series(["min"]));

In this post, I am not going to cover the details about Gulp. If you want to learn more, please visit Gulp official website.

Basically the following Gulp tasks are created:

  • Tasks to clean existing bundled files (CSS and JavaScript) and fonts
  • Tasks to minimize CSS files into one single CSS file.
  • Tasks to bundle and ugnify JavaScript files.
  • Tasks to run multiple serial tasks.

Once these tasks are created, you can view all the tasks in Task Runner Explorer:


During development time, you can run each task by right-click the task in Task Runner Explorer or through nodejs command:

node node_modules\\gulp\\bin\\gulp.js {task name}

Gulp glob

In the above Gulp script file, I’m using script file names to make it easier for you to understand. However, usually you can use Gulp globs to match different files in one go.

For more details, refer to Explaining Globs.

Add nodejs commands into project file

<Target Name="PrepublishScript" BeforeTargets="BeforePublish">
     <Exec Command="npm install" />
     <Exec Command="node node_modules\\gulp\\bin\\gulp.js clean" />
     <Exec Command="node node_modules\\gulp\\bin\\gulp.js build" />

You can now add the above code to your website project file so that npm and Gulp commands are called before publishing the website.

What about development environments?

In your project, you might be directly using these CSS or script files like the following:

<environment include="Development">
     <link rel="stylesheet" href="~/css/_imports.css" />
     <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
     <link rel="stylesheet" href="~/css/site.css" />
     <link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.css" />
     <link rel="stylesheet" href="~/lib/summernote/dist/summernote-bs4.css" />
<environment exclude="Development">
     <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />

Now because these libraries are not existing in your wwwroot folder (instead in node_modules filder), the code won’t work anymore.

To make sure it still works, we can just add middleware to route those requests to node_modules folder:

if (env.IsDevelopment())
                     app.UseStaticFiles(new StaticFileOptions()
                         FileProvider = new PhysicalFileProvider(
                           Path.Combine(Directory.GetCurrentDirectory(), @"node_modules")),
                         RequestPath = new PathString("/lib")


Now you will have a very clean wwwroot folder in your ASP.NET core websites. Gulp doesn’t only support bundle and minimisation but also can copy other resources like fonts into your publish folder.

You can also use Webpack (also based on nodejs) to implement similar tasks. In fact, ASP.NET SPA project template by default uses Webpack as client package management tool.

info Last modified by Raymond at 2 years ago * This page is subject to Site terms.

More from Kontext

Modern Web Application - Azure Blob Storage for Uploaded Files

local_offer Azure local_offer core local_offer dotnetcore

visibility 73
thumb_up 0
access_time 2 months ago

With cloud platforms like Azure, we can totally separate user content storage from web application storage to decouple components from each other and to make the application easy to scale and deploy. This article provides detailed information with code snippets about how to use Azure server-less product Blob Storage and App Service to enable horizontally scalable web application for users to upload files (BLOBs).

open_in_new View open_in_new Azure

local_offer core 2 local_offer core local_offer dotnetcore local_offer open-banking

visibility 228
thumb_up 0
access_time 2 years ago

I’ve just started an core 2.2 based implementation for Australia Consumer Data Standards (published by Data 61). Opening Banking initiative will follow these standards. The purpose is to help you to get familiar with these standards, especially the APIs that need to be implemented. ...

open_in_new View open_in_new ASP.NET Core

local_offer core local_offer identity core 2

visibility 18508
thumb_up 0
access_time 3 years ago

The identity system in ASP.NET has evolved over time. If you are using ASP.NET Core, you probably found User property is an instance of ClaimsPrincipal in Controller or Razor views. Thus to retrieve the information, you need to utilize the claims.

open_in_new View open_in_new ASP.NET Core

local_offer dotnet core local_offer core

visibility 4643
thumb_up 0
access_time 3 years ago

IP Address In ASP.NET Core, Request.UserHostAddress has been removed though that attribute exists in the traditional ASP.NET applications. We can use HttpContext.Connection to retrieve the remove client IP address: var ipAddress = HttpContext.Connecti...

open_in_new View open_in_new ASP.NET Core

info About author

Dark theme mode

Dark theme mode is available on Kontext.

Learn more arrow_forward

Kontext Column

Created for everyone to publish data, programming and cloud related articles. Follow three steps to create your columns.

Learn more arrow_forward