Skip to main content

Breadcrumbs Component for Rails

Breadcrumbs component for Rails. Shows the current location within a hierarchy, with collapsible segments for long paths and custom separators.

Usage

<%= render "components/breadcrumbs" do %>
  <%= render "components/breadcrumbs/list" do %>
    <%= render "components/breadcrumbs/item" do %>
      <%= render "components/breadcrumbs/link", href: "/" do %>Home<% end %>
    <% end %>
    <%= render "components/breadcrumbs/separator" %>
    <%= render "components/breadcrumbs/item" do %>
      <%= render "components/breadcrumbs/link", href: "/components" do %>Components<% end %>
    <% end %>
    <%= render "components/breadcrumbs/separator" %>
    <%= render "components/breadcrumbs/item" do %>
      <%= render "components/breadcrumbs/page" do %>Breadcrumbs<% end %>
    <% end %>
  <% end %>
<% end %>

Using Helper

<%= breadcrumbs({"Home" => root_path, "Users" => users_path}, "John Doe") %>

The hash maps link text to path, in order. The second argument is the current page (rendered with breadcrumbs/page); omit it and the last link ends the trail.

Examples

With Icons

<%= render "components/breadcrumbs/link", href: "/" do %>
  <%= icon_for(:home, class: "size-4") %>
  Home
<% end %>

Custom Separators

<%= render "components/breadcrumbs/separator", icon: :slash %>
<%= render "components/breadcrumbs/separator", icon: :arrow_right %>

Compact Size and Text Separators

The root picks up two styling hooks through data:. size: :sm tightens the gap and drops the text to text-xs; separator: replaces every separator's icon with a text glyph.

<%= render "components/breadcrumbs", data: { size: :sm, separator: :slash } do %>
  <%# list, items and separators as usual %>
<% end %>
data-separator Glyph
slash /
dot •
arrow →

With Ellipsis

<%= render "components/breadcrumbs/item" do %>
  <%= render "components/breadcrumbs/ellipsis" %>
<% end %>

Responsive

<%= responsive_breadcrumbs(
  {"Home" => "/", "Docs" => "/docs", "Components" => "/components"},
  "Breadcrumbs"
) %>

Items collapse only when they do not fit, and come back when they do. The controller measures the trail against its container and hides middle items one at a time, from the first one inward, until the row fits — so the ellipsis always stands for the items directly behind it. Widen the container and the hidden items return; there is no one-way collapse and no item-count threshold.

The container is what is measured, not the window, so a breadcrumb inside a collapsing sidebar or a resizing panel re-fits when that panel moves. If a single current-page title is too long to help by collapsing anything, it truncates with an ellipsis as a last resort.

The ellipsis dropdown

When items are collapsed, the … becomes a button. Clicking it opens a menu listing the hidden items as links, so nothing in the trail becomes unreachable. It renders in the top layer as a popover — light dismiss and Escape work natively — and needs no markup from you beyond responsive_breadcrumbs.

API Reference

Breadcrumbs

Parameter Type Default Description
responsive Boolean false Collapse middle items when the trail does not fit its container, and restore them when it does
collapse_after Integer — Deprecated, ignored. Removed in 0.8.0. It collapsed on item count without consulting available width, which also collapsed a trail with room to spare; space-based collapsing works now
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

The root renders <nav aria-label="Breadcrumb">. Pass "aria-label": (or aria: { label: }) to replace the label, e.g. for a translated page. With responsive: true it also attaches the breadcrumb Stimulus controller (see below).

<%= render "components/breadcrumbs", "aria-label": t(".breadcrumb") do %>
  <!-- list -->
<% end %>

Breadcrumbs List

Parameter Type Default Description
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Breadcrumbs Item

Parameter Type Default Description
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Renders an <li>.

Breadcrumbs Link

Parameter Type Default Description
href String required Link destination
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Breadcrumbs Page

Renders <span role="link" aria-current="page" aria-disabled="true"> around the block.

Parameter Type Default Description
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Breadcrumbs Separator

Parameter Type Default Description
icon Symbol :chevron_right Icon name passed to icon_for, or :custom to render the block instead
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Breadcrumbs Ellipsis

Parameter Type Default Description
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

breadcrumbs/separator renders its own <li role="presentation" aria-hidden="true">, so place it directly in the list — not inside an item. breadcrumbs/ellipsis renders a <span> with an ellipsis icon and an sr-only "More" label; wrap it in an item.

Helper Methods

Method Description
breadcrumbs(links = {}, current_page = nil, css_classes: "") Renders a static trail from a { text => path } hash
responsive_breadcrumbs(links = {}, current_page = nil, css_classes: "", collapse_after: nil) Same, with responsive: true, the ellipsis item and the Stimulus targets wired in. collapse_after is deprecated and ignored

Stimulus Controller

responsive: true adds data-controller="breadcrumb" to the root. responsive_breadcrumbs renders all of the targets for you; when you compose the partials by hand you must add them yourself.

Target Element
item A collapsible middle breadcrumbs/item (data: { breadcrumb_target: "item" })
ellipsis The breadcrumbs/item wrapping breadcrumbs/ellipsis, rendered with css_classes: "hidden"
ellipsisSeparator The separator after the ellipsis item, also starting hidden

The controller has no values and no public actions. It re-fits on container resize (ResizeObserver), on font load and on turbo:morph, and removes the overflow popover on turbo:before-cache. The separator directly after each item target is hidden along with it.