LiveStash

LiveStash provides a reliable, explicit API to safely stash and recover Phoenix LiveView assigns, keeping your application state completely intact whenever a socket connection is interrupted or re-established.

Try the interactive demo, check out our documentation.

https://github.com/user-attachments/assets/c03836e1-362e-4e06-bfc7-3656c6672c8e

Usage

Adding LiveStash to your existing LiveView is very simple.

  1. Add use LiveStash to your module. It registers LiveStash's on_mount hook, which initializes stash support for the socket. See LiveStash.__using__/1.

The assigns you want to persist are declared once at the module level with stored_keys: [...].

defmodule ShowcaseAppWeb.CounterLive do
  use LiveStash, stored_keys: [:count, :user_id]
  1. Update your assigns. By default, LiveStash requires you to call LiveStash.stash/1 manually, and only writes when the configured stored_keys values change.
  def handle_event("increment", _, socket) do
    socket
    |> assign(:count, socket.assigns.count + 1)
    |> assign(:user_id, 123)
    |> LiveStash.stash()
    |> then(&{:noreply, &1})
  end

You can pass auto_stash: true when you want auto-stashes after each render, but we recommend using it only if you have a very specific use case that requires it. In most cases, manual stashing is more efficient and gives you better control over when the state is saved:

use LiveStash, stored_keys: [:count, :user_id], auto_stash: true

def handle_event("increment", _, socket) do
  socket
  |> assign(:count, socket.assigns.count + 1)
  |> assign(:user_id, 123)
  |> then(&{:noreply, &1})
end
  1. Call recover_state(socket) in your mount/3 function call. It will automatically restore assigns to your socket.
  def mount(_params, _session, socket) do
    socket
    |> LiveStash.recover_state()
    |> case do
        {:recovered, recovered_socket} ->
          # socket with previously stashed assigns is recovered
          recovered_socket

        {_, socket} ->
          # could not recover assigns, proceed with standard setup using returned socket
          # ...
    end
    |> then(&{:ok, &1})
  end

Installation

Add live_stash to your dependencies in mix.exs:

def deps do
  [
    {:live_stash, "~> 0.3.0"}
  ]
end

In your app.js, pass initLiveStash into the LiveSocket params:

import initLiveStash from "../../deps/live_stash/assets/js/live-stash.js";

const liveSocket = new LiveSocket("/live", Socket, {
  params: initLiveStash({ _csrf_token: csrfToken }),
  // ...
});

Storage modes

You can control where the stashed data is kept by passing appropriate adapter module. LiveStash currently supports two adapters:

use LiveStash, adapter: LiveStash.Adapters.ETS, stored_keys: [:count, :user_id]

Remember to define adapters you would like to activate in your config.exs file.

config :live_stash, adapters: [LiveStash.Adapters.ETS, LiveStash.Adapters.BrowserMemory, LiveStash.Adapters.Redis]

The default adapter is LiveStash.Adapters.ETS and it is always activated.

See ETS Adapter Guide, Redis Adapter Guide and Browser Memory Adapter Guide for details on how to customize LiveStash to your needs.

When not to use

LiveStash is meant for explicitly stashing server-side LiveView assigns that you truly need to survive reconnects. For a lot of state, there are better (and simpler) tools:

Contributing

For those planning to contribute to this project, you can run an example projects with LiveStash with following commands:

cd examples/showcase_app
mix setup
iex -S mix phx.server

Authors

LiveStash is created by Software Mansion.

Since 2012 Software Mansion is a software agency with experience in building web and mobile apps as well as complex multimedia solutions. We are Core React Native Contributors, Elixir ecosystem experts, and live streaming and broadcasting technologies specialists. We can help you build your next dream product – Hire us.

Copyright 2026, Software Mansion

Software Mansion

Licensed under the Apache License, Version 2.0