Mobile enable your Ruby on Rails site for small screens

Jonathon Horsman Dec 01 41 comments

As mobile devices and small screens accessing web sites become more common place, having your website tailored for these devices is becoming increasingly important.

Fortunately Ruby on Rails makes this reasonably easy. I recently went through the process of mobile-enabling our Ruby on Rails site.

As you can see it’s a simple site, a few static pages with a bit of database-driven content (the blog and recent tweets on the sidebar).

Here’s the process I followed which is reasonably straight-forward and took about 1 day.

The key is detecting the client (browser) connecting to the server and rendering the appropriate layout. For simplicity we have just 2 layouts, one for mobile and one for regular browsers.

The beauty of Ruby on Rails is the layout applies across our entire site, so I just make my changes in one place and they flow across all pages.
The content remains the same on each site and we can control the presentation in the CSS. This is where the true benefit of the MVC layers comes into play, and the advantage of defining all styles in the CSS (rather than inline).

The steps are:

  1. Add mobile detection to your controllers.
  2. Have your controller return the appropriate layout.
  3. Duplicate and customise the application.html.erb layout.
  4. Duplicate and customise the css file – this is the most time consuming part.
  5. Resize large images for mobile screens.
  6. Add a link to switch between mobile and “normal” screen sizes.

I have a Google phone with screen dimension of 320px wide by 480px high, so being selfish that’s the screen size I’m targetting. One of my goals was to avoid the annoying horizontal scroll bar.

First is mobile detection. Whenver a browser or mobile device connects to a web server it sends a bunch of headers, such as the content type it accepts, language and the type of browser making the connection.

The header parameter we care about for mobile detection is the HTTP_USER_AGENT. When I connect from my browser the value I get is:

Mozilla/5.0 (X11; U; Linux i686; en-GB; rv: 
Gecko/20091109 Ubuntu/9.10 (karmic) Firefox/3.5.5

You should recognise a few meaningful pieces of information here, notably I’m running Firefox 3.5 on Ubuntu Linux 9.10.
Likewise when I connect from my Google Android (an HTC Magic branded phone) the header is:

Mozilla/5.0 (Linux; U; Android 1.6; en-gb; HTC Magic Build/DRC92)
AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1

Apple weenies connecting from an iPhone supply a user agent string which looks like this:

Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us)
AppleWebKit/528.18 (KHTML, like Gecko) Mobile/7D11

Since there’s no standard way of identifying a mobile device (and the definition is vague and constantly changing as new technology is released), I just use a known array of mobile user agents from here.

All our controllers extend from one ApplicationController, which in turn extends from ActionController::Base.
Being the parent class to all other controllers means we can define the browser detection and return the appropriate layout in one place.

Using the HTTP_USER_AGENT in the request headers we do a pattern match array of mobile user agents.

class ApplicationController < ActionController::Base
  helper :all
  layout :detect_browser

  MOBILE_BROWSERS = ["android", "ipod", "opera mini", "blackberry", "palm","hiptop","avantgo","plucker", "xiino","blazer","elaine", "windows ce; ppc;", "windows ce; smartphone;","windows ce; iemobile", "up.browser","","mmp","symbian","smartphone", "midp","wap","vodafone","o2","pocket","kindle", "mobile","pda","psp","treo"]

  def detect_browser
    agent = request.headers["HTTP_USER_AGENT"].downcase
    MOBILE_BROWSERS.each do |m|
      return "mobile_application" if agent.match(m)
    return "application"

Notice the layout is simply a method pointer to detect_browser which does a pattern match on each item in my array of mobile user agents.

If any user agent string matches we’ll render the mobile_application layout, otherwise just the usual application layout.

So in my app/views/layouts I copy application.html.erb to mobile_application.html.erb and make appropriate modifications.

The first thing I did was replace stylesheet_link_tag "main" with stylesheet_link_tag "mobile" and copy public/stylesheets/main.css to public/stylesheets/mobile.css

Since your application has a lovely clean separation of layout to content, you only need to make changes to your style sheet, right? If you’ve embedded styling elements in your HTML then you may have some work to do moving them into a stylesheet first.

Our site has a 900 pixel wide banner image which clearly would not fit well in a mobile browser. So I created a public/images/mobile directory and resized all the big images and stuck them in there.
Then in my mobile.css and mobile_application.html.erb files I linked these new, smaller images.

Next is the tedious process of going through your mobile.css stylesheet and tayloring it for small screens.
What I did was remove the boxes which float right (tweets and client testimonials). These I put inline below the main page content.
Likewise the copyright notice and client login button which sit at the top right I moved down to the bottom of the page.
I also squished up the menu, removing excess padding and any fixed width content. I prefer to let my browser render the content to fill the available space, the way HTML was intended.

This fiddling with CSS and moving content around is the most time consuming part. And not being a designer it takes me a while. Fortunately our site is small and the layout is quite clean, with most changes being made just in the CSS and layout file.

The final, optional step is allowing the user to override your browser detection. We may have incorrectly detected their user agent so giving them the option to switch layout is a nice feature.

Sites like Google and Wikipedia use a different URL for their mobile site but I decided to store a variable in the session if the user chooses to override the automatic browser detection. If you have a strong opinion on which is a better method I’d be interested to hear why.

So in the footer bar of my site application.html.erb file I added the following:

<%= link_to "MOBILE SITE", :controller => "home", :action => "set_layout", :mobile => "1" %>

and likewise in the mobile_application.html.erb file I added:

<%= link_to "REGULAR SITE", :controller => "home", :action => "set_layout", :mobile => "0" %>

I then modified the home_controller.rb file to stick the chosen layout in the session:

def set_layout
 session["layout"] = (params[:mobile] == "1" ? "mobile" : "normal")
 redirect_to :action => "index"

And finally I amend detect_browser in the application_controller.rb, having the session variable takes precendence over my auto-detection:

class ApplicationController < ActionController::Base
  helper :all
  layout :detect_browser

  MOBILE_BROWSERS = ["android", "ipod", "opera mini", "blackberry", "palm","hiptop","avantgo","plucker", "xiino","blazer","elaine", "windows ce; ppc;", "windows ce; smartphone;","windows ce; iemobile", "up.browser","","mmp","symbian","smartphone", "midp","wap","vodafone","o2","pocket","kindle", "mobile","pda","psp","treo"]

  def detect_browser
    layout = selected_layout
    return layout if layout
    agent = request.headers["HTTP_USER_AGENT"].downcase
    MOBILE_BROWSERS.each do |m|
      return "mobile_application" if agent.match(m)
    return "application"
  def selected_layout
    session.inspect # force session load
    if session.has_key? "layout"
      return (session["layout"] == "mobile") ? 
        "mobile_application" : "application"
    return nil

As an interesting aside I have to force the session to be loaded with session.inspect otherwise session.has_key? always returns false, since it’s lazy loaded.
If there’s a better way to do this please add a comment and let me know.

I tested this using the Google Android emulator which you can download to trial various screen sizes without having to actually use the hardware.

Comments //

Franklin Lyons

Franklin Lyons Dec 03

Super smart - thank you for putting this out there for us.
My app needed a tweak so I wanted to put it here.
I had to change:

agent = request.headers["HTTP_USER_AGENT"].downcase


agent = request.env["HTTP_USER_AGENT"].downcase


Amir Jan 05

What if you wanted to make other mobile html views for different parts of the site, not just the application.html.erb. Would I edit that specific controller with the same code below:
  def detect_browser
    layout = selected_layout
    return layout if layout
    agent = request.headers["HTTP_USER_AGENT"].downcase
    MOBILE_BROWSERS.each do |m|
      return "mobile_application" if agent.match(m)
    return "application"

Jonathon Horsman

Jonathon Horsman Jan 20

Hi Amir

To render different layouts in different parts of the site you could just override the detect_browser method in the specific controller.

It would be a good idea to abstract the browser detection logic so that it's not repeated in each controller. So instead of

return "mobile_application" if agent.match(m)

you could use

return mobile_layout() if agent.match(m)

then write a mobile_layout method in the application_controller, e.g.

def mobile_layout
  return "mobile_application"

and subclass that in the controllers which you want to render a different layout.

Hope this helps

Jared White

Jared White Apr 01

This is great stuff! Thanks!!

(BTW, you might want to add "iphone" to your array in the sample code, because after mentioning it you don't actually have it in the MOBILE_BROWSERS array...)

Jared White

Jared White Apr 01

This is great stuff! Thanks!!

(BTW, you might want to add "iphone" to your array in the sample code, because after mentioning it you don't actually have it in the MOBILE_BROWSERS array...)


Tom Apr 08

Great article, thanks! In the code above, is session["layout"] ever set to something? Or should that be done inside selected_layout?

Everett Considine

Everett Considine May 01

btw...I went to view your site on my iphone and got your regulars site.

Jonathon Horsman

Jonathon Horsman May 04

I deliberately omitted iphone because it seemed to render regular sites well enough, but yes if you wanted a mobile-specific site for iphone then adding that would make sense.

Tom: session["layout"] is set in the home controller, but you could put that in application controller.


Andy May 09

Thanks so much - I used your blog for a starting list of mobile browser search strings. Very helpful :)


Dean May 30

great every think is good.

Lunn Ponlork

Lunn Ponlork Jun 06


When i using: <%= @content_for_layout %> in mobile_application.html.erb for call view (mobile_application). it not working with detect_browser. so it going to website normal, not for phone.

what's the problem?

Stephan Wehner

Stephan Wehner Jul 22

You ask "If there’s a better way to do this please add a comment and let me know."

Original method:

def selected_layout
    session.inspect # force session load
    if session.has_key? "layout"
      return (session["layout"] == "mobile") ?
        "mobile_application" : "application"
    return nil

Suggested method

def selected_layout

This will work with this new set_layout method:

def set_layout
 session["layout"] = (params[:mobile] == "1" ? "mobile" : "application") # was "normal" instead of "application"
 redirect_to :action => "index"




Adda Jan 29

I tried following these instructions and when I reload my root I get the following error:

SyntaxError in AlertsController#index

/Users/abirnir/Sites/transpo-hub/app/controllers/application_controller.rb:4: syntax error, unexpected tIDENTIFIER, expecting kDO or '{' or '('
..., “ipod”, “opera mini”, “blackberry”, “palm”...

and it goes on an on...

Any thoughts as to why this is happening?

The tutorial is great, thank you!


gerald Jul 06

Why not use the mobile-fu gem?

ipa to apk converter

ipa to apk converter Sep 12

I am truly grateful to the holder of this site who has shared this
great article at here. Sep 13

I want to know what blogs program you're using, Recently I bumped into security difficulties, would
you mind referring me?


Hiram Oct 17

Hello to all, the contents present at this site are
truly awesome for people knowledge, well, keep up the good work fellows.

iphone 3g jailbreak

iphone 3g jailbreak Oct 17

Fascinating blog! Is your theme custom made or did you download it from somewhere?
A design like yours with a few simple adjustements would really
make my blog jump out. Please let me know where you got your theme.

league of legends mouse pad katarina

league of legends mouse pad katarina Oct 18

Also known as Waliy Abdur Rahim, Hook Mitchell is best known for his incredible leaping ability; performing backboard shattering ally-oops,
12 foot rim dunks, and car jumping 360 dunks.

The 10% melee haste will boost your Hack and Slash possible.
Even at this early point, clear clans are starting to
form and more in-depth strategies are being discovered both
for the game itself and in the meta-game. Oct 18

Greetings I am so thrilled I found your website, I really found you by accident, while I was
browsing on Aol for something else, Regardless I am here now and would just like to say
kudos for a fantastic post and a all round interesting blog (I
also love the theme/design), I don’t have time to go through
it all at the moment but I have saved it and also added your RSS feeds, so when I have time I will be
back to read more, Please do keep up the excellent jo.

North Face Winter Coats

North Face Winter Coats Nov 25 Face Fleece Jacket's North Face Jackets Face Oulet's North Face Jackets's North Face Jackets Face Jackets Face Coats Face Wholesale Face Denali Jacket Face Oulet Face Denali Jacket Face Oulet Face Jackets For Kids Face Kids Coats North Face North Face Face Sale Face Winter Coats Face Borealis Face Hyvent Jacket Face Sale Face Winter Coats Face Borealis Face Hyvent Jacket Face Sale Face Winter Coats Face Borealis Face Hyvent Jacket Face Sale Face Winter Coats Face Borealis Face Hyvent Jacket UK Mulberry UK UK Outlet UK For Sale UK Mulberry UK UK Outlet UK For Sale UK Mulberry UK UK Outlet UK For Sale UK Mulberry UK UK Outlet UK For Sale

wholesale jerseys

wholesale jerseys Nov 28

one particular could even find jerseys coming in the superior teams of the nba and manage to dress in them.
wholesale jerseys


thslzgnnmz Nov 28

<a href="">athslzgnnmz</a>

used cars for sale by owner in orlando florida

used cars for sale by owner in orlando florida Jan 13

Have you ever thought about publishing an ebook or guest authoring on other sites?
I have a blog based on the same subjects you discuss and would really like
to have you share some stories/information. I know my visitors would
value your work. If you're even remotely interested,
feel free to send me an e-mail.

dog trainers columbia sc

dog trainers columbia sc Jan 18

This type of coat ends to thicken with temperature changes, or regular clipping, and it will
also shed out minimally, but regularly, aas well.
>>>>>>If you agree, pass this on. s : This is the scent your pup will leave behind.

Kuwait escort

Kuwait escort Feb 15

Thank you for sharing your info. I truly appreciate your efforts
and I am waiting for your next post thanks once again.


Depression Feb 22

On the other hand, several of her Montreal Concordia University peers loved to wait to the last minute and loved the challenge.

Obsessive compulsive disorder, also known as OCD is best described by the recurring compulsive thoughts that lead
the individual to perform a constant aact or
a behavior more like a ritual. Together with remedies, stress
and freak out assault treatment is usually incredibly efficient in relieving freak out attacks and generalized nervousness well known problems so lengthy as
the patient's condition has been accurately diagnosed aand remedy administered correctly.


Tomas Feb 23

This blog was... how do you say it? Relevant!!
Finally I have found something that helped me. Many thanks!


washi Feb 24

I do not even understand how I stopped up right here,
but I thought this post used to be good. I do not realize who you're however definitely you're going to a well-known blogger in the event you aren't
already. Cheers!

superior velvet

superior velvet Mar 11

Butt don't bbe fooled byy the title, this is still ѵery traditional aոd beautifully crafted
Christmas CD. Handbags ƴou ill bbe moving tto hold possess comparable іmportance.
My roߋm had an elegant king size bed fitted witrh soft cotton
sheets ɑnd Jim Thompson silks.


Health Jun 08

It's a pity you don't have a donate button! I'd certainly donate
to this outstanding blog! I guess for now i'll settle for book-marking and adding your
RSS feed to my Google account. I look forward to fresh updates and will talk about this blog with my Facebook group.
Talk soon!

my singing monsters tool

my singing monsters tool Jun 18

Howdy! I know this is kinda off topic however I'd figured I'd ask.
Would you be interested in trading links or maybe guest authoring a blog post or
vice-versa? My site goes over a lot of the same subjects as yours and
I think we could greatly benefit from each other. If you are
interested feel free to shoot me an email. I look forward to hearing from you!
Superb blog by the way!

Cute Puppy Video

Cute Puppy Video Jun 20

you're in point of fact a good webmaster. The site loading
pace is incredible. It kind of feels that you are doing any unique trick.
Moreover, The contents are masterwork. you have done a magnificent activity
on this subject! -- If You Love Cute Puppy Videos, Please Watch This Cute Puppy Videos : --

The Whitley Residences

The Whitley Residences Jun 27

Nice replies in return of this issue with real arguments and describing the whole thing about that.


最新映画DVD Jul 15


輸入代行 中国

輸入代行 中国 Jul 20

 タオバオ 代行:
 輸入代行 中国:
輸入代行 中国 Jul 20

By approving our response, we're going to contribute $0.01 to a unique cause.

Justin Bieber Phone Number

Justin Bieber Phone Number Jul 26

Thanks for every other magnificent article. Where else may anyone get that
type of info in such a perfect approach of writing? I've a presentation subsequent week, and I'm at the look for such


android Aug 20

Excellent colors & thme.
Did you build this amazing site yourself? Please reply back as I'm
attempting to create my own personal site and want to find out where you
got this from or what the theme is called. Thank you!

Hotels in Letterkenny

Hotels in Letterkenny Aug 20

Magnificent items from you, man. I have be mindful your stuff
prior to and you are simply extremely excellent.
I really like what you've acquired right here, certainly like
what you're stating and the best way in which you assert it.
You make it entertaining and you continue to care for to stay
it wise. I cant wait to read far more from you. This is actually a wonderful site.

Quotes app

Quotes app Aug 25

Attractive part of content. I just stumbled upon your blog and in accession capital to
assert that I acquire in fact enjoyed account your blog posts.
Anyway I'll be subscribing to your augment or even I success you get admission to consistently

Comments are closed

Recent Tweets

Blog: The monumental Myspace cock-up:
Tweeted on Friday at 09:43

Awww railsapi, delete some logs:
Tweeted on Wednesday at 16:15