Skip to content

Commit 31990fa

Browse files
committed
Publish Elixir: Having multiple Live Views on the same page
1 parent 74bfebe commit 31990fa

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
+++
2+
title = "Elixir: Having multiple Live Views on the same page"
3+
description = "How to leverage Live Session for complex structures"
4+
date = 2025-07-25
5+
[taxonomies]
6+
tags = ["tutorial", "elixir", "phoenix", "liveview"]
7+
+++
8+
9+
## Context
10+
11+
In its early versions and up until v1.0, Phoenix LiveView has been designed to let you manage one LiveView at a time. But in a few cases, you'll need to be able to manage multiple Live Views, for either a complex dashboard, or even a navigation menu living on the side.
12+
13+
In my case, I had to implement a navigation bar in one of my projects, with a different class for the active element. Using a Live Component was not possible, because those can't be called from a regular (non-Live) template.
14+
15+
Although, since Live Views are GenServers, and therefore Processes, having one Process per user constantly running just for displaying a dynamic navigation bar isn't great. It would be much better to save those few kilobytes of RAM, and do the work with pure CSS and JS hooks, therefore offloading the logic to the client. But let's just consider we need two LiveViews sitting next to each other for our use case.
16+
17+
This is where [`live_session/3`][1] comes into play.
18+
19+
## Usage
20+
21+
`live_session/3` is a Router-specific "word" (in the Router DSL) that lets you define a handful of things:
22+
* A `name` to identify it
23+
* A `session` argument, in case you want to put a value in the connection (HTTP) Session
24+
* A `root_layout` argument, in case you want to override it (it may already be specified in your pipeline)
25+
* A `layout` argument, to specify a "partial view" that'll be rendered inside your root layout
26+
* An `on_mount` callback to specify a list of functions to call in order to update the socket
27+
28+
## Example
29+
30+
This is how my code looks like:
31+
32+
```elixir
33+
# router.ex
34+
live_session :admin,
35+
on_mount: {SutoWeb.InitAssigns, :admin},
36+
layout: {SutoWeb.Layouts, :"live.admin"} do
37+
scope "/admin", SutoWeb.Admin do
38+
pipe_through :admin
39+
live "/", IndexLive
40+
end
41+
end
42+
```
43+
44+
```elixir
45+
# init_assigns.ex
46+
defmodule SutoWeb.InitAssigns do
47+
import Phoenix.Component
48+
49+
def on_mount(:admin, _params, _session, socket) do
50+
current_page =
51+
case Atom.to_string(socket.view) do
52+
"Elixir.SutoWeb.Admin.AuthLive" <> _rest ->
53+
:auth
54+
55+
"Elixir.SutoWeb.Admin.ChannelLive" <> _rest ->
56+
:channels
57+
58+
"Elixir.SutoWeb.Admin.TimerLive" <> _rest ->
59+
:timers
60+
61+
...
62+
end
63+
64+
{:cont, assign(socket, :current_page, current_page)}
65+
end
66+
end
67+
```
68+
69+
```elixir
70+
# Partial layout
71+
<div class="min-h-screen h-full flex flex-row bg-gray-300">
72+
<.live_component
73+
module={SutoWeb.Admin.NavLive}
74+
id="nav"
75+
class="p-5"
76+
current_page={assigns.current_page}
77+
/>
78+
<main class="p-5 w-full h-screen overflow-auto">
79+
<.flash_group flash={@flash} />
80+
{@inner_content}
81+
</main>
82+
</div>
83+
```
84+
85+
And the `NavLive` module here is just a navigation bar built with [Petal Framework][2], defining list items and picking the right Tailwind class for the active item.
86+
87+
That's it :) Please keep in mind that using a Live Session for something that could be offloaded to the client (with JavaScript and hooks) isn't great, but this gives you an idea on what structure to use, in case you're building a complex trading dashboard, and you absolutely need to have separate LiveViews on the same page, for concurrency reasons or resilience.
88+
89+
[1]: https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Router.html#live_session/3
90+
[2]: https://petal.build/

0 commit comments

Comments
 (0)