mononym

mononym

I’m noticing quite a bit of slowdown when appending messages and replacing elements on a LiveView webpage which has an ever-increasing number of chat messages being displayed.

Each incoming message is wrapped in a p tag, and there are multiple spans within the text inside that p tag. As I start getting up to several hundred messages, replacing the input/form with a new one by using a new changeset starts to take some significant time, and the insertion of messages into the window starts creeping up to ~50ms just because of a reflow being triggered. The process of fully handling the form/input replacement can work its way up to 500ms or more.

Using some manual editing, I took those hundreds of messages and modified them so they were all part of a single huge p element on the page. Same number of text characters and ‘lines’, but just in one p tag instead of hundreds. This dropped the time to swap out the input field and insert new messages considerably, to a level which (while noticed) is at least acceptable. If I remove all the messages and have a blank slate, the insertion time is sub 15ms.

Does anyone have any tips/tricks they use with having a large amount of text and doing a lot of appending? Do I need to create some javascript hooks that go in and shuffle around the text LiveView pushes to the page so that each incoming message ends up in a single p element with all the other messages? Should I just accept having even less text available and only allow ~100 messages (this really isn’t workable for me honestly) to live at a time? Should I switch to channels and cut out LiveView dealing with sending messages to and from the client, leaving LiveView to handle other parts of the UI that aren’t chat related?

Showing Posts 1 to 10

Exadra37

Exadra37

You may want to take a look to the video from Chris McCord where he builds a Twitter clone and see how he is appending tweets, instead of triggering a full render of all tweets in the page:

Unfortunately he doesn’t provide the link for a Github repo, but if you take a look to the topic Where is the chirp repo for the real-time Twitter clone in 15 minutes by Chris McCord? you will find several links to different repos and discussion about it.

mononym

mononym OP

I am already using temporary assigns and appending to the DOM. The update happens exactly as in that video with small chunks of html being sent over and the update happening very quick…when there are a few items on the screen like in that demo.

My issue isn’t triggering a rerender of all messages from the perspective of the server, but that slipping these tiny chunks of HTML into the DOM is triggering a browser redraw in some cases and it’s causing huge spikes in apparent lag due to the browser freezing up while it scans the DOM to recalculate stuff.

Now what’s a bit odd is that while replacing the form in place takes a huge amount of time, I can perform a click interaction elsewhere on the same page that inserts and removes elements and it takes almost no time at all. So the redraw being triggered and it taking a while is happening only in a couple cases, apparently based on the relative position of those elements to the huge list of messages I am inserting as that last quick case happens “to the side” of where the messages are displayed.

This may very well be a browser limit thing I can’t get around and I have to get fancy with some tricks like I mentioned in the original post (using JS to shift incoming messages into the existing single “mega message”) but I lose some flexibility that way.

Part of me is almost wondering if I’m going to need to keep the messages server side and allow the user to “scan” through their messages up and down using hotkeys and swapping out what messages I show based on state. Much how many client side JS libraries handle large collections to keep performance good.

Exadra37

Exadra37

So I am new to Live View and still in the learning process, but I was wondering if you are rendering each message as a live_component, using socket update instead of assign, and at same time adding phx-update="prepend" to the for loop rendering the messages?

Maybe you could share your code or repo so that the experts here can help you better then me.

mononym

mononym OP

I’m not rendering a series of live components as I don’t need that complexity for the messages. However, other than that I’m doing almost what I’ve seen done elsewhere. However, rather than injecting values into an html fragment, I instead build up the HTML elsewhere and send it over to LiveView to be injected raw, rather than having values interpolated in. So my message might actually be: "<p>Goodbye <span class="emotion">cruel</span> <span class="place">world!</span><p>

Skipping the rest of the layout because it’s not important, but this is in my client.leex file:

  <div class="w-3/5 flex flex-col">
    <div id="story" phx-hook="Story" phx-update="append" class="border-4 border-r-0 min-w-full flex-1 flex flex-col overflow-y-scroll p-2 font-extrabold font-story">
      <%= for message <- @messages do %>
        <%= raw(message) %>
      <% end %>
    </div>
    <div class="h-8 flex flex-col">
      <%= form_for @input, "#", [phx_submit: :submit_input, class: "flex flex-col flex-1"], fn _f -> %>
        <%= text_input :input, :content, phx_hook: "Input", phx_blur: "stop_typing", placeholder: "Enter Commands Here...", class: "min-w-full rounded-lg resize-y min-h-full", autocomplete: "off" %>
      <% end %>
    </div>
  </div>

The return for my mount:

    {:ok,
     assign(socket,
       character_id: session["character_id"],
       input: Input.new(),
       messages: [],
       client_data: client_data
     ), temporary_assigns: [messages: []]}
i-n-g-m-a-r

i-n-g-m-a-r

Are you sure this is the culprit?
Are you also doing other LiveView stuff?
For example, once I had some component rendering an inspect of some state and it was killing performance.
In the mean time I was investigating the wrong LiveView for problems.

Exadra37

Exadra37

It looks like you are defeating the programming model of Live View here that is built to avoid precisely this “complexity”.

From my understanding in a for-loop you will want to use a live_component so that live view can properly track the changes and be able to do its amazing diff to only send the values that have changed to the client, not the html. Live view engine separates static from dynamic parts, but you are defeating that in your code, or if you prefer you are working against the program model of live view.

mononym

mononym OP

The culprit is a large number of individual DOM nodes on the webpage causing a very long redraw operation by the browser. ~50ms just to insert a single message into the div containing all of the p elements containing the individual messages.

Appending a message is pretty quick, relatively speaking, but swapping out the form/input by creating a new changeset (which triggers LiveView to send over a new form HTML fragment) takes over 500ms when we’re talking hundreds of messages. This happens because on my webpage the design is that the window containing messages sits directly above the text input field, so replacing the form means the browser takes a look at everything above it and it forces a redraw calculation of the form, the window containing the messages, and the hundreds of messages within the window.

I have a completely different LiveView interaction on the same webpage that happens in another ‘column’, and it’s still quick even with hundreds of messages. So the culprit is absolutely browser redraws/calculations when elements are replaced/added to the page in such a way that things have to be recalculated.

This is why I asked how people are handling having large numbers of messages when using LiveView and appending/updating the page, because that’s where the issue comes from.

LostKobrakai

LostKobrakai

To be fair this is also a problem with pure client side libraries and the reason why e.g. many client side frameworks have components, which render just the visible rows of lists instead of every available item.

mononym

mononym OP

It looks like you are defeating the programming model of Live View here that is built to avoid precisely this “complexity”.

I think you are misunderstanding LiveView, what it is, and what it is built on. The templating system EEX which LiveView is built on has been around since much earlier Erlang. All of the templates you see, whether the normal EEX templates or the LEEX templates, end up being processed and turned into strings after which is where LiveView comes in.

Using EEX absolutely does make it easier to write things like loops and create complex interfaces, but that has nothing to do with LiveView and is not a functional feature of LiveView at all. LiveView works just fine if you take EEX out of the equation entirely and write everything by hand.

From my understanding in a for-loop you will want to use a live_component so that live view can properly track the changes and be able to do its amazing diff to only send the values that have changed to the client, not the html. Live view engine separates static from dynamic parts, but you are defeating that in your code, or if you prefer you are working against the program model of live view.

For loops have nothing to do with Live Components. A Live Component is simply a way to encapsulate state (in some cases) and display logic in an easily reusable way, much like templates. That demo only used LiveComponents because it was effectively writing a bunch of cards to the webpage, each with like buttons and other interactivity. If you don’t need to encapsulate state, because you’re just writing out some text, you don’t need to use Live Components. That’s a red herring.

LiveView is about replacing JS as much as possible to provide richer UI experiences and to provide for a whole new set of interoperability between client and server out of the box while keeping as much server side as possible to reduce context change, development context switching, and improve the security and integrity of data. All of the templates, for loops, and everything else that makes building the actual HTML easy is outside LiveView’s bailiwick.

mononym

mononym OP

Which I called out explicitly in the initial post. And hence my asking, in the first post, how people are approaching it and even gave the idea of simulating that using hotkeys and “scrolling” through the messages that way.

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews