Rails Asset Pipeline Handling of CSS and JS

These days, when we build websites and web applications, if we’re lucky, we don’t have to worry about supporting legacy browsers. But if we do need to support Internet Explorer below version 9, there are JavaScript files that will make this support easier. We may also need to supply a separate IE-specific stylesheet, depending on how we write our CSS. And we don’t want to make website users download these assets if they don’t need it.

Rails 3.1 introduced a feature called the asset pipeline. Among other things, it allows developers to be efficient by separating their code into smaller, more manageable files, while simultaneously reducing HTTP requests in the browser and thus improving website/web app performance, by having only one file—the manifest file.

What is a manifest file?

It’s a file – JavaScript or CSS – that you load with directives that specify which files should be included when it concatenates and minifies into one file. Tell the manifest file which files or directories to include, and then simply point to the manifest file in the head of your layout.

Adding files to the manifest

There are a few ways to add files to the manifest. One, by using the “require_tree” directive; another, by listing specific files.

For JavaScript files, Rails automatically includes jquery and jquery_ujs; any other files we will need to specifically include (note: you do not need to include the file extension).

//= require jquery
//= require jquery_ujs
//= require "jquery_functions"
//= require_tree .
//= require_tree ./common

The example above most likely wouldn’t happen in real life – it’s simply there for illustration purposes. Here we see a few (of many) ways to include files into the manifest. This example is showing the inclusion of the file “jquery_functions.js”, the inclusion of the entire /javascripts/ directory, and the inclusion of all files in the /javascripts/common/ directory, respectively.

In real life applications, the order of execution is important, so the manifest file should list each JavaScript file in dependent order.

A note about require_tree

This directive will include all files from the root of the /javascripts/ directory in alphabetical order, which is, in my opinion, never desirable, unless the order of files doesn’t matter. Frequently, it does. I’ll speak about alternate implementations further down in this post.

For CSS files, the implementation is the same, but the syntax of the manifest file is a little different:

*= require_self
*= require "styles"
*= require_tree ./common

The example above shows different ways to include files into the CSS manifest. I would caution against ever using “require_tree .” (including ALL files) with CSS, as the order of files is very important. When working with SASS files and partials, those partials should be included with @import directives in the .scss/.sass files themselves, and not in the manifest.

Now we know how to include files into the manifest. Next, we will link to the manifest file(s) in our layout.

 <%= stylesheet_link_tag 'all' %>

That’s it!

But what if you want to include files only for old versions of Internet Explorer?

In this case, we don’t want to include in our manifest the files that specifically apply to making these browsers behave. My solution: make subdirectories and manifest files, declare the new manifest files, include the new manifest files with IE conditional comments in the head of your layout. I’ll explain further.

Make subdirectories and manifest files

In order to continue to take advantage of the asset pipeline and its manifest files, we should create subdirectories in the /stylesheets/ and /javascripts/ directories. I like to use /stylesheets/ie/ and /javascripts/ie/. In this way, we can tell new manifest files to include the files in these directories, and reap the performance benefit of having them concatenated into one file.

Declare new manifest files

After stocking your subdirectories with the relevant files, create manifest files for them. For the above example, I would create /javascripts/ie.js and /stylesheets/ie.css. Include the same directives for telling the manifest which files to pull in and consolidate.

Set up your JavaScript directive:

//= require_tree ./ie

And your CSS directive:

*= require_self
*= require_tree ./ie

Since these new manifest files are not part of another manifest, one more step is required in order for these files to be compiled in the project. The manifest files need to be individually listed out in application.rb. This code is not in application.rb by default, so it will need to be added as necessary. List files as they appear in the root of /javascripts/ or /stylesheets/, with extensions:

# Enable the asset pipeline
config.assets.enabled = true
config.assets.precompile += %w(
  ie.js
  ie.css
)

Include the manifest files with IE conditional comments in your layout

The last step is calling these new files in the of your layout:

... all the other stuff ...
<!--[if lt IE 9]>
   <%= stylesheet_link_tag 'all' %>
<![endif]-->

This seems to be the least painful method of implementation. We could easily forego the directories and list files out individually, but why, when we can still take advantage of the streamlined process the asset pipeline has to offer?

This is certainly not the only solution. We would love to hear your thoughts on how you include individual files within a Rails project using the asset pipeline that don’t fit in the manifest.

Tweet at Foraker

Share this post!