18 4 / 2012

Responsive Images with Cloudinary on Rails

Responsive Images is a hot topic lately. There are few solutions to the problem: introduction of new html element (which will take years to be ready for use), new image format (which may take even more time), and using other technologies to solve the problem. Here, I’ll focus on the last one.

The goal is clear:

  1. Detect user’s device screen resolution
  2. Serve appropriate images for best browsing experience

In order to handle cases where you cannot detect user’s device capabilities (e.g. no javascript support), we need to provide reasonable defaults (e.g. medium sized images).

Here is how you can accomplish this in few simple steps by using Cloudinary service for hosting your assets.


The concept is simple:

  1. When user enters our website, we’ll use javascript to detect his device resolution.
  2. We’ll store that resolution in a cookie.
  3. Backend will utilize information inside cookie to serve appropriate image format.

First two steps are quite easy to do. All you have to do is inject following javascript

document.cookie='resolution='+screen.width+'x'+screen.height)+'; path=/';

That would store user’s device screen resolution in a cookie. On the next page load, that cookie will be available to your backend.

Now, lets override cloudinary cl_image_tag to allow :responsive option. You can do so by including this in application_helper file:

def cl_image_tag(source, options = {})
  if (sizes = options.delete(:responsive)) && (resolution = cookies[:resolution])
    res = case resolution.split('x').first.to_i
          when 0..480 then :phones
          when 481..767 then :tablets
          else :desktops
          end

    size = sizes[res]
    options[:size] = size if size
  end

  super(source, options)
end

Method simply checks whether :responsive option and :resolution cookie are present. If so, it replaces the :size parameter with appropriate value. The usage would be like this:

<%= cl_image_tag "sample.jpg", size: '200x200', crop: 'fit', 
    responsive: { phones: '50x50', tablets: '100x100' } %>

That would serve 50x50px image for phones, 100x100px version for tablets and fallback to 200x200px version for all others. Sweet!