SKILL.md
Turbo Streams
Turbo Streams let you change any part of the page using eight actions: append, prepend, replace, update, remove, before, after, and refresh. They work as HTTP responses (after form submissions) and as real-time broadcasts over WebSocket.
HTTP Turbo Streams
Detecting Turbo Stream Requests
Check if the request accepts Turbo Stream responses before returning them:
@verbatim
<code-snippet name="Detecting" lang="php"> public function store(Request $request) { $post = Post::create($request->validated());
if ($request->wantsTurboStream()) { return turbo_stream($post); }
return redirect()->route('posts.show', $post); } </code-snippet>
@endverbatim
The turbo_stream() Helper
@verbatim
<code-snippet name="turbostream helper" lang="php"> // Auto-detect action from context (uses model state: created → append, updated → replace, deleted → remove) return turbostream($model); return turbo_stream($model, 'prepend');
// Fluent builder (no arguments returns a PendingTurboStreamResponse) return turbostream()->append('posts', view('posts.post', ['post' => $post])); return turbostream()->prepend('posts', view('posts.post', ['post' => $post])); return turbostream()->before(domid($post), view('posts.post', ['post' => $newPost])); return turbostream()->after(domid($post), view('posts.post', ['post' => $newPost])); return turbostream()->replace($post, view('posts.post', ['post' => $post])); return turbostream()->update($post, view('posts.post', ['post' => $post])); return turbostream()->remove($post); return turbostream()->refresh(); </code-snippet>
@endverbatim
Targeting Multiple Elements
Use the *All methods or targets() to target multiple elements by CSS selector:
@verbatim
<code-snippet name="Multiple targets" lang="php"> return turbostream()->appendAll('.comment', view('comments.comment', ['comment' => $comment])); return turbostream()->replaceAll('.notification', view('notifications.notification')); return turbo_stream()->removeAll('.old-item'); </code-snippet>
@endverbatim
Morph Method
Use morph() on replace/update to morph content instead of replacing it:
@verbatim
<code-snippet name="Morph" lang="php"> return turbostream()->replace($post, view('posts.post', ['post' => $post]))->morph(); </code-snippet>
@endverbatim
Combining Multiple Streams
Pass an array or collection to return multiple stream actions in one response:
@verbatim
<code-snippet name="Multiple streams" lang="php"> return turbostream([ turbostream()->append('posts', view('posts.post', ['post' => $post])), turbostream()->update('postcount', view('posts.count', ['count' => Post::count()])), turbostream()->remove('emptystate'), ]); </code-snippet>
@endverbatim
Turbo Stream Views
Render a full Blade view with the Turbo Stream content type. Useful for complex multi-stream responses:
@verbatim
<code-snippet name="Stream view" lang="php"> return turbostreamview('posts.turbo.created', ['post' => $post]); </code-snippet>
<code-snippet name="Stream view template" lang="blade"> {{-- resources/views/posts/turbo/created.blade.php --}} <x-turbo::stream action="append" target="posts"> @include('posts._post', ['post' => $post]) </x-turbo::stream>
<x-turbo::stream action="update" target="post_count"> {{ Post::count() }} posts </x-turbo::stream> </code-snippet>
@endverbatim
The Stream Blade Component
@verbatim
<code-snippet name="Blade component" lang="blade"> {{-- Single target by ID --}} <x-turbo::stream action="append" target="messages"> <div id="message_1">My new message!</div> </x-turbo::stream>
{{-- Target by model (auto-generates DOM ID) --}} <x-turbo::stream action="replace" :target="$post"> @include('posts._post', ['post' => $post]) </x-turbo::stream>
{{-- Multiple targets by CSS selector --}} <x-turbo::stream action="remove" targets=".notification" /> </code-snippet>
@endverbatim
Broadcasting (Real-Time Streams)
The Broadcasts Trait
Add the Broadcasts trait to your Eloquent model:
@verbatim
<code-snippet name="Broadcasts trait" lang="php"> use HotwiredLaravel\TurboLaravel\Models\Broadcasts;
class Post extends Model { use Broadcasts; } </code-snippet>
@endverbatim
Manual Broadcasting
Call broadcast methods directly on a model instance:
@verbatim
<code-snippet name="Manual broadcasting" lang="php"> $comment->broadcastAppend(); $comment->broadcastPrepend(); $comment->broadcastReplace(); $comment->broadcastUpdate(); $comment->broadcastRemove(); $comment->broadcastBefore('sometarget'); $comment->broadcastAfter('sometarget'); $comment->broadcastRefresh();
// Broadcast only to other users (exclude current user) $comment->broadcastAppend()->toOthers();
// Queue the broadcast for async processing $comment->broadcastAppend()->later(); </code-snippet>
@endverbatim
Directed Broadcasting
Broadcast to a specific model's channel:
@verbatim
<code-snippet name="Directed broadcasting" lang="php"> // Broadcast to the post's channel instead of the comment's own channel $comment->broadcastAppendTo($post); $comment->broadcastPrependTo($post); $comment->broadcastReplaceTo($post); $comment->broadcastUpdateTo($post); $comment->broadcastRemoveTo($post); $comment->broadcastRefreshTo($post); </code-snippet>
@endverbatim
Automatic Broadcasting
Enable automatic broadcasts on model lifecycle events:
@verbatim
<code-snippet name="Auto broadcasting" lang="php"> class Comment extends Model { use Broadcasts;
// Enable auto-broadcasting (broadcasts on create, update, delete) protected $broadcasts = true;
// Customize insert action (default is 'append') protected $broadcasts = ['insertsBy' => 'prepend'];
// Specify which model's channel to broadcast to protected $broadcastsTo = 'post';
// Or define dynamically public function broadcastsTo() { return $this->post; } } </code-snippet>
@endverbatim
Page Refresh Broadcasting
Instead of granular stream actions, broadcast a page refresh signal:
@verbatim
<code-snippet name="Refresh broadcasting" lang="php"> class Post extends Model { use Broadcasts;
// Auto-broadcast page refreshes on model changes protected $broadcastsRefreshes = true; } </code-snippet>
@endverbatim
This works best with <x-turbo::refreshes-with method="morph" scroll="preserve" /> in the layout.
Listening for Broadcasts
Use the <x-turbo::stream-from> component in your Blade views to subscribe to a channel:
@verbatim
<code-snippet name="Listening" lang="blade"> {{-- Private channel (default) — requires channel auth --}} <x-turbo::stream-from :source="$post" />
{{-- Public channel — no auth needed --}} <x-turbo::stream-from :source="$post" type="public" /> </code-snippet>
@endverbatim
Define the channel authorization in routes/channels.php:
@verbatim
<code-snippet name="Channel auth" lang="php"> use App\Models\Post;
Broadcast::channel(Post::class, function ($user, Post $post) { return $user->belongsToTeam($post->team); }); </code-snippet>
@endverbatim
Handmade Broadcasts (via Facade)
Use the TurboStream facade for broadcasts not tied to a model:
@verbatim
<code-snippet name="Facade broadcasting" lang="php"> use HotwiredLaravel\TurboLaravel\Facades\TurboStream;
TurboStream::broadcastAppend( content: view('notifications._notification', ['notification' => $notification]), target: 'notifications', channel: 'general', );
TurboStream::broadcastRemove(target: 'notification_1', channel: 'general'); TurboStream::broadcastRefresh(channel: 'general'); </code-snippet>
@endverbatim
Broadcasting from Response Builder
Chain broadcastTo() on a Turbo Stream response to also broadcast it:
@verbatim
<code-snippet name="Response broadcasting" lang="php"> turbostream() ->append('posts', view('posts.post', ['post' => $post])) ->broadcastTo('general'); </code-snippet>
@endverbatim
Global Broadcast Scope
Exclude the current user from all broadcasts in a request:
@verbatim
<code-snippet name="Broadcast to others" lang="php"> use HotwiredLaravel\TurboLaravel\Facades\Turbo;
// In a controller or middleware Turbo::broadcastToOthers();
// Anywhere Turbo::broadcastToOthers(function () { // Turbo Streams broadcasted here will not be delivered to the current user... }); </code-snippet>
@endverbatim