Time-ago formatting with Stimulus (no heavy date libs)

For small UX touches like “3 minutes ago”, I don’t want to pull in a giant date library. A Stimulus controller can use Intl.RelativeTimeFormat plus a lightweight difference calculation. The server renders the ISO timestamp (via time_tag), and the cont

Importmap setup for Stimulus controllers (Rails 7 style)

If you’re using importmap, a clean Stimulus setup matters: it keeps controllers discoverable and avoids mystery load order bugs. I pin @hotwired/turbo-rails and @hotwired/stimulus, then use controllers index discovery to register everything. The payof

Active Storage direct upload progress with Stimulus

Direct uploads are great because they keep file traffic away from your Rails dynos, but the default UX is opaque. I attach a Stimulus controller that listens for Active Storage’s direct-upload:* events and updates a progress bar. This keeps the markup

Action Text comment form that works inside a Turbo Frame

Action Text is great, but you need to be deliberate about how you render it in a Turbo-powered UI. I keep the comment form inside a turbo_frame_tag so it can be replaced on validation errors (still 422). On success, I clear the form by replacing the f

Notifications dropdown that live-updates with Turbo Streams

For a notifications dropdown, I keep the list server-rendered and let Turbo Streams keep it fresh. The dropdown content sits in a turbo_frame_tag (so you can also navigate to a full notifications page), and the unread badge is a separate small target

Remove deleted items instantly with turbo_stream.remove

Destructive actions should feel immediate. For delete links inside a list, I return a Turbo Stream that does turbo_stream.remove dom_id(record). That removes the DOM node without re-rendering the rest of the list, which avoids the common “jump” effect

Per-account stream scoping to prevent “cross-tenant” updates

Broadcasting is powerful, but it’s also easy to accidentally leak updates if everyone subscribes to a global stream. My default is to scope streams to a tenant boundary (like Current.account) and a resource. That means turbo_stream_from [current_accou

Avoid caching sensitive pages in Turbo Drive

Turbo’s page cache is great until you have sensitive screens (account settings, billing) where the browser back button might show stale content. I handle this by setting cache-control headers and, for specific actions, disabling Turbo caching via turb

Morphing page refreshes with turbo_refreshes_with

When a page has lots of dynamic pieces but I still want “just refresh it” semantics, I use Turbo’s morphing refreshes. With turbo_refreshes_with method: :morph, Turbo updates the DOM by morphing rather than doing a full replace, which tends to preserv

Broadcast a status badge update on background processing

A lot of Rails apps have records that transition through states: queued, processing, done. With Hotwire, I render a status badge partial and broadcast replacements when the state changes. A background job updates the record, and the model broadcasts a

Live comments with model broadcasts + turbo_stream_from

If a feature is fundamentally “live” (comments, activity), I reach for model broadcasts. Rails can broadcast Turbo Stream fragments on after_create_commit, and the UI subscribes with turbo_stream_from. The beauty is that it’s still server-rendered HTM

Copy-to-clipboard button with Stimulus

Copy buttons are everywhere (invite links, API keys, CLI commands). With Stimulus, I keep it tiny and resilient: use navigator.clipboard.writeText when available, and fall back to selecting a hidden input for older browsers. I also provide immediate f