Rails' Partial Features You (didn't) Know

Rails Designer - Nov 14 '24 - - Dev Community

This article was originally published on Rails Designer


Partials have been an integral part of Rails. They are conceptually simple to understand, but they pack quite a few smart and lesser known features you might not know about. Let's look at all of them!

Basic rendering

Let's look at the basics of rendering a partial.

<%= render partial: "application/navigation" %>
Enter fullscreen mode Exit fullscreen mode

This will render the partial app/views/application/_navigation.html.erb. The filename starts with an underscore, but is references without one.

You can also omit the partial keyword.

<%= render "application/navigation" %>
Enter fullscreen mode Exit fullscreen mode

So far nothing you didn't know already, I assume.

Local Variables

You can pass variables like so.

<%= render partial: "user", locals: {user: @user}  %>
Enter fullscreen mode Exit fullscreen mode

Again, this short-hand can also be used:

<%= render "user", user: @user %>
Enter fullscreen mode Exit fullscreen mode

Or if you follow conventions from a to z, you could write this:

<%= render @user %>
Enter fullscreen mode Exit fullscreen mode

This assumes a few things:

  • @user is present
  • a partial _user.html.erb in app/view/users.

Quite a bit cleaner, right? So when does one use the short-hand version and when not? Typically you can use the short-hand version, especially when you are only passing 1 or 2 variables. But you can also pass other arguments; then it's required to use the longer syntax. Like this example:

<%= render partial: "user", locals: {admin: @admin}, as: :user %>
Enter fullscreen mode Exit fullscreen mode

Explicit local variables

Introduced in Rails 7.2. You can set explicit local variables to clearly tells which variables are required.

<%# locals: (name:) -%>
<%= name %>
Enter fullscreen mode Exit fullscreen mode

You can also set a default value. So that if the user has no name set, it renders "Stranger".

<%# locals: (name: "Stranger") -%>
<%= name %>
Enter fullscreen mode Exit fullscreen mode

Local assigns

You can also use “implicit local variables”. Assume the above user_details is used in an admin view.

<div>
<p>
  <%= name %>
</p>

<% local_assigns[:admin?] %>
  <dl>
    <dt>
      Created at:
    </dt>
    <dd>
      <%= user.created_at %>
    </dd>
  </dl>
<% end %>
</div>
Enter fullscreen mode Exit fullscreen mode

You can render the partial like so:

<%= render "user", user: @user, name: "Cam", admin?: true %>
Enter fullscreen mode Exit fullscreen mode

Or omit the admin?: true in non-admin views:

<%= render "user", user: @user, name: "Cam" %>
Enter fullscreen mode Exit fullscreen mode

This is useful, because without the local_assigns, rendering would fail.

Layouts for partials

You can also wrap a partial in a “layout”. This is not to be confused with view layouts stored in app/views/layouts.

<%= render partial: "user", layout: "shared/admin" %>
Enter fullscreen mode Exit fullscreen mode

The admin “layout” is stored at app/views/shared/_admin.html.erb and could look like this:

<div class="admin">
  <%= yield %>
</div>
Enter fullscreen mode Exit fullscreen mode

Render collection

If you want to render a collection of users, you can write the following, for example in app/views/users/index.html.erb:

<%= render partial: "user", collection: @users %>
Enter fullscreen mode Exit fullscreen mode

Or its shorthand variant:

<%= render @users %>
Enter fullscreen mode Exit fullscreen mode

This automatically looks up the _user.html.erb partial in app/views/users, assuming @users is a collection of User's.

It doesn't have to a collection of one and the same Resource though, e.g. User. This too works:

<%= render [User.first, Admin.first, Customer.first, User.second] %>
Enter fullscreen mode Exit fullscreen mode

This is assuming you have those resources, and each has their respective partial, e.g. _user.html.erb, _admin.html.erb and _customer.html.erb.

Empty State

If the collection, e.g. @users is empty, render returns nil. So if you want to render an empty state, that is simple.

<%= render(@users) || "No users yet…" %>
Enter fullscreen mode Exit fullscreen mode

Local variables

You can change the the local variable with a collection too.

<%= render partial: "user", collection: @users, as: :customer %>
Enter fullscreen mode Exit fullscreen mode

In the _user.html.erb partial, you can now use customer.id (or whatever other attribute is available).

You also pass other variables:

<%= render partial: "user", collection: @users, locals: {admin?: true} %>
Enter fullscreen mode Exit fullscreen mode

Counter variables

You can render a counter too: <%%= user_counter %>. The name, the part before the underscore, is derived from the partial name. So **customer.html.erb* would be* <%%= customer_counter %>.

Layout for collections

Similarly to resource partials, you can also pass collections partials the layout option.

<%= render partial: @users, layout: "users/wrapper" %>
Enter fullscreen mode Exit fullscreen mode

Then in app/views/users/_wrapper.html.erb, you could have something like this:

<ul id="users">
  <%= yield %>
</ul>
Enter fullscreen mode Exit fullscreen mode

Spacer template

There is one last option for collection which is spacer templates. It will be inserted between each instance. Use it like so:

<%= render partial: @users, spacer_template: "user/divider" %>
Enter fullscreen mode Exit fullscreen mode

This will load the partial app/views/users/_divider.html.erb.

Bonus: Make partials look like components

If you have used ViewComponent, but can't use them in your current project for whatever reason, here's a way you make your partials quack a bit more like ViewComponent.

Create your components in app/view/components. For example a _card.html.erb.

<section class="card">
  <h4 class="card__header">
    <%= title %>
  </h4>

  <div class="card__body">
    <%= yield %>
  </div>
</section>
Enter fullscreen mode Exit fullscreen mode

Then in your view use it like this:

<%= render layout: "components/card", locals: { title: "User Profile" } do %>
  <p>Just some text for this user</p>
<% end %>
Enter fullscreen mode Exit fullscreen mode

But you probably want a helper to abstract some of that boilerplate away.

# app/helpers/components_helper.rb
module ComponentsHelper
  def component(name, **, &)
    render layout: "components/#{name}", locals: **, &
  end
end
Enter fullscreen mode Exit fullscreen mode

Now you render it like this:

<%= component "card", title: "User Profile" do %>
  <p>Just some text for this user</p>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Pair that with the Explicit local variables and you got a pretty solid component-like set up without any dependencies.

And that are all the things you (didn't) know about Rails' partials.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .