resources/boost/skills/developing-with-turbo-streams/SKILL.md
Develops with Turbo Streams for partial page updates and real-time broadcasting. Activates when using turbo_stream() or turbo_stream_view() helpers; working with stream actions like append, prepend, replace, update, remove, before, after, or refresh; using the Broadcasts trait, broadcastAppend, broadcastPrepend, broadcastReplace, broadcastRemove, or broadcastRefresh methods; listening with x-turbo::stream-from; using the TurboStream facade for handmade broadcasts; combining multiple streams; or when the user mentions Turbo Stream, broadcasting, real-time updates, WebSocket streams, or partial page changes.
npx skillsauth add hotwired-laravel/turbo-laravel developing-with-turbo-streamsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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
turbo_stream() Helper@verbatim
<code-snippet name="turbo_stream helper" lang="php"> // Auto-detect action from context (uses model state: created → append, updated → replace, deleted → remove) return turbo_stream($model); return turbo_stream($model, 'prepend');// Fluent builder (no arguments returns a PendingTurboStreamResponse) return turbo_stream()->append('posts', view('posts._post', ['post' => $post])); return turbo_stream()->prepend('posts', view('posts._post', ['post' => $post])); return turbo_stream()->before(dom_id($post), view('posts._post', ['post' => $newPost])); return turbo_stream()->after(dom_id($post), view('posts._post', ['post' => $newPost])); return turbo_stream()->replace($post, view('posts._post', ['post' => $post])); return turbo_stream()->update($post, view('posts._post', ['post' => $post])); return turbo_stream()->remove($post); return turbo_stream()->refresh(); </code-snippet>
@endverbatim
Use the *All methods or targets() to target multiple elements by CSS selector:
@verbatim
<code-snippet name="Multiple targets" lang="php"> return turbo_stream()->appendAll('.comment', view('comments._comment', ['comment' => $comment])); return turbo_stream()->replaceAll('.notification', view('notifications._notification')); return turbo_stream()->removeAll('.old-item'); </code-snippet>@endverbatim
Use morph() on replace/update to morph content instead of replacing it:
@verbatim
<code-snippet name="Morph" lang="php"> return turbo_stream()->replace($post, view('posts._post', ['post' => $post]))->morph(); </code-snippet>@endverbatim
Pass an array or collection to return multiple stream actions in one response:
@verbatim
<code-snippet name="Multiple streams" lang="php"> return turbo_stream([ turbo_stream()->append('posts', view('posts._post', ['post' => $post])), turbo_stream()->update('post_count', view('posts._count', ['count' => Post::count()])), turbo_stream()->remove('empty_state'), ]); </code-snippet>@endverbatim
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 turbo_stream_view('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
@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
Broadcasts TraitAdd 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
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('some_target'); $comment->broadcastAfter('some_target'); $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
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
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
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.
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
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
Chain broadcastTo() on a Turbo Stream response to also broadcast it:
@verbatim
<code-snippet name="Response broadcasting" lang="php"> turbo_stream() ->append('posts', view('posts._post', ['post' => $post])) ->broadcastTo('general'); </code-snippet>@endverbatim
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
development
Tests Turbo Laravel features in PHPUnit or Pest. Activates when using the InteractsWithTurbo trait; simulating requests with $this->turbo(), $this->fromTurboFrame(), or $this->hotwireNative(); asserting responses with assertTurboStream(), assertNotTurboStream(), assertRedirectRecede(), assertRedirectResume(), or assertRedirectRefresh(); faking broadcasts with TurboStream::fake(), assertBroadcasted(), assertNothingWasBroadcasted(), or assertBroadcastedTimes(); writing feature tests for Turbo Stream responses; or when the user mentions testing Turbo, testing broadcasts, or Turbo test assertions.
development
Develops with Turbo Frames for scoped navigation and lazy loading. Activates when using the x-turbo::frame Blade component or turbo-frame HTML element; working with data-turbo-frame targeting, frame lazy loading via src attribute, or data-turbo-action for URL updates; detecting frame requests with wasFromTurboFrame(); using frame morphing with refresh="morph"; or when the user mentions Turbo Frame, turbo frame, scoped navigation, inline editing, lazy loading frames, or breaking out of a frame with _top.
development
Develops with Turbo Drive for SPA-like navigation. Activates when configuring page morphing with x-turbo::refreshes-with; working with data-turbo, data-turbo-track, data-turbo-permanent, or data-turbo-preload attributes; managing cache control with x-turbo::exempts-page-from-cache, x-turbo::exempts-page-from-preview, or x-turbo::page-requires-reload; enabling view transitions with x-turbo::page-view-transition; handling form redirects with TurboMiddleware; customizing the progress bar; or when the user mentions Turbo Drive, navigation, page morphing, prefetching, or asset tracking.
tools
Basics of developing with Turbo Laravel. Activates when starting a new Turbo Laravel project; using dom_id, dom_class, turbo_stream(), or turbo_stream_view() helpers; working with Blade components like x-turbo::frame, x-turbo::stream, x-turbo::stream-from, or x-turbo::refreshes-with; using @domid, @domclass, @channel, or @turbonative directives; checking wantsTurboStream(), wasFromTurboFrame(), or wasFromHotwireNative() request macros; or when the user mentions Hotwire, Turbo, HTML over the wire, or partial page updates.