Flood of "LiveView asset version mismatch" errors after updating

I updated phoenix_live_view, and deployed, then got 100s of errors reported to Sentry:

LiveView asset version mismatch. JavaScript version 1.0.18 vs. server 1.1.19. To avoid issues, please ensure that your assets use the same version as the server.

deps/phoenix_live_view/priv/static/phoenix_live_view.js contains:

  if (liveview_version !== this.liveSocket.version()) {
    console.error(
      `LiveView asset version mismatch. JavaScript version ${this.liveSocket.version()} vs. server ${liveview_version}. To avoid issues, please ensure that your assets use the same version as the server.`
    );
  }

It’s just a simple log, no logic. Shouldn’t something like window.reload() be invoked when this happens? (Maybe set a flag for what we want the behavior to be.)

Or am I missing something? I see almost nothing in the interwebs about this, yet I know everyone else is updating phoenix_live_view…

My root layout has:

        <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
        </script>

which appears to still be the way to include it.

Dockerfile runs RUN mix assets.deploy which is:

      "assets.deploy": [
        "tailwind my_app --minify",
        "esbuild my_app --minify",
        "phx.digest"
      ],

This would instantly nuke all client-side state without giving users a way to save their work.

You have to opt-in to root-layout updates, see static_changed?/1.

In a large app though, what is the practical way to implement this?

Add an on_mount helper in the router for all LiveView’s, and then in the root layout add the logic of showing the banner?

I’m considering adding a PR to update the static_changed? docs to give a bit more realistic details. No one is doing what they propose in a large app with 50+ pages… or maybe that really is what is expected?

Thank you!

Yes, that’s exactly what I do.

Create a module to contain your on_mount hook:

  def on_mount(:default, _params, _session, socket) do
    {:cont, assign(socket, static_changed?: static_changed?(socket))}
  end

Add it to MyAppWeb to apply to all LiveViews:

  def live_view do
    quote do
      use Phoenix.LiveView

      on_mount MyAppWeb.CheckStaticChanges

In the root template, you can conditionally show something, e.g. a button, to let the user know there’s a new version and they can reload the page. Use the assign in a :if or to set some data attribute and use conditional CSS.

    <a
      :if={@static_changed?}
      href="#"
      onclick="window.location.reload()"
    >
      New version available, click to reload
    </a>

Of course the specifics you can decide for your taste or requirements, but those are the building blocks.

Happy holidays!

1 Like