Animating list items with LiveView streams

I have a list (LiveView stream) which on mount is populated, it is then subsequently prepended to via PubSub using stream.insert. When prepending a new item to the list I want to nicely animate it’s entry.

I’m able to add this animation using phx-mounted and JS.transition, but my problem is the initial lifecycle mounts as it causes this animation to flash for the server rendered list on the initial mounts. I only want this animation to occur when a new item comes in after the initial mount.

My question is if anyone has figured out an elegant way to handle this. My first thought is counting renders and conditionally applying the transition but was hoping for a more declarative way. The markup is as follows…

<ul id="jobs" phx-update="stream" class="p-4 flex flex-col gap-4">
  <li
    :for={{id, j} <- @streams.jobs}
    id={id}
    class="border-2 p-2 h-fit flex flex-row justify-between overflow-hidden"
    phx-mounted={
        JS.transition(
           {"first:ease-in duration-500", "first:opacity-0 first:p-0 first:h-0", "first:opacity-100"},
           time: 500
         )
    }
  >
    <p>Name: <%= j.name %></p>
    <p>ID: <%= j.job_id %></p>
    <p>Status: <%= j.state %></p>
  </li>
</ul>

You could do something like this:

<li
    :for={{id, j} <- @streams.jobs}
    id={id}
    class="border-2 p-2 h-fit flex flex-row justify-between overflow-hidden"
    phx-mounted={
        @insert && 
        JS.transition(
           {"first:ease-in duration-500", "first:opacity-0 first:p-0 first:h-0", "first:opacity-100"},
           time: 500
         )
    }
  >

Where you assign insert to false on initial mount and true before the first insert.

3 Likes

This is essentially what I ended up doing. Have a flag for that list, flip the flag appropriately to enable/disable animation. No render counting required as I was initially thinking.