erb

erb
<body>
  <%= render 'shared/navbar' %>

  <main class="mx-auto max-w-5xl p-6">
    <%= yield %>
  </main>

Turbo Frame modal that renders server HTML

rails hotwire turbo
by Henry Kim 3 tabs
erb
<h1><%= @post.title %></h1>
<p><%= @post.author_name %></p>

<%# Only sanitized rich text should be rendered as HTML %>
<div class="prose"><%= sanitize(@post.body_html, tags: %w[p a ul ol li strong em code], attributes: %w[href]) %></div>

Cross site scripting defense with output encoding and CSP

xss content-security-policy owasp
by Kai Nakamura 3 tabs
ruby
class AdminController < ApplicationController
  before_action :authenticate_admin!
  before_action :disable_turbo_cache, only: %i[dashboard moderation_queue]

  helper_method :turbo_cache_control_tag

Turbo Drive: disable caching on volatile admin pages

rails turbo hotwire
by codesnips 3 tabs
erb
<div data-controller="upload-progress">
  <%= form.file_field :avatar, direct_upload: true, data: { action: 'change->upload-progress#start' } %>

  <div class="mt-2 h-2 w-full rounded bg-gray-200">
    <div data-upload-progress-target="bar" class="h-2 w-0 rounded bg-blue-500"></div>
  </div>

Active Storage direct upload progress with Stimulus

rails hotwire stimulus
by Henry Kim 2 tabs
ruby
class Export < ApplicationRecord
  enum status: { queued: 0, processing: 1, completed: 2, failed: 3 }

  after_update_commit -> {
    broadcast_replace_later_to(
      self,

Broadcast a status badge update on background processing

rails hotwire turbo
by Henry Kim 2 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["input"]
  static values = { wait: { type: Number, default: 300 }, url: String }

Stimulus: debounced search that plays nicely with Turbo

rails stimulus hotwire
by codesnips 3 tabs
erb
<header id="navbar" data-turbo-permanent>
  <%= render 'shared/navbar' %>
</header>

Keep navbar state across Turbo navigations with data-turbo-permanent

rails hotwire turbo
by Henry Kim 2 tabs
erb
<div id="item_form">
  <%= render 'form', item: Item.new %>
</div>

<div id="items">
  <%= render @items %>

Inline create form that prepends into a list with Turbo Streams

rails hotwire turbo
by Henry Kim 2 tabs
ruby
class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.build(comment_params)
    @comment.author = current_user

Turbo Streams for real-time list updates

hotwire turbo turbo-streams
by Jordan Lee 2 tabs
erb
<aside class="w-64">
  <% @projects.each do |project| %>
    <%= link_to project.name, project_path(project), data: { turbo_frame: 'main' }, class: 'block py-1' %>
  <% end %>
</aside>

Scoped navigation inside a sidebar with Turbo Frames

rails hotwire turbo
by Henry Kim 2 tabs
erb
<div class="grid grid-cols-3 gap-6">
  <div class="col-span-1">
    <% @items.each do |item| %>
      <%= link_to item.title, item_path(item), data: { turbo_frame: 'details' }, class: 'block border-b py-2' %>
    <% end %>
  </div>

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

rails hotwire turbo
by Henry Kim 2 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["checkbox", "selectAll", "count", "submit"]
  static values = { selectedIds: Array }

Stimulus: bulk selection + Turbo batch action

rails stimulus hotwire
by codesnips 4 tabs