Rails' Partial Features You (didn't) Know

railsdesigner

Rails Designer

Posted on November 14, 2024

Rails' Partial Features You (didn't) Know

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.

💖 💪 🙅 🚩
railsdesigner
Rails Designer

Posted on November 14, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related