Turbo Stream responses that spark joy
Tags: rails, hotwire, turbo, ux
Published: 2022-12-15 10:27:45

[Turbo Streams](https://turbo.hotwired.dev/handbook/streams), either fired via a controller action or over websockets are an incredibly easy but powerful way to make your apps more responsive and interactive. 

At [Spinal](https://spinalcms.com/), the micro-SaaS I've founded I'm all-in on this “boring Rails stack”. It helps me ship features quickly without a lot of overhead. 

There is one technique that I haven't seen talked about before in the Rails/Hotwire sphere, but I use it often to spark joy when using my SaaS app: **add an animation when inserting elements into the DOM via Turbo Streams**. 

It's simple, really. Have an optional `animated: false` attribute on the ViewComponent or the partial that you insert. Let's see some of these in action and then go over each step needed to replicate this.


## Fade in a new icon

Customers can change the icon for their content type. Upon save, the icon is shown on the page with a subtle fade- and zoom-in animation. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/omv7wlhmv7yfu7tnj07k.gif)


## Animate content rows

When changing the order of the content, each row is fading in one row at a time. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkfy56hnn5xaizskqquw.gif)


## Animate content cells individually

When customising which columns are visible in the content overview, they are shown top to bottom and from left to right with a fade-in animation. Notice how they are not fading in all at once, but one at the time. Sparking all the more joy. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvfpxn3ixzrla19ugotf.gif)


It's important to note that these animations do not get triggered on regular page visits—which would get annoying pretty quickly, but only when triggered via Turbo Streams. So how do you this?

_Feel free to [sign up for Spinal](https://spinalcms.com/) to see these little animations in 4K instead of a crappy gif._ 


## How to spark joy yourself

Let's walk through all the critical parts on how to do this. I'm using [ViewComponent](https://github.com/viewcomponent/view_component), but the same principles apply if you use Rails' partials.


### ViewComponent class

```ruby
class ContentRowComponent < ApplicationComponent
  def initialize(content_row:, content_row_counter:, animate: false)
    @content_row = content_row
    @animate = animate
    @content_row_counter = content_row_counter
  end

# … other methods omitted for brevity

  def row_css
    class_names(
      "content-row",
      {"animate-fadeIn": @animate}
    )
  end
end
```

First initialise the `@content_row_counter` and the `@animate` instance variable (set it to `false` by default). The `content_row_counter` is a variable coming from [ViewComponent](https://viewcomponent.org/guide/collections.html#collection-counter).

Then a `row_css` method where all the css for the `<li />`-element is defined. Here the most interesting is the `class_names()` helper from [ActionView](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-token_list). It only appends the `animate-fadeIn` class if `@animate` is `true`.


### ViewComponent erb

```ruby
<%= tag.li @content_row, class: row_css, style: "animation-delay: #{@content_row_counter * 70}ms;" %>
```

`row_css` is referenced here. Also a delay for the animation is set which simply is `@content_row_counter` multiplied by 70(ms). This then makes each row fade in one after another. 


### Turbo Stream response

```ruby
<%= turbo_stream.update "content-rows" do %>
  <%= render ContentRowComponent.with_collection(@content, animate: true) %>
<% end %>
```

And finally, where the magic happens. Upon saving, this turbo_stream method is called. It updates the element with the id of `content-rows`. And only here is `animate` set to `true`. 

You are, of course, not limited to a boolean `animate` variable. You could as well pass the animation name as a string and like that add more advanced animation option based on your needs. For example: `animate: fadeInSlow`. 

And that's the bare essentials of sparking joy to your Turbo Streams. If you do apply this technique, do share the results with. Love to see it.