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: 47333


- 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.
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 I end up deleting quite a lot. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Requests to fix your code (post a question to forums.asp.net instead, please)
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam

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

Lorenzo 3/26/2015 8:21 AM
In response to iTextSharp - Introducing Tables
Hi Mike How can I add padding to all cells in the table? Kind Regards Lorenzo...

Satyabrata Mohapatra 3/25/2015 8:11 AM
In response to How To Send Email In ASP.NET MVC
Great article. Simple and up to the point....

Afzaal Ahmad Zeeshan 3/24/2015 8:17 PM
In response to How To Send Email In ASP.NET MVC
A great way to teach the MVC framework for sending the emails... Also, what I found helpful was the...

Jim H 3/24/2015 2:32 PM
In response to Migrating From Razor Web Pages To ASP.NET MVC 5 - Model Binding And Forms
Thank you. This helps....

wazz 3/22/2015 5:48 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
great info!!...

rael 3/21/2015 8:53 PM
In response to Getting the identity of the most recently added record
I spent hours trying to figure how to achieve this in C#. This article helped me. Thanks a lot...

Stephen 3/21/2015 8:48 PM
In response to Ajax with Classic ASP using jQuery
This was very helpful, thanks:)...

patrick voes 3/19/2015 10:19 AM
In response to iTextSharp - Introducing Tables
Thank you! very helpfull....

Bigmachini 3/19/2015 6:13 AM
In response to ASP.NET MVC DropDownLists - Multiple Selection and Enum Support
This just made my day, afternoon, evening, night... was looking for a way of doing this without to a...

Bobbyg 3/19/2015 4:14 AM
In response to HTML Helpers For Forms In Razor Web Pages
Nice article. There are bugs in MVC 5 with dropdowns retaining values from other screens and them. I...