Petit Poucet 🥖
Breadcrumbs for Rails, the simple way.
A lightweight, zero-dependency breadcrumbs gem for Ruby on Rails. Simple DSL, controller inheritance, and full view customization — help your users find their way back, one pebble at a time.
Features
- 🪶 Zero dependencies — only Rails required
- 🎯 Simple DSL — declare breadcrumbs in one line
- 🔗 Controller inheritance — child controllers inherit parent breadcrumbs
- 🎨 Flexible rendering — use the built-in helper or full custom views
- ⚡ Lazy evaluation — lambdas for dynamic names and paths
- 🎛️ Action filtering —
onlyandexceptoptions for fine control - 📦 Grouping — apply filters to multiple breadcrumbs at once
Installation
gem "petit_poucet"Usage
Controller
class ApplicationController < ActionController::Base
breadcrumb -> { t("home") }, :root_path
end
class ArticlesController < ApplicationController
breadcrumb "Articles", :articles_path
breadcrumb -> { @article.title }, only: [:show, :edit, :update]
def show
@article = Article.find(params[:id])
end
endDSL Options
# Static
breadcrumb "Dashboard", :dashboard_path
# Dynamic name
breadcrumb -> { t("breadcrumbs.home") }, :root_path
# Dynamic path
breadcrumb "Profile", -> { user_path(current_user) }
# Action filtering
breadcrumb "Edit", :edit_article_path, only: [:edit, :update]
breadcrumb "Details", :articles_path, except: :index
# Action filtering without path
breadcrumb "Current", only: :show
# No link (current page)
breadcrumb -> { @article.title }Runtime Breadcrumbs
You can also add breadcrumbs at runtime in actions or before_action callbacks:
def show
@article = Article.find(params[:id])
breadcrumb @article.title, article_path(@article)
breadcrumb "Details" # No link
endCombining Declarative and Runtime
Use declarative breadcrumbs for general structure and runtime for action-specific additions:
class ArticlesController < ApplicationController
breadcrumb "Articles", :articles_path
def show
@article = Article.find(params[:id])
breadcrumb @article.title, article_path(@article)
breadcrumb @article.category.name, category_path(@article.category) if @article.category
end
def edit
@article = Article.find(params[:id])
breadcrumb @article.title, article_path(@article)
breadcrumb "Edit"
end
end
# show → Articles → My Article → Tech (if category exists)
# edit → Articles → My Article → EditView Rendering
Simple (built-in helper)
<%= render_breadcrumbs %>
<%# => <nav class="breadcrumb"><a href="/">Home</a> / Articles / My Article</nav> %>
<%= render_breadcrumbs(class: "my-breadcrumb", separator: " > ") %>Custom (full control)
<nav aria-label="Breadcrumb">
<ol>
<% breadcrumb_trail do |crumb| %>
<li>
<% if crumb.current? %>
<%= crumb.name %>
<% else %>
<%= link_to crumb.name, crumb.path %>
<% end %>
</li>
<% end %>
</ol>
</nav>CrumbPresenter
| Method | Description |
|---|---|
name |
Display text |
path |
URL (can be nil) |
current? |
true if last breadcrumb |
to_s |
Returns name
|
Clearing Inherited Breadcrumbs
class AdminController < ApplicationController
clear_breadcrumbs
breadcrumb "Admin", :admin_root_path
endConditional Clearing
Clear inherited breadcrumbs only for specific actions:
class Admin::ArticlesController < AdminController
# Start fresh on :new and :create actions only
clear_breadcrumbs only: %i[new create]
breadcrumb "New Article", only: %i[new create]
end
class PublicController < ApplicationController
# Clear inherited breadcrumbs on all actions except :index
clear_breadcrumbs except: :index
breadcrumb "Public Section"
endGrouping Breadcrumbs
Use breadcrumb_group to apply the same only/except filters to multiple breadcrumbs:
class ArticlesController < ApplicationController
# These breadcrumbs only appear on :edit and :update
breadcrumb_group only: %i[edit update] do
breadcrumb "Articles", :articles_path
breadcrumb -> { @article.title }, -> { article_path(@article) }
breadcrumb "Edit"
end
endNested Groups
Groups can be nested. Options are merged intelligently:
-
:onlyuses intersection (more restrictive) -
:exceptuses union (cumulative exclusions)
class ArticlesController < ApplicationController
breadcrumb_group except: :index do
breadcrumb "Articles", :articles_path
breadcrumb_group only: %i[edit update] do
# Appears on :edit and :update, but NOT on :index
breadcrumb -> { @article.title }, -> { article_path(@article) }
end
end
endOverriding Group Options
Individual breadcrumbs can override group options:
breadcrumb_group only: %i[show edit update] do
breadcrumb "Details", :article_path # Appears on :show, :edit, :update
breadcrumb "Edit Form", only: :edit # Appears only on :edit (intersection)
endCombining Groups with Regular Breadcrumbs
class ArticlesController < ApplicationController
breadcrumb "Home", :root_path # Always
breadcrumb_group only: %i[edit update] do
breadcrumb "Edit Section", :edit_article_path # Only on :edit, :update
end
breadcrumb -> { @article.title }, except: :index # Except :index
endComplete Example
A typical CRUD controller setup:
class ArticlesController < ApplicationController
breadcrumb "Articles", :articles_path
# Show article title on :show, :edit, :update, :destroy
breadcrumb_group only: %i[show edit update destroy] do
breadcrumb -> { @article.title }, -> { article_path(@article) }
end
# Add "Edit" crumb on :edit and :update
breadcrumb "Edit", only: %i[edit update]
# Different breadcrumb for new articles
breadcrumb "New Article", only: %i[new create]
def show
@article = Article.find(params[:id])
end
# ...
endResult:
| Action | Breadcrumbs |
|---|---|
| index | Articles |
| show | Articles / My Article |
| edit | Articles / My Article / Edit |
| new | Articles / New Article |
API Reference
Controller Class Methods
| Method | Description |
|---|---|
breadcrumb(name, path = nil, **options) |
Declare a breadcrumb |
clear_breadcrumbs(**options) |
Clear inherited breadcrumbs |
breadcrumb_group(**options, &block) |
Group breadcrumbs with shared options |
Options
| Option | Description | Example |
|---|---|---|
:only |
Show only on these actions | only: %i[edit update] |
:except |
Show on all actions except these | except: :index |
Dynamic Values
| Type | Name | Path |
|---|---|---|
| String | "Home" |
"/path" |
| Symbol | :method_name |
:path_helper |
| Proc | -> { @model.title } |
-> { model_path(@model) } |
Requirements
- Ruby >= 3.0
- Rails >= 7.0
License
MIT
About the Name
Le petit Pouçet les laissoit crier, sçachant bien par où il reviendroit à la maison ; car en marchant il avoit laissé tomber le long du chemin les petits cailloux blancs qu'il avoit dans ses poches.
— Charles Perrault, Le Petit Poucet (1697)
Named after the French fairy tale "Le Petit Poucet" (Hop-o'-My-Thumb), where a clever boy leaves a trail of pebbles to find his way home.