Rendering view components with Turbo Stream broadcasts03 Nov 2021
View components, via Github’s view_component gem, are growing in popularity in the Rails community but until recently, view components and Turbo Stream broadcasts didn’t play well together. This made using both view components and Turbo Streams in the same application clunky and a little frustrating.
While there were ways to get components working with streams, thanks to a recent addition to turbo-rails, rendering view components from Turbo Streams now works seamlessly out of the box.
To demonstrate how to connect these two powerful tools together, we’ll be building a very simple Rails application that allows users to manage a list of Spies.
Each spy in the list of spies will be rendered using a view component, and when new spies are added to the database, the newly created spy record will be rendered and broadcast via a callback in the spy model.
When we’re finished, it’ll work like this:
Beautiful, I know.
This article assumes that you’re comfortable building Rails applications. You won’t need any previous experience with Turbo Streams or view components to follow along.
If you want to skip right to the end the complete code for this article can be found on Github.
Let’s start building!
We’ll be working from a fresh Rails application. I’m working off of the latest Rails 7 release (alpha2 at the time of this writing) but everything in this tutorial will work fine on Rails 6.1 too.
If you want to follow along, you can run the below commands from your terminal, or you can clone this Github repo and skip ahead to the Building the spy component section.
If you’re using Rails 6.1 instead of 7, you’ll also need to install
Whichever route you choose, you’re ready to move on to the next section once you have
turbo-rails installed in your application and a
Spy resource created. Once you’re setup, start up your server with
rails s and head to http://localhost:3000/spies.
Building the spy component
To use view components, we need to create a component to render a spy object. We can create new components with the built-in generator
This generator will create both a
spy_component.rb file and a
spy_component.html.erb file in the
Since we’re building a very simple component,
spy_component.rb is good to go out of the box. For reference, it should look like this:
spy_component.html.erb contains placeholder content provided by the generator right now, so let’s update it to render information about each spy:
This is just regular old
erb, essentially a copy of the default content of
_spy.html.erb generated by the Rails scaffold generator we ran during application setup.
With the spy component in place, we now need to actually use it. To do that, we’ll update the spies index view to use the our new component. Update
spies/index.html.erb like this:
Here we’re using collection rendering to loop through each
@spies and render each with
We’ve now got our list of spies rendering using view components — next up we’ll add Turbo Stream broadcasts so that newly created spies are appended to the list automatically.
Add Turbo Stream broadcasts
First, we need to ensure that visitors to the spies index page are subscribed to the appropriate turbo stream channel.
To do this, we can use the
turbo_stream_from helper from turbo-rails. Update
spies/index.html.erb like this:
There are two important pieces here. First, we added the
turbo_stream_from helper, with
spies as the name.
This helper creates a
<turbo-cable-stream-source> in the rendered HTML with a signed-stream-name that looks something like this:
Next, the div wrapping the list of spy components has an id of
spies. This id must match the target of the Turbo Stream broadcast, which defaults to the plural name of the model we are broadcasting from. If our wrapper div doesn’t have an id, the broadcast we add next will fail.
With the stream subscription added to the view, the last step is to add a model callback in
models/spy.rb to broadcast newly created spies on the
spy.rb like this:
Here we’re using the new
html option added to Turbo Stream broadcast methods to render the SpyComponent instead of rendering a partial.
Note that using
ApplicationController.render to render a view_component isn’t officially sanctioned by the
view_component folks are actively discussing an official way to add support for stream broadcasts, which you can track on this issue.
With the model broadcast in place, we’re ready to test our Turbo Stream-enabled view components.
A note for Rails 7 users: If you’re on Rails 7 alpha2 (the latest release at the time of this writing) an issue exists that will prevent the broadcast from working because of a disabled session error. This issue is entirely unrelated to view components but it will break our broadcast all the same.
This issue will be fixed in the next Rails release, but until then you can prevent the issue by updating development.rb with this line:
To see it action, refresh the index page, and then open a new tab to http://localhost:3000/spies/new, create a new spy, and see that the newly created spy is automatically appended to the list of spies automatically.
Today we looked at a technique to render View Components in Turbo Stream broadcasts, leveraging the recently added ability to render html in Turbo Stream broadcasts.
This article’s focus is on Turbo Stream broadcasts + view_component, so we didn’t dive deep into how Turbo Streams work or how to take full advantage of the real power of view_component.
To dig deeper, you might find these resources helpful starting points:
- The view_component documentation is excellent, and worth reviewing in detail to understand more about how you can use view components
- Encapsulating Ruby on Rails views from Github’s blog is a nice introduction to the work behind the ViewComponent library and includes links to see how Github uses ViewComponents in their application
- The Turbo Rails source code. If you’re serious about using Turbo in your Rails application, thoroughly reviewing the source and accompanying comments is highly recommend.
- The Turbo handbook is the best place to start if you’re new to Turbo and want to understand the basics of Streams, Frames, and Drive
That’s all for today. As always, thanks for reading!