# Turbo Streams

As mentioned earlier, out of everything Turbo provides, it's Turbo Streams that benefits the most from a back-end integration.

Turbo Drive will get your pages behaving like an SPA and Turbo Frames will allow you to have a finer grained control of chunks of your page instead of replacing the entire page when a form is submitted or a link is clicked.

However, sometimes you want to update _multiple_ parts of your page at the same time. For instance, after a form submission to create a comment, you may want to append the comment to the comment's list and also update the comment's count in the page. You may achieve that with Turbo Streams.

Form submissions will get annotated by Turbo with a `Accept: text/vnd.turbo-stream.html` header (besides the other normal Content Types). This will indicate to your back-end that you can return a Turbo Stream response for that form submission if you want to.

Here's an example of a route handler detecting and returning a Turbo Stream response to a form submission:

```php
Route::post('posts/{post}/comments', function (Post $post) {
    $comment = $post->comments()->create(/** params */);

    if (request()->wantsTurboStream()) {
        return response()->turboStream($comment);
    }

    return back();
});
```

The `request()->wantsTurboStream()` macro added to the request will check if the request accepts Turbo Stream and return `true` or `false` accordingly.

The `response()->turboStream()` macro may be used to generate streams, but you may also use the `turbo_stream()` helper function. From now on, the docs will be using the helper function, but you may use either one of those. Using the function, this example would be:

```php
Route::post('posts/{post}/comments', function (Post $post) {
    $comment = $post->comments()->create(/** params */);

    if (request()->wantsTurboStream()) {
        return turbo_stream($comment);
    }

    return back();
});
```

Here's what the HTML response will look like:

```html
<turbo-stream action="append" target="comments">
    <template>
        <div id="comment_123">
            <p>Hello, World</p>
        </div>
    </template>
</turbo-stream>
```

Most of these things were "guessed" based on the [naming conventions](#conventions) we talked about earlier. But you can override most things, like so:

```php
return turbo_stream($comment)->target('post_comments');
```

Although it's handy to pass the model instance to the `turbo_stream()` function - which will be used to decide the default values of the Turbo Stream response based on the model's current state, sometimes you may want to build a Turbo Stream response manually, which can be achieved like so:

```php
return turbo_stream()
    ->target('comments')
    ->action('append')
    ->view('comments._comment', ['comment' => $comment]);
```

There are 7 _actions_ in Turbo Streams. They are:

* `append` & `prepend`: to add the elements in the target element at the top or at the bottom of its contents, respectively
* `before` & `after`: to add the elements next to the target element before or after, respectively
* `replace`: will replace the existing element entirely with the contents of the `template` tag in the Turbo Stream
* `update`: will keep the target and only replace the contents of it with the contents of the `template` tag in the Turbo Stream
* `remove`: will remove the element. This one doesn't need a `<template>` tag. It accepts either an instance of a Model or the DOM ID of the element to be removed as a string.

Which means you will find shorthand methods for them all, like:

```php
turbo_stream()->append($comment);
turbo_stream()->prepend($comment);
turbo_stream()->before($comment);
turbo_stream()->after($comment);
turbo_stream()->replace($comment);
turbo_stream()->update($comment);
turbo_stream()->remove($comment);
```

For these shorthand stream builders, you may pass an instance of an Eloquent model, which will be used to figure out things like `target`, `action`, and the `view` partial as well as the view data passed to them.

Alternativelly, you may also pass strings to the shorthand stream builders, which will be used as the target, and an optional content string, which will be rendered instead of a partial, for instance:

```php
turbo_stream()->append('statuses', __('Comment was successfully created!'));
```

The optional content parameter expects either a string, a view instance, or an instance of Laravel's `Illuminate\Support\HtmlString`, so you could do something like:

```php
turbo_stream()->append('some_dom_id', view('greetings', ['name' => 'Tester']));
```

Or more explicitly by passing an instance of the `HtmlString` as content:

```php
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\HtmlString;

turbo_stream()->append('statuses', new HtmlString(
    Blade::render('<div>Hello, {{ $name }}</div>', ['name' => 'Tony'])
));
```

Which will result in a Turbo Stream like this:

```html
<turbo-stream target="statuses" action="append">
    <template>
        <div>Hello, Tony</div>
    </template>
</turbo-stream>
```

For both the `before` and `after` methods you need additional calls to specify the view template you want to insert, since the given model/string will only be used to specify the target, something like:

```php
turbo_stream()
    ->before($comment)
    ->view('comments._flash_message', [
        'message' => __('Comment was created!'),
    ]);
```

Just like the other shorthand stream builders, you may also pass an option content string or `HtmlString` instance to the `before` and `after` shorthands. When doing that, you don't need to specify the view section.

```php
turbo_stream()->before($comment, __('Oh, hey!'));
```

You can read more about Turbo Streams in the [Turbo Handbook](https://turbo.hotwired.dev/handbook/streams).

The shorthand methods return a pending object for the response which you can chain and override everything you want before it's rendered:

```php
return turbo_stream()
    ->append($comment)
    ->view('comments._comment_card', ['comment' => $comment]);
```

As mentioned earlier, passing a model to the `turbo_stream()` helper will pre-fill the pending response object with some defaults based on the model's state.

It will build a `remove` Turbo Stream if the model was deleted (or if it is trashed - in case it's a Soft Deleted model), an `append` if the model was recently created (which you can override the action as the second parameter), a `replace` if the model was just updated (you can also override the action as the second parameter.) Here's how overriding would look like:

```php
return turbo_stream($comment, 'append');
```


You may also [target multiple elements](https://turbo.hotwired.dev/reference/streams#targeting-multiple-elements) using CSS classes using the `xAll` methods:

```php
turbo_stream()->appendAll('.comment', 'Some content');
turbo_stream()->prependAll('.comment', 'Some content');
turbo_stream()->updateAll('.comment', 'Some content');
turbo_stream()->removeAll('.comment');
turbo_stream()->beforeAll('.comment', 'Some content');
turbo_stream()->afterAll('.comment', 'Some content');
```

With the exception of the `removeAll` method, all these `xAll` accept as the second parameter a string of inline content, an instance of a View (which you may create using the `view()` function provided by Laravel), or an instance of the `HtmlSafe` class.

When creating Turbo Stream using the builders, you may also specify the CSS class using the `targets()` (plural) method instead of the singular version:

```php
return turbo_stream()
    ->targets('.comment')
    ->action('append')
    ->view('comments._comment', ['comment' => $comment]);
```

## Turbo Streams Combo

You may combine multiple Turbo Stream responses in a single one like so:

```php
return turbo_stream([
    turbo_stream()
        ->append($comment)
        ->target(dom_id($comment->post, 'comments')),
    turbo_stream()
        ->update(dom_id($comment->post, 'comments_count'), view('posts._comments_count', ['post' => $comment->post])),
]);
```

Although this is a valid option, it might feel like too much work for a controller. If that's the case, use [Custom Turbo Stream Views](#custom-turbo-stream-views).

## Custom Turbo Stream Views

If you're not using the model partial [convention](#conventions) or if you have some more complex Turbo Stream constructs to build, you may use the `response()->turboStreamView()` version instead and specify your own Blade view where Turbo Streams will be created. This is what that looks like:

```php
return response()->turboStreamView('comments.turbo.created_stream', [
    'comment' => $comment,
]);
```

Similarly, you may prefer to use the helper function `turbo_stream_view()`:

```php
return turbo_stream_view('comments.turbo.created_stream', [
    'comment' => $comment,
]);
```

And here's an example of a more complex custom Turbo Stream view:

```blade
@include('layouts.turbo.flash_stream')

<turbo-stream target="@domid($comment->post, 'comments')" action="append">
    <template>
        @include('comments._comment', ['comment' => $comment])
    </template>
</turbo-stream>
```

Remember, these are Blade views, so you have the full power of Blade at your hands. In this example, we're including a shared Turbo Stream partial which could append any flash messages we may have. That `layouts.turbo.flash_stream` could look like this:

```blade
@if (session()->has('status'))
<turbo-stream target="notice" action="append">
    <template>
        @include('layouts._flash')
    </template>
</turbo-stream>
@endif
```

Similar to the `<x-turbo-frame>` Blade component, there's also a `<x-turbo-stream>` Blade component that can simplify things quite a bit. It has the same convention of figureing out the DOM ID of the target when you're passing a model instance or an array as the `<x-turbo-frame>` component applied to the `target` attribute here. When using the component version, there's also no need to specify the template wrapper for the Turbo Stream tag, as that will be added by the component itself. So, the same example would look something like this:

```blade
@include('layouts.turbo.flash_stream')

<x-turbo-stream :target="[$comment->post, 'comments']" action="append">
    @include('comments._comment', ['comment' => $comment])
</x-turbo-stream>
```

I hope you can see how powerful this can be to reusing views.

[Continue to Broadcasting...](/docs/{{version}}/broadcasting)
