Skip to main content

Breadcrumbs

to run in your site's root directory
View template source
file "app/models/concerns/breadcrumbs.rb" do
<<~"_"
module Breadcrumbs
  extend ActiveSupport::Concern

  included do
    helper_method :breadcrumbs
  end

  def breadcrumbs = @breadcrumbs ||= []

  def set_breadcrumbs(*crumbs)
    @breadcrumbs = crumbs.map do |crumb|
      case crumb
      in [ String => name, path ] then Breadcrumb.new(name, path)
      in [ String => name ] then Breadcrumb.new(name)
      in Breadcrumb then crumb
      end
    end
  end

  private

  class Breadcrumb
    attr_reader :name, :path

    def initialize(name, path = nil)
      @name, @path = name, path
    end

    def link? = @path.present?
  end
end

_
end

file "app/views/shared/_breadcrumbs.html.erb" do
<<~"_"
<%= tag.nav "aria-label": "Breadcrumb" do %>
  <ol itemscope itemtype="https://schema.org/BreadcrumbList">
    <% breadcrumbs.each.with_index(1) do |crumb, index| %>
      <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
        <% if crumb.link? %>
          <%= link_to crumb.name, crumb.path, class: "breadcrumb-link", itemprop: "item", "aria-current": (crumb == breadcrumbs.last ? "page" : nil) %>

          <meta itemprop="name" content="<%= crumb.name %>">
        <% else %>
          <span itemprop="name" aria-current="page">
            <%= crumb.name %>
          </span>
        <% end %>
        <meta itemprop="position" content="<%= index %>">

        <% unless crumb == breadcrumbs.last %>
          <span aria-hidden="true">/</span>
        <% end %>
      </li>
    <% end %>
  </ol>
<% end if breadcrumbs.any? %>

_
end


inject_into_class "app/controllers/application_controller.rb", "ApplicationController" do
  "  include Breadcrumbs\n"
end

This headless component provides breadcrumb navigation. It uses a concern to add breadcrumb functionality to controllers and includes proper ARIA attributes and JSON-LD structured data for accessibility and SEO.

The breadcrumbs can be set in controllers using an explicit array-based approach. The system includes a Breadcrumb class that handles both linked and non-linked breadcrumb items.

Usage in controllers:

class Content::PostsController < ApplicationController
  def show
    @resource = Content::Post.find!(params[:id])

    set_breadcrumbs(
      ["Blog", posts_path],
      [@resource.title]
    )
  end
end

In your layout or views:

<%= render "shared/breadcrumbs" %>