ux

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

A common UI is a list on the left and a details panel (drawer) on the right. With Turbo Frames, each list item link can target a details frame. Clicking an item swaps the drawer content while leaving the list intact. The server still renders HTML, so

Frontend: copy-to-clipboard with fallback

Copying code is a core interaction in a snippets app, and it needs to work reliably across browsers. The modern API is navigator.clipboard.writeText, but it requires HTTPS and permissions in some contexts. I try it first, and if it fails I fall back t

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

Turbo Drive records visits in browser history. For some interactions (like typing through search results), you don’t want every click to create a new history entry. Turbo supports data-turbo-action='replace' to replace the current history entry instea

Stimulus: copy-to-clipboard with fallback + selection

Copy buttons are deceptively tricky across browsers. This Stimulus controller uses the Clipboard API when available, falls back to execCommand, and provides a hook for “Copied!” UI.

Custom turbo_stream action tag: highlight an updated element

Turbo gives you a handful of built-in stream actions (append, replace, remove), but sometimes you want behavior like “replace then highlight”. You can implement this as a custom action: send a turbo-stream with action='highlight' and include a templat

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

Turbo Frames: Inline edit that swaps form <-> row

Inline editing works best when the frame boundary is small and stable (one row). Render the display state inside a frame, then link to edit inside the same frame. Submit swaps it back. This keeps controllers simple and UI snappy.

API error shape that frontend can rely on

Inconsistent error responses cause death-by-a-thousand-cuts on the frontend. If one endpoint returns a string, another returns nested objects, and a third throws HTML, you end up with messy UI conditionals everywhere. I use a small set of stable error

Tabs UI using Turbo Frames (no client router)

Tabs often turn into a mini-SPA. Instead, I treat each tab as a URL and load its content into a turbo_frame_tag named tab_content. Clicking a tab link targets that frame. This gives you browser history, deep linking, and sharable URLs, while keeping t

Custom confirm dialog with Stimulus (better than window.confirm)

data-turbo-confirm uses the browser confirm dialog, which is functional but not pretty. For a more polished app, I replace it with a Stimulus controller that intercepts clicks, shows a custom modal, and only proceeds if the user confirms. Turbo makes

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

A command palette feels like a SPA feature, but you can do it Hotwire-first: place a turbo_frame_tag 'modal' in the layout and load the palette HTML into it. A small Stimulus controller listens for meta+k and navigates the modal frame to /palette. The

Server-rendered skeleton UI for slow frames

For slow endpoints, I like a skeleton placeholder rather than a blank area. Turbo Frames make this easy: render a “loading skeleton” inside the frame tag as the initial content, then set src so Turbo fetches the real content. When the response arrives