[c#] How to use npm with ASP.NET Core

I'm using npm to manage the jQuery, Bootstrap, Font Awesome and similar client libraries I need for my ASP.NET Core application.

The approach that worked for me started by adding a package.json file to the project, that looks like this:

{
    "version": "1.0.0",
    "name": "myapp",
    "private": true,
    "devDependencies": {
  },
  "dependencies": {
    "bootstrap": "^3.3.6",
    "font-awesome": "^4.6.1",
    "jquery": "^2.2.3"
  }
}

npm restores these packages into the node_modules folder which is on the same level as wwwroot in the project directory:

enter image description here

As ASP.NET Core serves the static files from the wwwroot folder, and node_modules is not there, I had to make a couple of changes to make this work, the first one: adding app.UseFileServer right before app.UseStaticFiles in my Startup.cs file:

app.UseFileServer(new FileServerOptions()
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(Directory.GetCurrentDirectory(), @"node_modules")), 
    RequestPath = new PathString("/node_modules"),
    EnableDirectoryBrowsing = true
});

app.UseStaticFiles();

and the second one, including node_modules in my publishOptions in the project.json file:

"publishOptions": {
  "include": [
    "web.config",
    "wwwroot",
    "Views",
    "node_modules"
  ]
},

This works in my development environment and it also works when I deploy it to my Azure App Service instance, the jquery, bootstrap and font-awesome static files get served well, but I'm not sure about this implementation.

What is the right approach for doing this?

This solution came after collecting lots of bits of info from several sources and trying some that didn't work, and it seems a bit odd having to serve these files from outside wwwroot.

Any advice will be greatly appreciated.

This question is related to c# asp.net-mvc npm gulp asp.net-core

The answer is


Shawn Wildermuth has a nice guide here: https://wildermuth.com/2017/11/19/ASP-NET-Core-2-0-and-the-End-of-Bower

The article links to the gulpfile on GitHub where he's implemented the strategy in the article. You could just copy and paste most of the gulpfile contents into yours, but be sure to add the appropriate packages in package.json under devDependencies: gulp gulp-uglify gulp-concat rimraf merge-stream


enter image description here

  • Using npm for managing client-side libraries is a good choice (as opposed to Bower or NuGet), you're thinking in the right direction :)
  • Split server-side (ASP.NET Core) and client-side (e.g. Angular 2, Ember, React) projects into separate folders (otherwise your ASP.NET project may have lots of noise - unit tests for the client-side code, node_modules folder, build artifacts, etc.). Front-end developers working in the same team with you will thank you for that :)
  • Restore npm modules at the solution level (similarly how you restore packages via NuGet - not into the project's folder), this way you can have unit and integration tests in a separate folder as well (as opposed to having client-side JavaScript tests inside your ASP.NET Core project).
  • Use might not need FileServer, having StaticFiles should suffice for serving static files (.js, images, etc.)
  • Use Webpack to bundle your client-side code into one or more chunks (bundles)
  • You might not need Gulp/Grunt if you're using a module bundler such as Webpack
  • Write build automation scripts in ES2015+ JavaScript (as opposed to Bash or PowerShell), they will work cross-platform and be more accessible to a variety of web developers (everyone speaks JavaScript nowadays)
  • Rename wwwroot to public, otherwise the folder structure in Azure Web Apps will be confusing (D:\Home\site\wwwroot\wwwroot vs D:\Home\site\wwwroot\public)
  • Publish only the compiled output to Azure Web Apps (you should never push node_modules to a web hosting server). See tools/deploy.js as an example.

Visit ASP.NET Core Starter Kit on GitHub (disclaimer: I'm the author)


Install the Bundler and Minifier into Visual Studio Extensions

Then you create a bundleconfig.json and enter the following like :

// Configure bundling and minification for the project.
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
[
 {
    "outputFileName": "wwwroot/js/jquery.min.js",
    "inputFiles": [
      "node_modules/jquery/dist/jquery.js"
    ],
    // Optionally specify minification options
    "minify": {
      "enabled": true,
      "renameLocals": false
    },
    // Optionally generate .map file
    "sourceMap": false
  }
]

So the bundler and minifier (gulp based) has access to the source files (which should be excluded from Visual Studio and also excluded from GIT) and puts them into the wwwroot as specified

only side effect every time you save it will run this (but you can set it to run it manually)


I give you two answers. npm combined with other tools is powerful but requires some work to setup. If you just want to download some libraries, you might want to use Library Manager instead (released in Visual Studio 15.8).

NPM (Advanced)

First add package.json in the root of you project. Add the following content:

{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "devDependencies": {
    "gulp": "3.9.1",
    "del": "3.0.0"
  },
  "dependencies": {
    "jquery": "3.3.1",
    "jquery-validation": "1.17.0",
    "jquery-validation-unobtrusive": "3.2.10",
    "bootstrap": "3.3.7"
  }
}

This will make NPM download Bootstrap, JQuery and other libraries that is used in a new asp.net core project to a folder named node_modules. Next step is to copy the files to an appropriate place. To do this we will use gulp, which also was downloaded by NPM. Then add a new file in the root of you project named gulpfile.js. Add the following content:

/// <binding AfterBuild='default' Clean='clean' />
/*
This file is the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');
var del = require('del');

var nodeRoot = './node_modules/';
var targetPath = './wwwroot/lib/';

gulp.task('clean', function () {
    return del([targetPath + '/**/*']);
});

gulp.task('default', function () {
    gulp.src(nodeRoot + "bootstrap/dist/js/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/js"));
    gulp.src(nodeRoot + "bootstrap/dist/css/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/css"));
    gulp.src(nodeRoot + "bootstrap/dist/fonts/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/fonts"));

    gulp.src(nodeRoot + "jquery/dist/jquery.js").pipe(gulp.dest(targetPath + "/jquery/dist"));
    gulp.src(nodeRoot + "jquery/dist/jquery.min.js").pipe(gulp.dest(targetPath + "/jquery/dist"));
    gulp.src(nodeRoot + "jquery/dist/jquery.min.map").pipe(gulp.dest(targetPath + "/jquery/dist"));

    gulp.src(nodeRoot + "jquery-validation/dist/*.js").pipe(gulp.dest(targetPath + "/jquery-validation/dist"));

    gulp.src(nodeRoot + "jquery-validation-unobtrusive/dist/*.js").pipe(gulp.dest(targetPath + "/jquery-validation-unobtrusive"));
});

This file contains a JavaScript code that is executed when the project is build and cleaned. It’s will copy all necessary files to lib2 (not lib – you can easily change this). I have used the same structure as in a new project, but it’s easy to change files to a different location. If you move the files, make sure you also update _Layout.cshtml. Note that all files in the lib2-directory will be removed when the project is cleaned.

If you right click on gulpfile.js, you can select Task Runner Explorer. From here you can run gulp manually to copy or clean files.

Gulp could also be useful for other tasks like minify JavaScript and CSS-files:

https://docs.microsoft.com/en-us/aspnet/core/client-side/using-gulp?view=aspnetcore-2.1

Library Manager (Simple)

Right click on you project and select Manage client side-libraries. The file libman.json is now open. In this file you specify which library and files to use and where they should be stored locally. Really simple! The following file copies the default libraries that is used when creating a new ASP.NET Core 2.1 project:

{
  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
    {
      "library": "[email protected]",
      "files": [ "jquery.js", "jquery.min.map", "jquery.min.js" ],
      "destination": "wwwroot/lib/jquery/dist/"
    },
    {
      "library": "[email protected]",
      "files": [ "additional-methods.js", "additional-methods.min.js", "jquery.validate.js", "jquery.validate.min.js" ],
      "destination": "wwwroot/lib/jquery-validation/dist/"
    },
    {
      "library": "[email protected]",
      "files": [ "jquery.validate.unobtrusive.js", "jquery.validate.unobtrusive.min.js" ],
      "destination": "wwwroot/lib/jquery-validation-unobtrusive/"
    },
    {
      "library": "[email protected]",
      "files": [
        "css/bootstrap.css",
        "css/bootstrap.css.map",
        "css/bootstrap.min.css",
        "css/bootstrap.min.css.map",
        "css/bootstrap-theme.css",
        "css/bootstrap-theme.css.map",
        "css/bootstrap-theme.min.css",
        "css/bootstrap-theme.min.css.map",
        "fonts/glyphicons-halflings-regular.eot",
        "fonts/glyphicons-halflings-regular.svg",
        "fonts/glyphicons-halflings-regular.ttf",
        "fonts/glyphicons-halflings-regular.woff",
        "fonts/glyphicons-halflings-regular.woff2",
        "js/bootstrap.js",
        "js/bootstrap.min.js",
        "js/npm.js"
      ],
      "destination": "wwwroot/lib/bootstrap/dist"
    },
    {
      "library": "[email protected]",
      "files": [ "list.js", "list.min.js" ],
      "destination": "wwwroot/lib/listjs"
    }
  ]
}

If you move the files, make sure you also update _Layout.cshtml.


Much simpler approach is to use OdeToCode.UseNodeModules Nuget package. I just tested it with .Net Core 3.0. All you need to do is add the package to the solution and reference it in the Configure method of the Startup class:

app.UseNodeModules();

I learned about it from the excellent Building a Web App with ASP.NET Core, MVC, Entity Framework Core, Bootstrap, and Angular Pluralsight course by Shawn Wildermuth.


I've found a better way how to manage JS packages in my project with NPM Gulp/Grunt task runners. I don't like the idea to have a NPM with another layer of javascript library to handle the "automation", and my number one requirement is to simple run the npm update without any other worries about to if I need to run gulp stuff, if it successfully copied everything and vice versa.

The NPM way:

  • The JS minifier is already bundled in the ASP.net core, look for bundleconfig.json so this is not an issue for me (not compiling something custom)
  • The good thing about NPM is that is have a good file structure so I can always find the pre-compiled/minified versions of the dependencies under the node_modules/module/dist
  • I'm using an NPM node_modules/.hooks/{eventname} script which is handling the copy/update/delete of the Project/wwwroot/lib/module/dist/.js files, you can find the documentation here https://docs.npmjs.com/misc/scripts (I'll update the script that I'm using to git once it'll be more polished) I don't need additional task runners (.js tools which I don't like) what keeps my project clean and simple.

The python way:

https://pypi.python.org/pyp... but in this case you need to maintain the sources manually


What is the right approach for doing this?

There are a lot of "right" approaches, you just have decide which one best suites your needs. It appears as though you're misunderstanding how to use node_modules...

If you're familiar with NuGet you should think of npm as its client-side counterpart. Where the node_modules directory is like the bin directory for NuGet. The idea is that this directory is just a common location for storing packages, in my opinion it is better to take a dependency on the packages you need as you have done in the package.json. Then use a task runner like Gulp for example to copy the files you need into your desired wwwroot location.

I wrote a blog post about this back in January that details npm, Gulp and a whole bunch of other details that are still relevant today. Additionally, someone called attention to my SO question I asked and ultimately answered myself here, which is probably helpful.

I created a Gist that shows the gulpfile.js as an example.

In your Startup.cs it is still important to use static files:

app.UseStaticFiles();

This will ensure that your application can access what it needs.


Instead of trying to serve the node modules folder, you can also use Gulp to copy what you need to wwwroot.

https://docs.asp.net/en/latest/client-side/using-gulp.html

This might help too

Visual Studio 2015 ASP.NET 5, Gulp task not copying files from node_modules


Please excuse the length of this post.

This is a working example using ASP.NET Core version 2.5.

Something of note is that the project.json is obsolete (see here) in favor of .csproj. An issue with .csproj. file is the large amount of features and the fact there is no central location for its documentation (see here).

One more thing, this example is running ASP.NET core in a Docker Linux (alpine 3.9) container; so the paths will reflect that. It also uses gulp ^4.0. However, with some modification, it should work with older versions of ASP.NET Core, Gulp, NodeJS, and also without Docker.

But here's an answer:

gulpfile.js see the real working exmple here

// ROOT and OUT_DIR are defined in the file above. The OUT_DIR value comes from .NET Core when ASP.net us built.
const paths = {
    styles: {
        src: `${ROOT}/scss/**/*.scss`,
        dest: `${OUT_DIR}/css`
    },
    bootstrap: {
        src: [
            `${ROOT}/node_modules/bootstrap/dist/css/bootstrap.min.css`,
            `${ROOT}/node_modules/startbootstrap-creative/css/creative.min.css`
        ],
        dest: `${OUT_DIR}/css`
    },
    fonts: {// enter correct paths for font-awsome here.
        src: [
            `${ROOT}/node_modules/fontawesome/...`,
        ],
        dest: `${OUT_DIR}/fonts`
    },
    js: {
        src: `${ROOT}/js/**/*.js`,
        dest: `${OUT_DIR}/js`
    },
    vendorJs: {
        src: [
            `${ROOT}/node_modules/jquery/dist/jquery.min.js`
            `${ROOT}/node_modules/bootstrap/dist/js/bootstrap.min.js`
        ],
        dest: `${OUT_DIR}/js`
    }
};

// Copy files from node_modules folder to the OUT_DIR.
let fonts = () => {
    return gulp
        .src(paths.styles.src)
        .pipe(gulp.dest(paths.styles.dest));
};

// This compiles all the vendor JS files into one, jsut remove the concat to keep them seperate.
let vendorJs = () => {
    return gulp
        .src(paths.vendorJs.src)
        .pipe(concat('vendor.js'))
        .pipe(gulp.dest(paths.vendorJs.dest));
}

// Build vendorJs before my other files, then build all other files in parallel to save time.
let build = gulp.series(vendorJs, gulp.parallel(js, styles, bootstrap));

module.exports = {// Only add what we intend to use externally.
    default: build,
    watch
};

Add a Target in .csproj file. Notice we also added a Watch to watch and exclude if we take advantage of dotnet run watch command.

app.csprod

  <ItemGroup>
    <Watch Include="gulpfile.js;js/**/*.js;scss/**/*.scss" Exclude="node_modules/**/*;bin/**/*;obj/**/*" />
  </ItemGroup>

  <Target Name="BuildFrontend" BeforeTargets="Build">
    <Exec Command="yarn install" />
    <Exec Command="yarn run build -o $(OutputPath)" />
  </Target>

Now when dotnet run build is run it will also install and build node modules.


Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to asp.net-mvc

Using Lato fonts in my css (@font-face) Better solution without exluding fields from Binding Vue.js get selected option on @change You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to send json data in POST request using C# VS 2017 Metadata file '.dll could not be found The default XML namespace of the project must be the MSBuild XML namespace How to create roles in ASP.NET Core and assign them to users? The model item passed into the dictionary is of type .. but this dictionary requires a model item of type How to use npm with ASP.NET Core

Examples related to npm

What does 'x packages are looking for funding' mean when running `npm install`? error: This is probably not a problem with npm. There is likely additional logging output above Module not found: Error: Can't resolve 'core-js/es6' Browserslist: caniuse-lite is outdated. Please run next command `npm update caniuse-lite browserslist` ERROR in The Angular Compiler requires TypeScript >=3.1.1 and <3.2.0 but 3.2.1 was found instead DeprecationWarning: Buffer() is deprecated due to security and usability issues when I move my script to another server Please run `npm cache clean` What exactly is the 'react-scripts start' command? On npm install: Unhandled rejection Error: EACCES: permission denied Difference between npx and npm?

Examples related to gulp

How to fix ReferenceError: primordials is not defined in node Everytime I run gulp anything, I get a assertion error. - Task function must be specified Stylesheet not loaded because of MIME-type Node update a specific package 'gulp' is not recognized as an internal or external command How to watch and reload ts-node when TypeScript files change How to use npm with ASP.NET Core Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style Gulp error: The following tasks did not complete: Did you forget to signal async completion? NPM vs. Bower vs. Browserify vs. Gulp vs. Grunt vs. Webpack

Examples related to asp.net-core

dotnet ef not found in .NET Core 3 How to use Bootstrap 4 in ASP.NET Core ASP.NET Core - Swashbuckle not creating swagger.json file Getting value from appsettings.json in .net core .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Automatically set appsettings.json for dev and release environments in asp.net core? Get ConnectionString from appsettings.json instead of being hardcoded in .NET Core 2.0 App Unable to create migrations after upgrading to ASP.NET Core 2.0 EF Core add-migration Build Failed ASP.NET Core form POST results in a HTTP 415 Unsupported Media Type response