An alternative way to build and bundle Javascript, CSS in ASP.NET Core MVC and Razor Page projects

This article shows how Javascript packages, files, CSS files could be built and bundled in an ASP.NET Core MVC or Razor Page application. The Javascript packages are loaded using npm in which most Javascript projects are deployed. No CDNs are used, only local files so that all external URLs, non self URL links can be completely blocked. This can help in reducing security risks in your application. By using npm, it makes it really simple to update and maintain the UI packages.


Npm is used to manage the frontend packages. To use this, download Node.js and install. Npm will then be ready to use.

The npm package.json contains the packages required for the ASP.NET Core application. For most applications, I use Bootstrap 4. jQuery is used in all of my ASP.NET Core MVC, Razor Page projects as this is probably the most stable Javascript package which exists. This has been stable for a very long time, and is easy to update. I think the cost of updating and maintaining Javascript script packages is underestimated when creating a new technoligy stack. query-validation, jquery-validation-unobtrusive and jquery-ajax-unobtrusive are used for the ASP.NET Core validation and the html attributes which setup the ajax calls, or partial requests.

  "version": "1.0.0",
  "name": "",
  "private": true,
  "devDependencies": {
    "bootstrap": "4.3.1",
    "jquery": "3.4.1",
    "jquery-validation": "1.19.1",
    "jquery-validation-unobtrusive": "3.2.11",
    "jquery-ajax-unobtrusive": "3.2.6",
    "popper.js": "^1.15.0"
  "dependencies": {}

The bundleconfig.json file is used to bundle the packages and minify and so on. This file must be configured to match the packages which you use.

// 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": [
    "minify": { "enabled": true }
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
    // Optionally specify minification options
    "minify": {
      "enabled": true,
      "renameLocals": true
    // Optinally generate .map file
    "sourceMap": false
  // Vendor CSS
    "outputFileName": "wwwroot/css/vendor.min.css",
    "inputFiles": [
    "minify": { "enabled": true }
  // Vendor JS
    "outputFileName": "wwwroot/js/vendor.min.js",
    "inputFiles": [
    "minify": {
      "enabled": true,
      "renameLocals": true
    "sourceMap": false
  // Vendor Validation JS
    "outputFileName": "wwwroot/js/vendor-validation.min.js",
    "inputFiles": [
    "minify": {
      "enabled": true,
      "renameLocals": true
    "sourceMap": false

The bundles are created and saved to the wwwroot.

The csproj file is setup to use the BuildBundlerMinifier, so that the bundles are created. The npm packages are also downloaded, if the node_modules don’t exist. This project is an ASP.NET Core 3.0 project.

<Project Sdk="Microsoft.NET.Sdk.Web">


    <PackageReference Include="BuildBundlerMinifier" Version="2.9.406" />

  <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('node_modules') ">
    <!-- Ensure Node.js is installed -->
    <Exec Command="node --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
    <Exec WorkingDirectory="" Command="npm install" />

The _Layout.cshtml razor view contains the links to the CSS and the Javscript bundles. Depending on your ASP.NET Core MVC project, Razor Page project, the vendor-validation.min.js file could be moved to a separate view, which is only used for the forms or the ajax views. In this layout no external CDNs are used.

<!DOCTYPE html>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - AspNetCoreInjectConfigurationRazor</title>

    <link rel="stylesheet" href="~/css/vendor.min.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/css/fontawesome-free-5.4.1-web/css/all.min.css" asp-append-version="true" />

    <div class="container body-content">

    <script src="~/js/vendor.min.js" asp-append-version="true"></script>
    <script src="~/js/vendor-validation.min.js" asp-append-version="true"></script>
    <script src="~/js/site.min.js" asp-append-version="true"></script>

    @RenderSection("scripts", required: false)

Updating packages

I use npm-check-updates to update my npm packages. Install this as documented in the package docs.

For the command line, ncu -u can be used to update the npm packages. These needs to be executed in the same folder where the packages.json file is.

ncu -u

You can then update the bundleconfig.json to match the updated npm packages. Per default, jQuery and Bootstrap 4 do not change so often, so usually nothing is required here.

After trying many different ways of updating UI packages and CSS in different projects for many different clients, this solution had the least effort to update and maintain the Javascript and CSS.



  1. […] An alternative way to build and bundle Javascript, CSS in ASP.NET Core MVC and Razor Page projects (Damien Bowden) […]

  2. […] An alternative way to build and bundle Javascript, CSS in ASP.NET Core MVC and Razor Page projects – Damien Bowden […]

Leave a Reply to The Morning Brew - Chris Alcock » The Morning Brew #2799 Cancel reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: