erb
<div data-controller="disclosure">
  <button type="button" data-action="disclosure#toggle" aria-expanded="false" class="font-medium">
    Advanced
  </button>
  <div data-disclosure-target="panel" class="mt-2 hidden">
    <%= render 'advanced_filters' %>

Collapse/expand UI with Stimulus that survives Turbo swaps

rails hotwire stimulus
by Henry Kim 2 tabs
erb
<div id="save_state" class="text-xs text-gray-500">Not saved yet</div>

Turbo Streams: inline “saved” indicator that updates on autosave

rails hotwire turbo
by Henry Kim 2 tabs
erb
<form data-controller="query-sync" data-action="change->query-sync#apply">
  <select name="status" class="rounded border p-2">
    <option value="">Any</option>
    <option value="open">Open</option>
    <option value="closed">Closed</option>
  </select>

Filter UI that syncs query params via Stimulus (no front-end router)

rails hotwire stimulus
by Henry Kim 2 tabs
erb
<%= turbo_frame_tag 'modal' %>

Keyboard shortcut “command palette” modal (Hotwire-first)

rails hotwire stimulus
by Henry Kim 3 tabs
erb
<div class="dashboard">
  <header class="dashboard__header">
    <h1>Overview</h1>
    <p class="muted">Real-time metrics update as they load.</p>
  </header>

Server-rendered skeleton UI for slow frames

rails hotwire turbo
by codesnips 4 tabs
ruby
class ProductsController < ApplicationController
  PER_PAGE = 24

  def index
    @page = [params[:page].to_i, 1].max
    @sort = %w[name price recent].include?(params[:sort]) ? params[:sort] : "recent"

Use data-turbo-action to control history (advance vs replace)

rails hotwire turbo
by codesnips 3 tabs
erb
<div class="orders-layout" data-controller="drawer">
  <table class="orders">
    <tbody>
      <% @orders.each do |order| %>
        <tr>
          <td><%= order.reference %></td>

Turbo Frames: inline “details drawer” without a SPA router

rails hotwire turbo
by codesnips 4 tabs
ruby
class LikesController < ApplicationController
  before_action :set_post

  def create
    @like = current_user.likes.create!(post: @post)
    respond(liked: true)

Turbo Streams: swap a button state and counter in one response

rails hotwire turbo
by codesnips 4 tabs
ruby
class SessionsController < ApplicationController
  def new
    @user = User.new
  end

  def create

Turbo-Location header: redirect a frame submission to a new URL

rails hotwire turbo
by codesnips 3 tabs
ruby
class DocumentsController < ApplicationController
  before_action :set_document, only: :destroy

  def destroy
    @document.discard!

Undo delete with a Turbo Stream “restore” action

rails hotwire turbo-streams
by codesnips 4 tabs
erb
<%= tag.div id: dom_id(comment), class: "comment", data: { controller: "comment" } do %>
  <div class="comment__body">
    <%= comment.body %>
  </div>

  <footer class="comment__meta">

Use dom_id everywhere to keep Turbo replacements deterministic

rails hotwire turbo
by codesnips 4 tabs
erb
<%= turbo_stream_from :comments %>

<section class="comments">
  <h2>Discussion</h2>

  <%= turbo_frame_tag "new_comment" do %>

Inline create form that prepends into a list with Turbo Streams

rails hotwire turbo
by codesnips 4 tabs