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: Saturday, September 15, 2012 5:05 PM
Last Updated: Thursday, September 20, 2012 3:38 PM
Posted by: Mikesdotnetting
Total Views to date: 43383


Wednesday, December 5, 2012 3:26 PM - 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.

Tuesday, February 26, 2013 10:30 AM - Naveen

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

Sunday, September 14, 2014 2:03 PM - 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!


Tuesday, November 11, 2014 1:53 PM - swapnil

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

Tuesday, November 11, 2014 8:06 PM - Mike


Read the article above. It includes CSS files.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

stephen 1/28/2015 9:53 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Hi, Sorry for wasting your time, I had a comment <!--Images--> inside the form this stopped it from...

stephen 1/28/2015 9:47 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Everything has been ok up until this part, here's a snippet of the entire file but i removed lines =...

Stephen kenny 1/28/2015 8:26 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Hi, great article. My issue is with the create view. @using (Html.BeginForm("Create", "Reptiles",...

Jenelyn 1/27/2015 6:44 PM
In response to MVC 5 with EF 6 in Visual Basic - Implementing Basic CRUD Functionality
I'm searching CRUD using VB.net 2010 and I can't find any, how sad...

Kadu Portugal 1/23/2015 10:55 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Hi Mike, Thank you for your useful article. I implement it in my project, and everything run...

Satyabrata 1/23/2015 6:02 AM
In response to Usage of the @ (at) sign in ASP.NET
Refreshed a lot of things.Thanks...

Vo Thanh Chinh Trung 1/22/2015 4:30 PM
In response to Adding A View
It's very helpful...Thank you so much, Mikes!...

Joop Stringer 1/22/2015 12:00 PM
In response to Inline Editing With The WebGrid
How to add a row for a new record ?...

Gaetan C 1/21/2015 8:49 PM
In response to Looking At The WebMatrix WebGrid
I use the webgrid very often and I Wonder if it's possible to merge to different sql query into on...

Teresa 1/19/2015 3:08 PM
In response to MVC 5 with EF 6 in Visual Basic - Creating an Entity Framework Data Model
At the top, the first thing you write is "Open Visual Studio and create a new C# Web project named I...