179

I use a different layout for some actions (mostly for the new action in most of the controllers).

I am wondering what the best way to specify the layout would be? (I am using 3 or more different layouts in the same controller)

I don't like using

render :layout => 'name'

I liked doing

layout 'name', :only => [:new]

But I can't use that to specify 2 or more different layouts.

For example:

When I call layout 2 times in the same controller, with different layout names and different only options, the first one gets ignored - those actions don't display in the layout I specified.

Note: I'm using Rails 2.

Andrew Hendrie
  • 6,205
  • 4
  • 40
  • 71
mrbrdo
  • 7,968
  • 4
  • 32
  • 36
  • 1
    The Rails Guides documentation: http://guides.rubyonrails.org/layouts_and_rendering.html#finding-layouts – Kevin Jun 03 '14 at 16:46
  • 1
    Good point. The documentation for Rails 2: http://guides.rubyonrails.org/v2.3.11/layouts_and_rendering.html#finding-layouts – Kevin Jun 10 '14 at 18:22
  • 2
    (Also, I posted the guide to help future readers. I have no doubt you had your problem solved a long time ago ;-) – Kevin Jun 10 '14 at 18:25

8 Answers8

317

You can use a method to set the layout.

class MyController < ApplicationController
  layout :resolve_layout

  # ...

  private

  def resolve_layout
    case action_name
    when "new", "create"
      "some_layout"
    when "index"
      "other_layout"
    else
      "application"
    end
  end
end
August Lilleaas
  • 54,010
  • 13
  • 102
  • 111
  • 23
    Cool, thanks. And in case someone wants to do simpler things with one-liner the following is possible. Its easy to read and place in top of the controller. --- `layout Proc.new{ ['index', 'new', 'create'].include?(action_name) ? 'some_layout' : 'other_layout' }` – holli Aug 28 '11 at 21:13
  • 1
    Would this have a large effect on application performance if various layouts are using say several different css & js files respectively? – Noz Nov 08 '12 at 22:50
  • 15
    -1: Too complex. The comment below (https://stackoverflow.com/a/21203783/285154) is the best option. – dimitarvp Sep 11 '14 at 18:48
  • Thanks! This is still a problem in Rails 4.2, perhaps only if you are using inheriting controllers in many levels. I have used method before, but wasn't thinking of testing that when I had the problem now, thanks again. – 244an Oct 01 '15 at 15:07
  • @dimitko no it's not, I already referenced that solution in the question itself. It does not work if you want to use 3 or more different layouts (as is exactly my case). – mrbrdo Oct 06 '16 at 23:14
  • @LucaGuidi how is this not thread safe? The method / proc has no side effects. – GuyPaddock Sep 27 '17 at 13:26
  • See also [Choosing Layouts at Runtime in this Rails guide](https://guides.rubyonrails.org/layouts_and_rendering.html#choosing-layouts-at-runtime) – Nathan Hinchey Jun 26 '19 at 21:56
  • 6 years later I must tell you @dimitarvp, although both answers solve the question, they're meant to work in a different way. – Karol Karol Dec 02 '20 at 16:56
219

If you are only selecting between two layouts, you can use :only:

class ProductsController < ApplicationController
   layout "admin", only: [:new, :edit]
end

or

class ProductsController < ApplicationController
   layout "application", only: [:index]
end
Nathan Hinchey
  • 1,191
  • 9
  • 30
axeltaglia
  • 2,543
  • 1
  • 13
  • 5
  • 2
    Well, the problem with this is that you cannot access objects such as current_user to conditionally determine the layout – Andrew K Dec 14 '14 at 05:25
  • @AndrewK Dynamically choosing a layout doesn't seem to be part of the question asked. – Nick Mar 25 '16 at 15:19
  • 13
    If any of you read the question you would know this is not the right answer, as I have already described this solution in the question itself and also why it does not work in my case (3 or more layouts). – mrbrdo Oct 06 '16 at 23:13
  • 2
    Like mrbrdo said, this is not the answer. His question specifically indicates `(I am using 3 or more different layouts in the same controller)`. This answer allows a layout and no layout, *not* different layouts. – Michael May 14 '17 at 14:19
55

You can specify the layout for an individual action using respond_to:

  def foo
    @model = Bar.first
    respond_to do |format|
      format.html {render :layout => 'application'}
    end
  end
Gavin Terrill
  • 1,029
  • 7
  • 7
  • To me this is the more flexible answer. DRYing things out by applying `layout "[...]" to the controller class only allows one statement effectively. If you have more than two layouts to deal with (say, admin, generic_app, tailored_app), you will experience `Render and/or redirect were called multiple times in this action` errors; and you have no choice but to use this suggestion. – Jerome Nov 22 '14 at 10:15
  • 1
    This answer is the best, its simple and neat as opposed to using methods with switch or if statements inside. – kev Jul 07 '15 at 08:41
  • also it can specify the erb file and layout both: `format.html { render 'custom_index', layout: 'application' }` in the same way. – thatway_3 Mar 14 '16 at 02:57
17

You can also specify the layout for action using render:

def foo
  render layout: "application"
end
Tunaki
  • 132,869
  • 46
  • 340
  • 423
yottanami
  • 377
  • 2
  • 7
8

There's a gem (layout_by_action) for that :)

layout_by_action [:new, :create] => "some_layout", :index => "other_layout"

https://github.com/barelyknown/layout_by_action

barelyknown
  • 5,510
  • 3
  • 34
  • 46
8

Precision :

A not really but working DRY way is what you see above, but with a precision : the layout need to be after your variables for working ("@some"). As :

def your_action
   @some = foo
   render layout: "your_layout"
end

And not :

def your_action
   render layout: "your_layout"
   @some = foo
   @foo = some
end

If you do a before_action... it's won't work also.

Hope it helps.

Gregdebrick
  • 533
  • 6
  • 14
7

Various ways to specify layout under controller:

  1. In following code, application_1 layout is called under index and show action of Users controller and application layout(default layout) is called for other actions.

    class UsersController < ApplicationController
      layout "application_1", only: [:index, :show]
    end
    
  2. In following code, application_1 layout is called for all action of Users controller.

    class UsersController < ApplicationController
       layout "application_1"
    end
    
  3. In following code, application_1 layout is called for test action of Users controllers only and for all other action application layout(default) is called.

        class UsersController < ApplicationController
          def test
            render layout: "application_1"
          end
        end
    
puneet18
  • 4,341
  • 2
  • 21
  • 27
0

You can use it like my example:

def show
...
render layout: "empty",template: "admin/orders/print" if params.key?('print')
end

I specified the layout and the template html.erb if params print present.

ramzieus
  • 138
  • 11