We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Use `temporary_assigns` for large lists in LiveView
almirsarajcic
Sending entire lists over the socket on every LiveView update kills performance. Mark list assigns as temporary to send them once and drop them from socket state.
# ❌ FULL LIST - Sent on every update (slow and memory-heavy)
defmodule ProductsLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
products = load_products() # 1000 products
{:ok, assign(socket, products: products, filter: :all)}
end
def handle_event("toggle_filter", _params, socket) do
# BUG: Entire products list sent over socket again!
{:noreply, update(socket, :filter, fn f -> toggle(f) end)}
end
end
# ✅ TEMPORARY ASSIGNS - Sent once, then dropped from memory
defmodule ProductsLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
socket =
socket
|> assign(:filter, :all)
|> assign(:products, load_products()) # 1000 products
{:ok, socket, temporary_assigns: [products: []]}
end
def handle_event("toggle_filter", _params, socket) do
# Products list NOT sent - only filter change transmitted
{:noreply, update(socket, :filter, fn f -> toggle(f) end)}
end
end
After the first render, temporary_assigns resets the :products assign to []. The rendered HTML stays in the browser, but the socket no longer holds the data.
How temporary assigns work
When you mark assigns as temporary:
- First render: Full list sent to browser, rendered into HTML
- Subsequent updates: Assign reset to default value (empty list)
- Socket state: No longer stores the large dataset
- DOM: Original HTML remains unchanged in browser
# Server memory footprint comparison
# Regular assigns: socket.assigns.products = [1000 items]
# Temporary assigns: socket.assigns.products = []
# Network traffic comparison (per update)
# Regular assigns: ~500 KB (1000 products × ~500 bytes each)
# Temporary assigns: ~1 KB (just the filter change)
Perfect for paginated or infinite scroll lists
When loading data incrementally, temporary assigns prevent accumulating all pages in memory:
defmodule InfiniteScrollLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
socket =
socket
|> assign(:page, 1)
|> stream(:items, load_page(1))
{:ok, socket, temporary_assigns: [items: []]}
end
def handle_event("load-more", _params, socket) do
next_page = socket.assigns.page + 1
socket =
socket
|> update(:page, &(&1 + 1))
|> stream(:items, load_page(next_page), at: -1)
{:noreply, socket}
end
end
When to use temporary assigns
Perfect for:
- Large product catalogs or data tables
- Chat messages (append-only streams)
- Activity feeds or notifications
- Search results that don’t change
- Any list rendered once and rarely updated
Don’t use for:
- Lists that need filtering/sorting in LiveView
- Data you need to access in
handle_event - Small lists (< 50 items) where overhead doesn’t matter
- Dynamic lists where individual items update frequently
Accessing temporary data in events
If you need the data in event handlers, load it from the database instead:
defmodule ProductsLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
socket =
socket
|> assign(:category_id, 1)
|> assign(:products, load_products(1))
{:ok, socket, temporary_assigns: [products: []]}
end
def handle_event("select", %{"id" => id}, socket) do
# Products no longer in socket - fetch from DB
product = Products.get_product!(id)
socket =
socket
|> assign(:selected, product)
|> assign(:products, load_products(socket.assigns.category_id))
{:noreply, socket}
end
end
Combine with streams for ultimate efficiency
For append-only lists, use stream/3 with temporary assigns:
defmodule ChatLive do
use Phoenix.LiveView
def mount(%{"room_id" => room_id}, _session, socket) do
if connected?(socket) do
MyAppWeb.Endpoint.subscribe("room:#{room_id}")
end
socket =
socket
|> assign(:room_id, room_id)
|> stream(:messages, load_recent_messages(room_id))
{:ok, socket, temporary_assigns: [messages: []]}
end
def handle_info({:new_message, message}, socket) do
{:noreply, stream_insert(socket, :messages, message)}
end
end
Use temporary_assigns whenever you render a large list that doesn’t need to live in socket state. Your LiveView will use less memory and respond faster to user interactions.
copied to clipboard