Thank you Steffen! ![]()
It’s a huuge improvement! Now developers can get these huge diff size gains without much effort.
I was looking into this, and there’s these two possible improvements:
- when :key is present, payload still increases linearly with length of the collection due to always sending a full list of LiveComponent ids. I think it helps when reordering, but is it necessary when order doesn’t change?

- memory overhead is much higher than I thought it would be. I might be measuring it in a wrong way, just I have a feeling LiveComponents were not really optimized for memory overhead.
Without keys:
| Array length | Heap size | Payload size |
|---|---|---|
| 0 | 987 | 78 (empty) |
| 1 | 987 | 112 |
| 10 | 987 | 238 |
| 100 | 2586 | 1497 |
| 1000 | 17731 | 14098 |
With keys:
| Array length | Heap size | Payload size |
|---|---|---|
| 0 | 987 | 78 (empty) |
| 1 | 987 | 138 |
| 10 | 4185 | 175 |
| 100 | 28690 | 625 |
| 1000 | 121536 | 6029 |
Both these issues are less important when using more complex items, since I assume memory overhead for LiveComponent record is constant (didn’t measure it though). But now let’s remember LiveComponent usage will be much much easier thanks to :key syntax, so optimizing it might be more important? Still - can’t shake that feeling it could be done almost without memory overhead. Like, compare previous assign to the current one without using LiveComponents at all. I’ve tried doing it by myself and failed, engine code is not the easiest one to grasp ![]()
This is a simple livebook I’ve used:
Mix.install([
{:phoenix_playground, "~> 0.1.6"},
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view", branch: "main", override: true},
])
defmodule DemoLive do
use Phoenix.LiveView
require Logger
@list_size 1000
def mount(_params, _session, socket) do
list = Enum.map(1..@list_size, fn i -> %{index: i, value: random_value()} end)
report_memory_usage()
{:ok, assign(socket, list: list)}
end
def render(assigns) do
~H"""
<button phx-click="randomize">Randomize</button>
<div :for={item <- @list} :key={item.index}>
{item.value} {item.value + 1} {item.value + 2}
</div>
"""
end
def handle_event("randomize", _params, socket) do
index = Enum.random(1..@list_size)
new_list = put_in(socket.assigns.list, [Access.at(index - 1), :value], random_value())
report_memory_usage()
{:noreply, assign(socket, list: new_list)}
end
defp random_value() do
Enum.random([1, 2, 3])
end
defp report_memory_usage() do
:erlang.garbage_collect()
Logger.info("Heap size: #{Process.info(self())[:total_heap_size]}")
end
end
PhoenixPlayground.start(live: DemoLive)























