Mobile Tab Switching Triggers Full Page Reload in LiveView - Is This Expected Behavior?

Hi everyone,

I’m working on a Phoenix LiveView application and noticed an annoying behavior on mobile devices: whenever users switch tabs and return to the app, the entire page reloads even if it was fully loaded before. This happens consistently on both iOS and Android browsers.

The Issue

  • User loads a LiveView page on mobile
  • User switches to another browser tab or app
  • User returns to the original tab
  • The socket reconnects and triggers a full page reload, losing scroll position and any client-side state

This creates a poor user experience, especially on slower connections where users have to wait for the entire page to reload.

Our implementation uses standard LiveSocket configuration:

  let liveSocket = new LiveSocket("/live", Socket, {
    timeout: 60000,
    params: { _csrf_token: csrfToken },
    // ... standard configuration
  });

We don’t have any visibilitychange or pageshow/pagehide event handlers

Is this the default Phoenix LiveView behavior? Does LiveView intentionally trigger a full remount when reconnecting after tab switches?

Here a potential solution I’m considering

  // Detect tab visibility changes
  document.addEventListener("visibilitychange", () => {
    if (document.hidden) {
      // Gracefully disconnect?
      liveSocket.disconnect();
    } else {
      // Reconnect without full reload?
      liveSocket.connect();
    }
  });

But I’m unsure if this is the right approach or if it might cause other issues…

1 Like

I haven’t noticed this behaviour in my LiveView projects…

Can you try out my toy LiveView project and see if it still happens?

Also, here’s my liveSocket config:

1 Like

I just noticed the same thing on a recently created app using the 1.8.0-rc.3 phx.new generator.
@ivanminutillo Which version did you use?

This one works fine for me, as well as older projects I did with older versions of liveview (it was pre-1.0).
I’m not sure when this was introduced, but it doesn’t feel intentional and is probably a bug worth reporting?

I’m having the same issue, in all my liveview applications. But it’s so hard to reproduce intentionally, as you need to keep the tab in the background for “long enough”.
It’s really jarring, as when it happens the reconnect takes a long while, much longer than when first visiting.

My guess is that the socket disconnects after a while. Then when switching tabs, the socket reconnect is already in the backoff phase and that’s why it takes a long time to reconnect?
But I’ve also noticed that after the socket reconnected, it immediately does a full refresh as well.

So yeah, any hints on how to solve this are greatly appreciated! Also, how do you even debug this?

I’ve checked some public apps, it seems like livebeats isn’t affected but Users · Backpex is.

I found a way to reproduce it consistently, which is (on an iPhone):

  • open the tab
  • lock the phone with the side button
  • wait ~10s
  • unlock the phone, get welcomed by a red flash

Follow-up: I opened an issue:

2 Likes

I think this is solved by Stop reconnecting when page is hidden by SteffenDE · Pull Request #6534 · phoenixframework/phoenix · GitHub - please try it using the branch from GitHub linked in the PR and hopefully we’ll have this properly fixed in the next release! During testing, I did NOT see the device doing a full page reload though, it only reconnected the socket, so there might be something else in play as well. I think the full reload might happen if there was a deploy in between, then it would be expected iirc.

I’ve been testing this with an Android device + USB debugging to attach to the devtools and inspect what’s happening when the device goes to sleep. Previously, we’d immediately try to connect again. In the WebSocket case the socket would stay in connecting state indefinitely and when unlocking the device the actual connection would take a very long time (for no apparent reason). For LongPoll, the device would backoff the HTTP requests and then also when unlocking the actual connection could take a very long time to actually send data (I ran tcpdump on the server side).

The fix prevents trying to reconnect when the page indicates that it is hidden and only does a new connection attempt as soon as it’s visible again. For both WebSocket and LongPoll, I can’t reproduce the stalling in that case. The page reconnects instantly as it should.

1 Like

Either Phoenix adaptation has increased significantly or other websites/frameworks face the same issue.

I suspected it to be an annoying iOS ‘optimization’ as it happens on multiple websites.