Useful Flash Messages in Rails

Jul 11, 2008

Inevitably somewhere in your rails app you display flash messages to your users to inform them that an action has (or hasn't) taken place. Sometimes they need to provide more info, maybe you've just created some new information for them. Why make them sit around, or wonder where they need to go next when you can take them straight to it in the message itself?

I've ripped this code out of a project I've been working on and made it a little more generic. Initially I was going to just insert a link in the flash message, but as I was rightly pointed out by my pair and elegant coder extraordinaire, that would mean I'd be polluting my controller with logic and content that should be handled by the view. How do we solve a problem like pollution?

First, we updated the controller so that the flash message now looked something like this:

flash[:error] = "Username and password do not match. If you have 
                   forgotten your password you can %s"

The %s is there to use a string substitution later. If you wanted to insert your link somewhere else, move the %s. Next, we created a new key in flash to hold our additional information (the link path and text):

flash[:error_item] = ["reset it here", forgot_path]

Quite simply, it's an array with the first item being the text and the second the path. Now for handling it within our view, here's what I've got in `application_helper.rb`:

FLASH_NOTICE_KEYS = [:error, :notice, :warning]
  def flash_messages
    return unless messages ={|k| FLASH_NOTICE_KEYS.include?(k)}
    formatted_messages = do |type|      
      content_tag :div, :class => type.to_s do
        message_for_item(flash[type], flash["#{type}_item".to_sym])

  def message_for_item(message, item = nil)
    if item.is_a?(Array)
      message % link_to(*item)
      message % item

The reason for the constant is that I don't use just :error and :notice, I've also got :warning for times when something needs action but not because of something the user has done wrong (I don't want to make them feel bad when they've not had the opportunity to be good!). In flash messages I exit out on the first line if we don't have content to process for the flashes I expect to output (:error, :notice, and :warning) otherwise I loop over each and create a div to contain the message. The class on the div will be either error, notice or warning so you can style it properly. We defer working out the content to message_for_item

In message_for_item I check if the input is an Array, and if so I use the build in string substitution method to insert the link in the relevant place. As you may notice, I've called splat on item by putting an asterisk in front of it. That means your array can essentially just be the parameters you'd pass in to link_to, allowing you to add additional classes or options if you wish. If it's not an array, I skip the link to and just to the substitution. This will handle traditional flash messages as well as some that might want substitution of a string or other object. Wherever you'd previously inserted your flash messages, now you call:

and the end result is something like this:

So what else could you do with this? Well we extended it to be a little more intelligent based on the object that was passed in. You could also use it as part of a before filter if you've got flash messages that are relatively similar across actions on controllers. Just put %s in the bit to substitute, and then at the relevant part of your action you can insert the a string into flash[:error_item] or the appropriate key, and have it displayed. Like this:

before_filter :set_message, :only => [:create, :update]

  def create
    @order = Order.create!(params[:order])
    flash[:notice_item] = "#{@order.items} items" 

  def update
    @order = Order.find(params[:id])
    flash[:notice_item] = "#{@order.items} items" 

    def set_message
      flash[:notice] = "Thank you, we have updated your order with %s" 

I hope it helps. Coming up next, my sexy form builder!

Hi, I'm Glenn! 👋 I'm currently Director of Product @ HashiCorp, and we're hiring! If you'd like to come and work with me and help make Terraform Cloud even more amazing we have multiple positions opening in Product ManagementDesign, and Engineering & Engineering Management across a range of levels (i.e., junior through to senior). Please send in an application ASAP so we can get in touch.