DurableStash

Keep Phoenix LiveView state alive across reconnects, crashes, and redeploys.

A LiveView's assigns live in memory and vanish when the socket drops, the process crashes, or you deploy. DurableStash saves the assigns you pick to object storage and restores them the next time the LiveView mounts. The saved copy belongs to the browser session, so every LiveView the user opens shares it. Under the hood it plugs into LiveStash and stores state through DurableServer.

State survives:

State dies with the browser session: cleared cookies, another browser, or TTL expiry mean defaults.

Usage

defmodule MyAppWeb.SomeLive do
use MyAppWeb, :live_view
use LiveStash, adapter: DurableStash, stored_keys: [:count, :username]
def mount(_params, _session, socket) do
socket = assign(socket, count: 0, username: nil)
socket =
if connected?(socket) do
{_status, socket} = LiveStash.recover_state(socket)
socket
else
socket
end
{:ok, socket}
end
def handle_event("increment", _params, socket) do
socket = update(socket, :count, &(&1 + 1))
{:noreply, LiveStash.stash(socket)}
end
end

mount/3 runs twice — once for the HTTP render, once when the socket connects — and recovery on the HTTP render costs an object-store round-trip. A view with only :reconnect keys skips it, since nothing is recoverable without a socket. A view with :session keys pays it so the stored value makes the first paint; guard the call with connected?/1 if you would rather it did not. See the DurableStash moduledoc.

Scopes

Not all state wants the same recovery policy, so each stored key declares one:

use LiveStash, adapter: DurableStash,
stored_keys: [
theme: :session, # recover on every mount (the default for bare atoms)
draft: :reconnect # recover only on rejoins; cleared on fresh navigation
]

See the DurableStash moduledoc for setup (adapter registration, backend config, the ensure_session_id plug) and all options (:vsn, :migrate, :secret).

How it works

DurableStash.TestBackend ships with the package: a faithful in-memory DurableServer.StorageBackend (including etag CAS) for tests and make run style development without S3 credentials.

Installation

def deps do
[
{:durable_stash, "~> 0.1.0"}
]
end

Credits

DurableStash stands on two lineages: