zac

zac

I’ve been digging into why Phoenix LiveView’s (forms, especially) sometimes “break” and become non-responsive.

It was a bit of a deep dive and I couldn’t find a really good synopsis anywhere, so… documenting my findings here and looking for any added advice, suggestions, and commentary. Mostly, putting this out there for discussion & to help anyone else looking into similar problems. (If there are good resources on this problem, I’d love a pointer to them).

Scenario

  1. I have a relative simply LiveView form, and it does a little bit of dynamic rendering (offering hints and whatnot while filling out the form).
  2. Sometimes, the page becomes totally non-responsive.
  3. There’s nothing in the logs or server output to indicate failure. In fact, the logs all say “everything’s great!” right up to the point of failure… then, silence.

Cause

It probably comes down to either:

  1. The DOM getting confused due to dynamic updates (LiveView patches).
  2. Something else. :slight_smile:
    (I’m putting something else because I’m sure there are other situations that will break your LiveView connection, but this is a dive into dynamic DOM updates causing a specific problem).

Diagnosis & solution

This is a classic LiveView issue related to DOM patching conflicts when elements are conditionally rendered. Here’s what’s likely happening:

The Problem:

Given a line similar to this:

<p :if={@message} class={@class}>{raw(@message)}</p>

When the <p> tag transitions from “not rendered” (e.g. :if={nil} is falsy) to “rendered” (becomes truthy), LiveView’s DOM diffing algorithm can get confused, especially if:

  1. There are form inputs nearby that have focus or pending changes
  2. The newly rendered element affects the DOM structure in a way that conflicts with pending updates
  3. There’s a race condition between the form validation and the DOM update

Common Causes:

  1. Focus conflicts: If a user is typing in a form field when the DOM structure changes
  2. Event timing: The phx-change event fires, updates the socket, but the DOM patch conflicts with the browser’s form state
  3. Input validation loops: Rapid state changes causing conflicting patches

Debugging Steps:

Online resources suggested all of the following, but none were useful. I never caught an error in the browser console, and the server-side logs/output looked just fine. But, clearly, the socket broke because all events stopped flowing.

  1. Check browser console for specific LiveView errors during the disconnect
  2. Add temporary logging around the state transition (this was informative, but ultimately did not point out the root cause).
  3. Check for rapid fire events: Look for multiple phx-change events firing in quick succession

Common Solutions:

  1. Use stable DOM structure … this is the simple win that I ended up going with. It makes for a more stable DOM structure, hence less confusion in DOM rendering, avoiding the problem:
<!-- Instead of conditional rendering -->
<p class={if @message, do: "@class", else: "hidden"}>
  <!-- Always render, just hide/show -->
  {raw(@message)}
</p>
  1. Add a small delay to debounce rapid changes (I did not try this):
<.input phx-change="validate" phx-debounce="300" />
  1. Use phx-update="ignore" on problematic elements (this prevents the socket from breaking, but it also prevents the inner <p> tag from updating… so, better insofar as avoiding a break, but not useful for dynamically rendered elements):
<div phx-update="ignore" id="stable-hint">
  <!-- Oops... no more dynamic changes -->
  <p :if={@message} class={@class}>{raw(@message)}</p>
</div>
  1. Separate the state update from the rendering (another one that I didn’t try, but I’d love to hear from anyone on whether this strategy works or not… and, compared to #1, what’s regarded as “best practice”):
# In handle_event
socket = 
  socket
  |> assign(show_hint: stereotype_id != nil)
  |> assign(form: updated_form)

So, in my case, the root cause was that making DOM elements appear/disappear interfered with LiveView’s ability to cleanly patch the DOM, especially if there are active form interactions happening simultaneously.

Lesson learned: If I’m patching the DOM, try to keep it stable (don’t wholesale remove elements, instead, hide them). If that’s not possible, then… look into some variation on strategies 3 and 4.

First 6 of 6 Posts Switch mode

steffend

steffend

Phoenix Core Team

Although your AI clearly sounds confident when it says

This is a classic LiveView issue related to DOM patching conflicts when elements are conditionally rendered.

I would strongly object to this. I guess it could happen that you lose focus when morphdom patches the DOM and your form inputs don’t have stable id attributes, but a form (or whole LiveView) should never become non responsive. When you are seeing this issue, are you in development (that is, on localhost)? If not, you might be missing LiveView’s duplicate ID warnings. Usually such weird behavior happens when you accidentally have duplicate element IDs in your DOM. Another thing you may try is to look for elements in the DOM with phx-change-loading class or data-phx-ref-lock attribute.

In any case, please try to create [a minimal example](phoenix_live_view/.github/single-file-samples/main.exs at main · phoenixframework/phoenix_live_view · GitHub) that reproduces the unresponsive behavior you’re seeing and open up an issue in the repo. It is definitely not supposed to happen under normal circumstances!

zac

zac OP

Thanks so much for the quick response!

This issue has been driving me a bit crazy on-and-off for the past week. It seemed like I had narrowed it down to a likely root cause… meh.

When you are seeing this issue, are you in development (that is, on localhost)?

Yes (I’m on localhost, watching logs / browser logs / phx.server output). I’m not getting anything that indicates a problem. No output, at all – the only working links on the page that are plain href, anything socket driven is dead.

you might be missing LiveView’s duplicate ID warnings

Haven’t seen any…

other thing you may try is to look for elements in the DOM with phx-change-loading class or data-phx-ref-lock attribute

Will watch for this. Haven’t seen it – but, I wasn’t looking either.

I’ll see what I can do about a repro. A bit non-trival (it’s an Ash Framework app, will have to disentangle a few things and likely reproduce the event chain somewhat to get it [not] working…).

zachdaniel

zachdaniel

Creator of Ash

The duplicate id things could possibly come from AshPhoenix.Form? Not sure.

But also @steffend is a smart cookie I’m sure he could handle some Ash resources being a part of the reproduction :laughing:

zachdaniel

zachdaniel

Creator of Ash

https://github.com/wojtekmach/mix_install_examples/blob/main/ash.exs

You can combine this single file Ash app with that single file Phoenix app.

steffend

steffend

Phoenix Core Team

It’s not an issue at all if it includes Ash. Also unlikely that it’s duplicate IDs when there’s no logs in development.

Thank you for investing the time @zac!

zac

zac OP

Thanks @steffend & @zachdaniel … will try to get you something this weekend, or (if my repro reveals the root cause) at least post my mistakes! :smiley:

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement