Optimising ASP.NET Web Pages Sites - Bundling And Minification

ASP.NET 4.5 saw the introduction of new features for improving the performance of web sites. Delivered within a new library called System.Web.Optimization, bundling and minification enables you to combine multiple JavaScript or CSS files into one bundle, and to minify them thereby reducing the number of HTTP requests that browsers have to make, reducing the size of the files, and improving performance of the site overall, especially over slower (read 3G) networks.

If you create your Web Pages site using Visual Studio Express 2012 or Visual Studio 2012, System.Web.Optimization.dll is included as part of the template, which is an extended version of the WebMatrix Starter Site template. And then you have to unpick all the clutter that you don't want. You should not remove Antlr3.Runtime.dll or WebGrease.dll, as these are required components for the Bundling and Minification system.

If you create your site using WebMatrix, none of the templates include System.Web.Optimization.dll. You have to get it from Nuget. You can do that using the Nuget Gallery feature, although that was playing up when I tried, so I used the Package Adminstration Manager. Just to add a layer of confusion, you need to install a package called Microsoft.AspNet.Web.Optimization. Not only that, but the package is only available outside of the default WebMatrix packages, so you need to set the Source to Default (All).

Once you have installed it, you are able to get working with the new features.

Minification takes place automatically. The process reduces white space in .js and .css file, removes any comments and in the case of .js files, will also shorten variable names. Significant file size savings can be achieved. Bundling, on the other hand, needs to be managed programmatically.

A Bundle is a collection of files that will be combined into one file. Basically, their contents are minified, and then appended to eachother. Obviously, especially in the case of .js files, it's important to ensure that files are added to bundles in the correct order. Bundles are stored in a BundleTable object as a BundleCollection. The best place to create a BundleCollection is in an _AppStart.cshtml file:

@using System.Web.Optimization;
    var bundles = BundleTable.Bundles;


Notice that System.Web.Optimization is referenced via a using directive.

You create bundles based on their content. Javascript bundles are created using the ScriptBundle type. Style sheet bundles are created using the StyleBundle type. For example, here's how to create a ScriptBundle and add it to the BundleTable:

var jquery = new ScriptBundle("~/bundles/jquery");

The new ScriptBundle object is given a virtual path which can be anything you like. It effectively acts as a name by which the bundle can be identified. It does not have to match an existing path in the web site folder structure. In fact, something as simple as "~/jquery" will do. Once the bundle has been instantiated, you specify the actual files that should be included by using the Include() method. The virtual path you provide here must relate to the actual file location otherwise the file will not be found. In the example above, the file has been specified by name, but there are other ways to specifiy the contents of a bundle as you will see soon. Multiple files can be specified by passing their virtual location in to the Include method as a comma-separated list or an array of strings. Finally, the bundle is added to the BundleTable's BundleCollection using the BundleCollection's Add method.

StyleBundle objects are created in exactly the same way:

bundles.Add(new StyleBundle("~/bundles/css").Include(

This example illustrates method chaining to produce a more concise syntax.

The following shows a couple of files being added to a bundle, using different methods to define the files:

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

The {version} wildcard allows you to update to newer versions of jQuery without having to change the code the references the file. At the moment, I have 1.8.1 in my site. If 1.8.2 comes out, I just replace the file. 1.8.2 will be picked up automatically by the bundling mechanism. The wildcard does more than that. It picks up the full version during debug, and any minified version if the site it set to release mode. Debug and release will be discussed a bit later.

The second file features use of the * wildcard character. Any file matching the specified pattern will be included, with multiple files added in alphabetical order. Only one wildcard can be used per virtual path. In the ecxample above, the wildcard is used as a suffix. It can also be used as a prefix to the file extension:

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

This will result in all files with a .js extension being included in the one bundle in alphabetical order. Often, multiple fileswill have dependencies on others so the order in which files are referenced is important and needs managing. For that reason, you are unlikely to use * wildcard matching like that very often.

The IncludeDirectory method offers another blunt instrument for specifying multiple files:

bundles.Add(new StyleBundle("~/allStyles").IncludeDirectory("~/Styles", "*.css")); 

This results in all .css files in the Styles folder being bundled in alphabetical order so doesn't differ from the previous wildcard example. However, the IncludeDirectory method offers an overloaded version that allows you to include subfolders in the search for matching files:

bundles.Add(new StyleBundle("~/allStyles").IncludeDirectory("~/Styles", "*.css", true));   

Having created bundles, you need a way to consume them in your pages. There are two classes to manage this: Scripts and Styles. Both have a static Render method which takes a comma-separated listof virtual paths, or an array of them. These paths are the identifiers you gave your bundles. Here is an example of a busier _AppStart.cshtml file:

@using System.Web.Optimization;
    var bundles = BundleTable.Bundles;
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

    bundles.Add(new ScriptBundle("~/bundles/knockout").Include(

    bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(

    bundles.Add(new StyleBundle("~/bundles/css").Include(

    bundles.Add(new StyleBundle("~/bundles/jquery-css").Include(

Three ScriptBundles have been created along with two StyleBundles. To render them, you include a using directive for System.Web.Optimization and then use their respective Render method:

@using System.Web.Optimization;

<!DOCTYPE html>

<html lang="en">
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>My Site's Title</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        @Scripts.Render("~/bundles/jquery", "~/bundles/knockout", "~/bundles/modernizr")
        @Styles.Render("~/bundles/css", "~/bundles/jquery-css")

At the moment, if you run the page, the files are all referenced correctly:

However, if you look at the jquery file that has been included, you see that it is the full development version. Equally, the debug version of knockout has been included. None of the files have been minified. This is because, by default, Web Pages applications run in debug mode as dictated by the compilation setting in the web.config file. You need to set debug = false to get minification to work. This also helps to improve the performance of your deployed site:

<?xml version="1.0" encoding="utf-8"?>

    <compilation debug="false" targetFramework="4.0" />

Having made that change, if you re-run the site, the bundles are transformed, minified and referenced by the value you provideed as their virtual paths:

<!DOCTYPE html>

<html lang="en">
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>My Site's Title</title>
        <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <script src="/bundles/jquery?v=MIr0sK49sxIK-epXF1SVcUd13V3eqzcL7nEICnt3vnc1"></script>
<script src="/bundles/knockout?v=pfuk13BYMQiDE7BiWZNzr6dcinflTsFL5KnKHpmJ4nY1"></script>
<script src="/bundles/modernizr?v=Vd40cG5fYxxjdknf_y9ilK-zi7pnjL35tk9IAsOQgQc1"></script>

        <link href="/bundles/css?v=qqSzEfGVYWAqpZj10F9l3OmsaavLPt8zTTTTuSNgO9s1" rel="stylesheet"/>
<link href="/bundles/jquery-css?v=ps9Ga9601PrzNA2SK3sQXlYmNW3igUv5FOdOPWptyus1" rel="stylesheet"/>


Bundling sets the content expiration for each bundle to 12 months so that they are cached by the browser. The querystring value that is generated for each bundle does not change unless the content of the bundle changes. At that point, a new querystring value is generated which results in the browser downloading the revised bundle immediately.

Bundling and minification are considered advanced topics, which is why the library wasn't included as part of the WebMatrix Razor templates. However, the library is very easy to add to your WebMatrix site and to use. A lot of Javascript libraries come with minified versions, so the minification system doesn't provide a lot of additional assistance there, but it certainly produces benefits on your own script and style files.


Date Posted:
Last Updated:
Posted by:
Total Views to date: 52852


- Kenneth

Why does no one ever mention the difference in approaches of using a BundleConfig.cs file (where your resources are hard-coded) vs using the bundle.config xml file? Is there some particular pro or con for using BundleConfig.cs? It seems like a more dynamic approach to use an xml file that you could modify without redeploying if needed.

- Naveen

Do you know if there is a way to get this work for ASP.NET 3.5 Web Forms application?

- Michael

Hi Mike,

Great article that's exactly what is missing in the asp.net community content. The page about bundling still says that it's not supported in webmatrix, but that doesn't mean that it's impossible to do it at all. Thanks!


- swapnil

I did bundling as per above code. I need to performed the minification for some css files. Kindly provide the minification steps .

- Mike


Read the article above. It includes CSS files.

- Saravanan


Great article about the Bundling and minification. Regards the caching as you mentioned the 12 months from the bundle created. Can we change the expiration date for the bundle caching.

- Saywer Ford

Great Article. I did everything right and working fine.

How about page specific js files. Lets say I have deafault.aspx and default.js file which is only for same page . Is there any facility so that I can add js file into existing bundle on a specific page.

- Mike


No - I don't believe it is possible to alter the expiry date of a bundle.

- Mike


The BundleTables collection is global for the application. If you add a new script to an existing bundle, it will be available wherever that bundle is referenced, so no - bundling is not the solution for page-specific scripts - unless you create a page-specific bundle.

Recent Comments

sandeep 8/28/2015 7:03 AM
In response to 7 C# 6.0 Features That Every ASP.NET Developer Should Know About
very good article, i like it........keep writing such quality article in future. thx Mike....

Hassan, MVC Learner 8/28/2015 6:37 AM
In response to Get The Drop On ASP.NET MVC DropDownLists
Great Help, simple, great and patiently explained article !...

Anvesh 8/28/2015 12:39 AM
In response to ASP.NET MVC DropDownLists - Multiple Selection and Enum Support
what if we are taking postback values from FormCollections instead of an array int[] category. How...

Ben 8/27/2015 10:50 PM
In response to Simple File Download Protection with ASP.NET
Is it possible to setup your project to publish files to outside of your root directory? I would to...

Fred 8/26/2015 12:50 AM
In response to WebMatrix Opens Wrong Version Of Visual Studio
I enjoyed many of your tutorials but the problem is none of the tutorials are combined like most be....

Muhammad Ashikuzzaman 8/25/2015 2:48 PM
In response to Managing Checkboxes And Radios In ASP.NET Razor Web Pages
That's a very good tips for razor...

Sergey 8/25/2015 8:32 AM
In response to More Flexible Routing For ASP.NET Web Pages
Hi. How I can set up my site to get urldata from link for default page? site.com/default/1-...

Tony Gray 8/25/2015 6:27 AM
In response to Adding Validation
Hi Mike, Really helpful article and series. Thanks. Small typo in 4th paragraph you have so...

amanda n 8/25/2015 12:38 AM
In response to Solving the Operation Must Use An Updateable Query error
Thank you very much. I'm a uni student and while solving coding problems is usually enjoyable and me...

salman 8/23/2015 9:25 AM
In response to How To Send Email In ASP.NET MVC
thanks ......