Keeping a side navigation components open/close state

Hi,

I have a site layout which has a side navigation which is collapsible (i.e. it has a button to make wider/thinner). One thing that I can’t quite figure out the best approach for is how to keep the open/close state persisted between page transitions. So if the default is open, and I navigate to another page, it will always go back to the open state. I was thinking about maybe using a JS hook and local storage, but wanted to see if others have solved this more elegantly?

Side note on the open/close implementation:
I’m using a data attribute for the open/close state, in conjunction with some CSS rules.

  <aside
    id={@id}
    class="w-20 data-[state=open]:w-[250px] transition-[width] ..."
    data-state="open"
    phx-update="ignore"
  >
    ...
  </aside>

thanks

For persisting when navigating across liveviews or live sessions, you could live render the sidebar as its own “sticky” liveview.

Another option is to append that sidebar state to the url as parameters, but not sure how elegant that is for this use case.

Thanks for the suggestion, I did consider that approach, but it feels too heavy as an approach for keeping UI state.

The LiveView, and particularly the sticky LV, is the abstraction for compartmentalizing state (and events).
If you want to keep the state on the server, then sticky LV is a good option, I had a positive experience implementing a similar navigation component after making a similar question myself.

The phx-update="ignore" and local/session storage direction is if you prefer to keep the state in the browser only.

Both options are viable. State on the server opens up some creative possibilities, like syncing the state across tabs, easily “studying” how users use the UI, remote-controlling the UI, conditional events based on UI state, etc.

1 Like

This part of the official guide explains the options Welcome — Phoenix LiveView v0.20.17

  • use nested Phoenix.LiveView to compartmentalize state, markup, events, and error isolation

For the navigation component, it means crashes on other LiveViews would not affect the open/close state.

And if you want to persist state across page refreshes, you can also save the state as a user preference in your database (have also done this in a few situations).

Thanks for the input, seems like I should take another look at the sticky liveview :slight_smile:

1 Like

Coming back to this, I wanted to share that there are other options worth exploring that don’t involve a separate (sticky) LiveView.

For instance, one can save the open/close state on the client using localStorage and then pass it as params to the LiveSocket, see the example in the docs at Phoenix.LiveView.get_connect_params/1.

For a collection of this and other techniques, there’s a talk from ElixirConf EU 2025: Phoenix LiveView: Mastering Reconnects for a Seamless User Experience -Giovanni Francischelli.

1 Like