Optimising ASP.NET Web Pages Sites - Bundling And Minification

4.4 (15 votes)

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


- 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

Ghazanfar 30/01/2016 06:43
In response to Getting Started with ASP.NET MVC 5 using Visual Basic
Nice working. Please keep it up to convert csharp code into vb.net. Its very helpful for vb...

sara 29/01/2016 09:39
In response to Simple Login and Redirect for ASP.NET and Access
Hi there, I am trying to validate and check for inputs entered. When I don't enter any inputs and...

Martin Thatcher 28/01/2016 17:28
In response to MVC 5 with EF 6 in Visual Basic - Advanced Entity Framework Scenarios
A small typo I think. In the code section that begins Function Index(ByVal SelectedDepartment As As...

Suresh_thefame 28/01/2016 08:03
In response to Sessions and Shopping Carts

Andrey Kurdyumov 28/01/2016 05:47
In response to ASP.NET 5: Uploading files with ASP.NET MVC 6
@Lee IFormFile has OpenReadStream(): Stream method...

Alisa 27/01/2016 18:37
In response to A Better Way To Export Gridviews To Excel
Thanks for this example! I am also asking about the date fields having to be re-formatted when the a...

Dan Buckley 27/01/2016 00:47
In response to What ASP.NET Can And Cannot Do
New to all things programming, this was very helpful and clear. Please write more....

satyabrata 26/01/2016 16:16
In response to Request.Form Is Empty When Posting To ASPX Page
Thank you....

david sanchez 26/01/2016 09:51
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Thanks! This line " <img src="~/images/@Model.FilePaths.First(f => f.FileType == alt="" />" an...

Bryon 25/01/2016 15:06
In response to Windows Authentication With ASP.NET Web Pages
Was hoping this would help solve the issues I'm having. Then I saw the dates and new it was too old....