Out Now JavaScript for Rails Developers

Rails' Partial Features You (Didn't) Know

3d rendering of toy blocks

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" %>

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

You can also omit the partial keyword.

<%= render "application/navigation" %>

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

Local Variables

You can pass variables like so.

<%= render partial: "user", locals: {user: @user}  %>

Again, this short-hand can also be used:

<%= render "user", user: @user %>

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

<%= render @user %>

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 %>

Explicit local variables

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

<%# locals: (name:) -%>
<%= name %>

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

<%# locals: (name: "Stranger") -%>
<%= name %>

Local assigns

Then there is “implicit local variables”. Assume the above user partial 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>

You can render the partial like so:

<%= render "user", user: @user, name: "Cam", admin?: true %>

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

<%= render "user", user: @user, name: "Cam" %>

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" %>

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

<div class="admin">
  <%= yield %>
</div>

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 %>

Or its shorthand variant:

<%= render @users %>

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 be a collection of one and the same Resource though, e.g. User. This too works:

<%= render [User.first, Admin.first, Customer.first, User.second] %>

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.

You are not limited to ActiveRecord models though. You can pass along any class as long as it responds to to_partial_path (this is what is used for all convential lookups, powered by ActionPack). Example:

# app/models/login.rb
class Login
  def to_partial_path
    "logins/login" # or whatever partial you want
  end
end

Or include ActiveModel::Conversion to get it for free:

# app/models/login.rb
class Login
 include ActiveModel::Conversion
end

Login.new.to_partial_path
# => "logins/login"

The latter option adds the option for you. See the source code on how this works (it adds not just to_partial_path).

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…" %>

For a more versatile solution, I suggest this technique using CSS though.

Local variables

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

<%= render partial: "user", collection: @users, as: :customer %>

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} %>

Caching

For long Rails made caching simple. Similar for collections:

<%= render partial: "user", collection: users, cached: true %>

This will cache each user’s partial. Make sure to run bin/rails dev:cache in development to try it.

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 the layout option to collection-partials.

<%= render partial: @users, layout: "users/wrapper" %>

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

<ul id="users">
  <%= yield %>
</ul>

Spacer template

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

<%= render partial: @users, spacer_template: "user/divider" %>

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>

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 %>

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

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

Now you render it like this:

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

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. Which one was new to you?

Published at . Last updated at . Have suggestions or improvements on this content? Do reach out.

Join Rails developers getting deep dives on modern front-end patterns—from Hotwire, Tailwind CSS and ViewComponent. Fresh insights from Rails Designer HQ.

UI components Library for Ruby on Rails apps

$ 149 one-time
payment

View components
  • One-time Payment

  • Access to the Entire Library

  • Built using ViewComponent

  • Designed with Tailwind CSS

  • Enhanced with Hotwire