Django LiveView vs Phoenix LiveView: a real benchmark
I was curious: how does Django LiveView hold up against the original Elixir implementation when you compare them under identical conditions? Not with theoretical arguments, but with real numbers.
I built an identical alert dashboard in both frameworks: add, delete, and search alerts in real time. The benchmark is automated with Playwright headless Chromium, measuring the time between the user action and the DOM change, plus the bytes sent over WebSocket per interaction.
The source code is in the repository and is fully reproducible with Docker Compose.
Let's look at the results.
The stack
| Component | Django LiveView | Phoenix LiveView |
|---|---|---|
| Language | Python 3.12 | Elixir 1.17.3 / OTP 27 |
| Framework | Django 6.0.5 | Phoenix 1.7 |
| LiveView | django-liveview 2.2.0 | phoenix_live_view 1.0 |
| Server | Uvicorn 0.47.0 | Bandit 1.5 |
| WS layer | Channels 4.3.2 + Redis 7 | BEAM (built-in) |
Common scenarios
I ran 10 iterations (2 warmup) for add, delete, and search on a list with a few items.
/https://andros.dev/media/blog/2026/05/latency_common.png)
| Scenario | Django LiveView avg (ms) | Phoenix avg (ms) |
|---|---|---|
| Add alert | 32.18 | 32.26 |
| Delete alert | 32.29 | 32.24 |
| Search / filter | 9.77 | 10.13 |
A complete tie. Both frameworks land in the same range: ~32 ms for mutations and ~10 ms for search. The difference is statistically irrelevant.
/https://andros.dev/media/blog/2026/05/distribution_common.png)
Edge cases
This is where things get interesting.
/https://andros.dev/media/blog/2026/05/latency_edge.png)
| Scenario | Django avg (ms) | Phoenix avg (ms) |
|---|---|---|
| Large list (500 items) | 60.42 | 53.49 |
| Rapid fire (5 clicks) | 321.80 | 318.44 |
| Empty search | 12.39 | 7.84 |
With 500 alerts loaded, Phoenix is 12% faster. Rapid fire is practically identical.
However, the most striking data point is the payload:
/https://andros.dev/media/blog/2026/05/payload.png)
| Scenario | Django receives | Phoenix receives |
|---|---|---|
| Add alert | 5,348 B | 976 B |
| Delete alert | 3,421 B | 829 B |
| Large list (500 items) | 327,428 B | 66,382 B |
| Rapid fire (5 clicks) | 60,953 B | 12,490 B |
With the large list, Django LiveView transfers 327 KB per action versus Phoenix's 66 KB. Every time you add an alert with 500 already loaded, Django LiveView sends you the entire table again. Phoenix only sends the new row.
It's a design difference, not an implementation flaw. In Django LiveView you explicitly define which selector to update and with what HTML:
@liveview_handler("add_alert")
def add_alert(consumer, content):
alerts = list(Alert.objects.all())
html = render_to_string("components/_alerts_table.html", {"alerts": alerts})
send(consumer, {"target": "#alerts-container", "html": html})
In Phoenix LiveView you update the assigns and the framework computes the diff:
def handle_event("add_alert", _params, socket) do
alert = Alerts.create_random_alert()
{:noreply, assign(socket, alerts: [alert | socket.assigns.alerts])}
end
Phoenix knows which part of the template changed and only sends that. Django LiveView has no such mechanism, so granularity is up to you: point to a small selector and you send less, point to a large container and you send everything. Does this matter? On slow connections or with many concurrent clients, yes. In an app with few users or small lists, it will be indistinguishable.
Conclusion
The data answers my original question: yes, Django LiveView holds up against Phoenix LiveView. In day-to-day operations they are practically equal. Phoenix's advantage shows when the payload grows. Even then, it's a problem you can mitigate with careful design in Django LiveView by targeting more specific selectors.
The advantage of Django LiveView is staying in Python and the Django ecosystem without leaving what you already know, and that Django LiveView is more explicit and predictable: you decide what HTML to send and where to place it.
I'm pleasantly surprised.
PS: You are looking at a site built 100% with Django LiveView, I invite you to push its limits.
This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.
Support me on Ko-fi
Comments
There are no comments yet.