---
title: "Restore LiveView State on Startup"
description: "Recipe for restoring saved LiveView state in the browser during the LiveView setup process. We take the extra steps to make it reusable."
---

# Restore LiveView State on Startup

[Annie Ruygt](https://annieruygtillustration.com/)

Fly.io runs apps close to users, by transmuting Docker containers into micro-VMs that run on our own hardware around the world. This is a recipe about how you can optimize restoring Phoenix LiveView state that’s stored in the browser. If you are interested in deploying your own Elixir project, the easiest way to learn more is to [try it out](https://fly.io/docs/speedrun/); you can be up and running in just minutes.

You are storing some [LiveView state in the browser](https://fly.io/phoenix-files/saving-and-restoring-liveview-state). You want to retrieve that saved state as early as possible to improve the user experience. How can you do that?

## Problem

---

The approach in [Saving and Restoring LiveView State](https://fly.io/phoenix-files/saving-and-restoring-liveview-state) waits for the LiveView to request the client to send the data. In your situation, you know you want the data, so why wait around to be asked for it? Can you have it automatically pushed from the client without being requested? Can you do it in a way that is reusable for other pages and other LiveViews and not just this one page?

## Solution

---

When you already know you want data stored in the browser during the startup of your LiveView, there is a way to send it up when the socket connection is being setup.

The data could be written into the HTML of the page or stored in `sessionStorage` or `localStorage`. Regardless of *where* it’s stored, you have a way to send it to the server from the client without being asked.

Assuming a freshly created Phoenix application, the `app.js` file will look something like this.

```
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"

let csrfToken =
  document.querySelector("meta[name='csrf-token']")
  .getAttribute("content")
let liveSocket =
  new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())

// connect if there are any LiveViews on the page
liveSocket.connect()

// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000)  // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket
```

Let’s look at this line in particular.

```
let liveSocket = new LiveSocket("/live", Socket, ...)
```

[The `params` option to the `LiveSocket` constructor](https://hexdocs.pm/phoenix_live_view/js-interop.html#content) accepts a javascript closure (aka “function”) which will be invoked on the client when generating the param value. We can change `params` to look something like this:

```
let params = () => {
  return {_csrf_token: csrfToken, restore: sessionStorage.getItem(...)}
}

let liveSocket = new LiveSocket("/live", Socket, {params: params})
```

This reads the data we want to send up from `sessionStorage` on the client. Then on the server you read the connect params in the mount callback:

```
  def mount(_params, _session, socket) do
    case get_connect_params(socket) do
      %{"restore" => token} -> ...
```

This works well enough and depending on your needs, you may be done!

However, the code in `app.js` get’s loaded on **every** page load. If you have *multiple* LiveViews and they don’t care about or even *want* the data stored on the client, then sending it up every time we connect the socket is a waste of resources! Especially if the payload is large.

Can we make it better by only sending it up when we want it? If multiple pages store data, then we might want to be selective about what data we send up. In short, can we make this more reusable within a single application?

If we add some custom DOM attributes to our template, the `app.js` file can look for it. If it’s there, it can also look for what key the data is stored under and fetch that during the connect process.

Our `html` might look like this:

```
<div data-state-restore="true" data-session-key="my_special_key">
  ...
</div>
```

Here we added `data-state-restore="true"` and `data-session-key="my-key"`. There’s nothing special about these keys except that we’ll look for their existence and act on it.

In `app.js`, we can update the `param` function to look for those keys.

```
let params = (node) => {
  var restoreNode =
    node && node.querySelector("div[data-state-restore='true']")
  if (restoreNode) {
    var key = restoreNode.getAttribute("data-session-key")
    return {_csrf_token: csrfToken, restore: sessionStorage.getItem(key)}
  }
  else {
    return {_csrf_token: csrfToken}
  }
}
```

If your LiveView does *not* include `data-state-restore`, then nothing extra happens. Your LiveView continues on as you’d expect. If it *is* present, it uses the key we provide to look up in `sessionStorage` for any data we have stored there. That data is sent along when the connection opens in the `mount` callback.

Sweet!

## Discussion

---

Phoenix and LiveView already provide the hooks we need to make this feature work. The key is knowing that the `new LiveSocket(...)` params can be a closure that determines at runtime if and what to send when connecting to the server.

When you know you want to restore LiveView state during the startup process, then this approach makes that easy. We took the extra steps here to make it reusable so our multiple LiveViews can save and restore their own state, or restore no state at all!

Restoring state during page setup helps improve the user experience. State is restored faster, and we avoid the extra round-trip of requesting to restore the state.

## Fly ❤️ Elixir

Fly is an awesome place to run your Elixir apps. It’s really easy to get started. You can be running in minutes.

[Deploy your Elixir app today! →](https://fly.io/docs/elixir/)
