How to handle bundling and minification in ASP .NET

What you will learn?

In this blog post, I will go through how you can handle your website scripts in an efficient way using bundle and minification technique.

ASP .NET framework provides a native and powerful way of script optimization and I often use it in my solutions.

After going through this step-by-step guide - you should be able to apply this solution to your website.

Let's go!

#1 Installing the package

First, make sure the right package is there

Install-Package Microsoft.AspNet.Web.Optimization

#2 Prepare BundleConfig inside App_Start folder

For this, let's create BundleConfig.cs file to under App_Start folder, if the folder don't exist just create it manually.

Then, you can organize your scripts and split them into bundles.

In the example below we create two separate bundles - one for javascripts files and the other for css.

You can create any number of bundles depending on your needs.

For example, you can create different bundles for different modules - lightweight bundles can speed up your site.

it is worth experimenting and checking in practice - what combination of scripts/bundles will be most effective.

At the end you should add the bundles to BundleCollection.

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            ScriptBundle scriptBundle = new ScriptBundle("~/bundles/master/js");

            scriptBundle
                .Include("~/assets/js/jquery.js")
                .Include("~/assets/js/plugins.js")
                .Include("~/assets/js/functions.js");

            bundles.Add(scriptBundle);

            StyleBundle styleBundle = new StyleBundle("~/bundles/master/css");

            styleBundle
                .Include("~/assets/css/plugins.css")
                .Include("~/assets/css/style.css")
                .Include("~/assets/css/custom.css");

            bundles.Add(styleBundle);

            BundleTable.EnableOptimizations = true;
        }
    }

4# Force optimization

In the next step, you should Disable debug mode and force optimization as follows:

<system.web>
     <compilation debug="false" />
</system.web>
BundleTable.EnableOptimizations = true

5# What about cache in browser?

It often happens that after making some changes to the scripts and fresh release, we can get strange feedback:

"It's not working" or "I can't see the changes".

Most of the times it's caused by browser's caching mechanism.

After a quick website source code review, we see:

<link href="/bundles/master/css" rel="stylesheet"/>

How to avoid those issues?

There is a good old trick to make sure your site will not hold old scripts in the browser. It’s really easy to force browser to refresh css/js bundles using appending query string parameter at the end of each bundle. After googling here and there I found great code snippet here on StackOverflow. All credit goes to author Ranjeet patil (thanks)!

internal static class BundleExtensions
    {
        public static Bundle WithLastModifiedToken(this Bundle sb)
        {
            sb.Transforms.Add(new LastModifiedBundleTransform());

            return sb;
        }

        public class LastModifiedBundleTransform : IBundleTransform
        {
            public void Process(BundleContext context, BundleResponse response)
            {
                foreach (var file in response.Files)
                {
                    var lastWrite = File.GetLastWriteTime(HostingEnvironment.MapPath(file.IncludedVirtualPath)).Ticks.ToString();
                    file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", lastWrite);
                }
            }
        }
    }

Using this clean and awesome csharp extension method - we get elegant and effective code inside BundleConfig.cs:

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            ScriptBundle scriptBundle = new ScriptBundle("~/bundles/master/js");

            scriptBundle
                .Include("~/assets/js/jquery.js")
                .Include("~/assets/js/plugins.js")
                .Include("~/assets/js/functions.js");

            bundles.Add(scriptBundle.WithLastModifiedToken());

            StyleBundle styleBundle = new StyleBundle("~/bundles/master/css");

            styleBundle
                .Include("~/assets/css/plugins.css")
                .Include("~/assets/css/style.css")
                .Include("~/assets/css/custom.css");

            bundles.Add(styleBundle.WithLastModifiedToken());

            BundleTable.EnableOptimizations = true;
        }
    }

This way - after each build - you will get unique extra parameter in your script url:

<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>

6# Register the bundle engine

Now it's the right time to register the bundles in the Application_Start event inside Global.asax.cs file as follows: 

protected void Application_Start(Object sender, EventArgs e)  
{  
    BundleConfig.RegisterBundle(BundleTable.Bundles);  
}

7# Using bundling and minification in Razor Views:

First of all, make sure ~/View/Web.config file contains the new namespace 

<add namespace="System.Web.Optimization"/>

Then, include namespace in your Razor View, so you could use the @Scripts and @Styles helpers:

@using System.Web.Optimization;

Finally, you can render the bundles in the View using helpers:

@Scripts.Render("~/bundles/master/js")
@Styles.Render("~/bundles/master/css")

Your master page can look like this:

<!DOCTYPE html>
<html lang="en-US">
<head>
    @Styles.Render("~/bundles/master/css")
</head>
<body>
    @RenderBody()

    @Scripts.Render("~/bundles/master/js")
</body>
</html>

Final result

As a result you should get nice and warm bundles output:

Css bundle output:

<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>

Js bundle output:

<script src="/bundles/master/js?v=8kmHc-ukmg3rp-jj2rNMjYYIG_lP3ErMNtowPG93XbM1"></script>

That's it - Happy bundling & minification 😀