erb
<div class="layout">
  <ul class="product-list">
    <% @products.each do |product| %>
      <li class="product-row">
        <%= link_to product.name,
              product_path(product),

Turbo Frames: “details” panel that lazy-loads on click

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

  def create
    @like = @post.likes.find_or_create_by(user: current_user)

Turbo Streams: optimistic UI for likes with disable-on-submit

rails turbo stimulus
by codesnips 3 tabs
javascript
let installed = false;

function notify(message, level = "error") {
  document.dispatchEvent(
    new CustomEvent("flash:show", { detail: { message, level } })
  );

Turbo Drive lifecycle: attach global error handler

rails turbo hotwire
by codesnips 3 tabs
ruby
class PostsController < ApplicationController
  def index
    @posts = Post.includes(:author)
                 .order(created_at: :desc)
                 .page(params[:page])
                 .per(10)

Turbo Frames: infinite scroll with lazy-loading frame

rails turbo hotwire
by codesnips 4 tabs
erb
<%# private stream: turbo signs the serialized record name %>
<%= turbo_stream_from current_user %>

<section class="notifications">
  <h1>Notifications</h1>

Turbo Streams + authorization: signed per-user stream name

rails turbo hotwire
by codesnips 3 tabs
javascript
import { StreamActions } from "@hotwired/turbo"

StreamActions.flash = function () {
  const container = document.getElementById("flashes")
  if (!container) return

Turbo Streams: custom action for flash messages

rails turbo hotwire
by codesnips 3 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["field"]

  connect() {

Stimulus: autofocus the first invalid field after Turbo update

rails stimulus hotwire
by codesnips 3 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["wrapper", "template", "anchor"]

  add(event) {

Stimulus: nested fields add/remove without re-rendering

rails stimulus hotwire
by codesnips 4 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["source"]

  async copy() {

Stimulus: copy-to-clipboard with fallback + selection

rails stimulus hotwire
by Sarah Chen 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
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
<!DOCTYPE html>
<html>
  <head>
    <title>Contacts</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

Turbo Frames modal: load, submit, then close via stream

rails turbo hotwire
by codesnips 4 tabs