Phoenix.LiveView.JS transitions gotcha

I just spent some time figuring out why all my LiveView click events are so slow on local server. Maybe this info will be useful to someone. Or maybe I misunderstand something or not aware of some option or solution for this - in that case please point me in right direction.

So I noticed that phx-change events update page instantly, but phx-click events on same page have noticeable delay. Functions which handle those events are very similar, just update some assigns and do a push_patch. Long story short it turns out updates coming from server were delayed by transition started by phx-click-away on totally unrelated element (in my case animation also had no visible effect on page).

phx-click events were instantly sent and handled by server, results were returned to client instantly, but displaying results on client-side were delayed until transition is over. This is just my case with phx-click-away, but I can imagine this causing unexpected delay in other circumstances. For example disappearing menu/dropdown.

Here is how to reproduce the effect:

mix archive.install hex phx_new 1.7.14 (optional)
mix phx.new transition_delay --no-ecto

Replace “/” in router.ex:

live "/", PageLive

Add page_live.ex:

defmodule TransitionDelayWeb.PageLive do
  use TransitionDelayWeb, :live_view

  alias Phoenix.LiveView.JS

  def mount(_params, _session, socket) do
    {:ok, assign(socket, clicks: 0)}
  end

  def handle_event("click", _params, socket) do
    {:noreply, assign(socket, clicks: socket.assigns.clicks + 1)}
  end

  def render(assigns) do
    ~H"""
    <div>
      <div phx-click-away={
        JS.transition({"duration-1000", "opacity-100", "opacity-0"},
          time: 1000
        )
      }>
        Click away to hide me
      </div>
      <button phx-click="click">Click me</button>
      <p>Clicked: <%= @clicks %></p>
    </div>
    """
  end
end

Run and click on “Click me” button. Normally it would update counter instantly but because of fading text above counter will update with 1 second delay.

2 Likes

Thanks for the tip!

1 Like

Yep, that’s what I complained about here:

2 Likes

I tried to search here and on github but somehow I didn’t find it. This is indeed same issue, and it was easily noticeable with 300ms transition. It will be less noticeable if goes into production, but 300ms will become min effective response time for server interactions and it will effect ux and most likely some seo metrics.

There’s also another issue with this. Namely, it is a major obstacle to LiveView/AlpineJS integration.

Post LV 0.20.1 I’ve had to replace eachphx-mounted={JS.show( ..)} with a corresponding Alpine-based transition.

There is a :blocking option in JS.transition which defaults to true. Try setting it to false. It might help.

5 Likes

Thank you very much for pointing this out! Looks like new option in rc6 so I wasn’t aware of it. blocking: false indeed fixes the problem if one is aware of it.

1 Like