henrik

henrik

Hi!

I’ve got a tabbed interface currently with two separate LiveViews, each with their own live route (e.g. live "/one", OneLive and live "/two", TwoLive in the router).

The live layout uses live_redirect for the tabs that switch between the two.

I don’t love the slight delay when clicking a link and a LiveView re-mounts. I’d like for both LiveViews to stay loaded for the duration of the user’s session.

What’s a good option here to load both LiveViews at the same time but let me navigate between them?

Put them both in the same non-live page with two <%= live_render … %> and make JS tabs to hide/show one or the other? And write my own push state stuff if I want the URL to be different?

Or perhaps nested LiveView components, where the outer one is responsible for the tabs and does push state stuff to the URL via LiveView hooks?

Showing Posts 1 to 8

josevalim

josevalim

Creator of Elixir

I am doing it LiveComponents in my app. The parent LiveView does a simple mapping from the current action to the component name, similar to how the phx.gen.live uses the action to enable the modal. The router looks like this:

live "/one", TabbedLive, :tab1
live "/two", TabbedLive, :tab2

Then you can use live_patch between them, but you shouldn’t really see such a drastic difference compared to live_redirect.

10
Post #1
henrik

henrik OP

Thank you, José! Sounds like a good way of doing it - will give that a shot and see how it compares.

henrik

henrik OP

Started experimenting with this.

I’m realising that turning my LiveViews into components makes them a little more work, because I’ll have to pass their state in from the parent TabsLive, have to explicitly tell them to pass events to @myself, and they don’t have a handle_info.

But I guess that’s the trade-off – if I want to switch between multiple live thingies without re-mounting them, I assume they need to be components within a parent LiveView. I guess there’s no way to switch between two different LiveViews without unmounting/remounting, other than if I render both on a non-live page and hide/show them?

Just to check that I’ve understood the idea correctly (and if it helps someone else who finds their way here), this is what I’ve got so far.

Routes:

live "/one", TabsLive, :one
live "/two", TabsLive, :two

With this navigation in the live layout:

<%= live_patch("One", to: Routes.tabs_path(@socket, :one)) %>
<%= live_patch("Two", to: Routes.tabs_path(@socket, :two)) %>

And this in TabsLive:

defmodule MyWeb.TabsLive do
  use MyWeb, :live_view

  def render(assigns) do
    ~L"""
    Hello: <%= @live_action %>

    <%= case @live_action do %>
    <% :one -> %>
      <%= live_component @socket, MyWeb.OneComponent, id: :one %>
    <% :two -> %>
      <%= live_component @socket, MyWeb.TwoComponent, id: :two %>
    <% end %>
    """
  end

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  # Needs to be defined for re-rendering to happen.
  def handle_params(_params, _uri, socket) do
    {:noreply, socket}
  end
end
henrik

henrik OP

Seems like I can do

<%= live_render @socket, RemitWeb.OneLive, id: :one %>

instead of

<%= live_component @socket, MyWeb.OneComponent, id: :one %>

Then when I navigate between the tabs, it does mount the components again, but it no longer does an Ajax request to “/one” like it did when I used live_redirect … for the links in the layout.

I can go even further and have TabsLive do

def render(assigns) do
  ~L"""
  <div style="display: <%= if @live_action == :one, do: "block", else: "none" %>">
    <%= live_render @socket, MyWeb.OneLive, id: :one %>
  </div>

  <div style="display: <%= if @live_action == :two, do: "block", else: "none" %>">
    <%= live_render @socket, RemitWeb.TwoLive, id: :two %>
  </div>
  """
end

Then it only mounts them once and hides/shows them, but all within LiveView.

When both LiveViews run at the same time, they can’t be relied on to set page_title anymore (the one that isn’t visible could suddenly set it based on some background event), but that responsibility fits well in a TabsLive anyway:

def handle_params(_params, _uri, socket) do
  socket =
    case socket.assigns.live_action do
      :one ->
        assign(socket, :page_title, "One")
      :two ->
        assign(socket, :page_title, "Two")
    end

  {:noreply, socket}
end

Would love some feedback on how sane this solution is :smiley:

josevalim

josevalim

Creator of Elixir

They can build their own state - you don’t have to do it in the parent. That’s what what I do in my case. Although you are correct on the @myself and handle_info bit.

You shouldn’t need to define mount/3 and handle_params/3 in the parent LiveView either. If you have to do so, it is definitely a bug, please open up an issuie. :slight_smile:

Regarding your new approach, it definitely works fine, but it does mean more processes are spawned.

dalerka

dalerka

Could you please show how to implement this use case in a better/efficient way?

Exadra37

Exadra37

Maybe you want to see how Surface implements tabs under the hood and replicate it, or just use Surface, that is an abstraction for web components on top of live view, made by @msaraiva.

dalerka

dalerka

Nice! Thank you for great example!

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
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
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews