Turbo streams patterns
Skill davidteren/hotwire-codex-skills/skills/turbo-streams-patterns
Unofficial Claude Code skillset inspired by The Rails and Hotwire Codex — 8 skills for building interactive Rails + Hotwire apps (Turbo, Stimulus, Hotwire Native) across web, iOS & Android, each with a runnable checker.
npx -y skills add davidteren/hotwire-codex-skills --skill turbo-streams-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Build targeted Turbo Streams correctly — model broadcasts over Action Cable, custom stream actions, authorized stream channels, and Kredis presence. Use when adding real-time/live updates (chat append, replace a card, toggle a class), writing a custom turbo-stream action, securing who can subscribe to a record's broadcasts, or debugging a stream that does nothing / a custom action that's ignored / broadcasts leaking to the wrong user. The surgical complement to the morph page-refresh model (see turbo-morphing).
SKILL.md
3.3 KB, as published. Nobody here has run it
Targeted Turbo Streams
Surgical DOM updates: broadcast/render specific <turbo-stream> actions against
specific ids. Use this when you need precision (append a chat message, replace one
card, move a class); use turbo-morphing when a whole-page refresh-with-morph is
simpler. Full patterns: references/turbo-streams-guide.md.
When to use
- Live updates: model change → broadcast a partial to subscribers.
- A custom stream action Turbo doesn't ship (e.g.
switch_class). - A record's broadcasts are private — only authorized users may subscribe.
- Debugging: a stream that does nothing, a custom action the client ignores, or broadcasts arriving for the wrong user.
Patterns (and their footguns)
-
Model broadcast:
after_create -> { broadcast_append_later_to parent, target:, partial: }. The_latervariants enqueue a job — need an Active Job backend. View subscribes withturbo_stream_from parent, channel: "FooChannel". -
Authorize the stream (security):
turbo_stream_fromonly signs the stream name — it does NOT prove this user may receive that record's broadcasts. Use a custom channel that authorizes andrejects otherwise (templates/authorized_channel.rb.tmpl). The defaultTurbo::StreamsChannelstreams from any signed name with no per-record check → eavesdropping risk. -
Custom stream action = two halves that MUST match: a JS
StreamActions.Xand a Ruby helper registered viaTurbo::Streams::TagBuilder.prepend(templates/custom_stream_action*.tmpl). One side alone fails silently. -
Stream tags inside a frame response: to patch an element outside the frame you're rendering into, embed
turbo_stream.*tags in the frame's HTML — no separate*.turbo_stream.erbneeded. -
Kredis presence: a
kredis_setof online users (maintained in the channel'ssubscribed/unsubscribed) decides live-append vs also notifying/emailing.
Lint an app
scripts/lint_turbo_streams.sh path/to/rails-app
Flags: custom stream actions wired on only one side; channels that stream_from
without authorizing the subscriber (broadcast eavesdropping — reported as an error);
broadcast_*_later without a job backend; and (response-stream path) a *.turbo_stream.erb
template whose literal slash-pathed partial: resolves to no file (→ MissingTemplate
when that stream renders) or a custom turbo_stream.<action> with no client-side
StreamActions.<action>. Heuristic grep scan (names its ceiling — bare/dynamic partials
and target-id existence aren't resolved). Verified: clean on the Piazza app and on a real
response-template app (miela_app, 14 templates), flags a synthetic leaky-channel +
half-wired + dangling-partial app.