Skip to content

Conversation

@SteffenDE
Copy link
Contributor

This tries to address the problem of seeing the "attempting to reconnect" flash lingering around for a very long time when resuming a LiveView tab on mobile. In my tests, this mostly happens when fallback to longpoll is disabled. In this case, the following happens:

  1. User puts their phone to sleep.
  2. After a couple of seconds, the browser kills the websocket connection and Phoenix registers this as an error.
  3. Phoenix tries to establish a new WebSocket connection. This hangs.
  4. User opens the page again. Instead of immediately establishing the connection, the WebSocket is stalled for a while. The exact amount seems a bit random, but I've seen 10+ seconds. During that time, the WebSocket in the devtools doesn't show any activity, but then suddenly the connection works again.

If LongPoll is active, at point 3, Phoenix instead tries a LongPoll connection. In mobile Chrome, the HTTP request fails and Phoenix registers this as 500 and tries again, backing off. If the user resumes the page - depending on their luck - they might need to wait for the next backoff attempt to fire, then the connection is established quickly.

This PR addresses this problem by checking the visibility state using the visibilitychange event, which is recommended for registering a tab going in the background (for example by a phone going to sleep). We set a flag that we're hidden and in the case that there's an error, we don't try to reconnect immediately (since we're hidden). Instead, we reconnect as soon as the visibilitychange fires again with the page not hidden any more.

With this change, the connection is immediately established in both mobile Chrome (Android) and Mobile Safari (iOS), most of the time not seeing the "trying to reconnect" flash at all.

Also, this PR addresses a problem where the connectWithFallback function would accumulate onOpen listeners, causing duplicate callbacks.

Hopefully closes phoenixframework/phoenix_live_view#3896.
Hopefully closes #6149.

If someone wants to test this, you can use:

{:phoenix, github: "phoenixframework/phoenix", branch: "sd-reconnect-visibility-assets", override: true}

This tries to address the problem of seeing the "attempting to reconnect"
flash lingering around for a very long time when resuming a LiveView tab
on mobile. In my tests, this mostly happens when fallback to longpoll is
disabled. In this case, the following happens:

1. User puts their phone to sleep.
2. After a couple of seconds, the browser kills the websocket connection
   and Phoenix registers this as an error.
3. Phoenix tries to establish a new WebSocket connection. This hangs.
4. User opens the page again. Instead of immediately establishing the
   connection, the WebSocket is stalled for a while. The exact amount
   seems a bit random, but I've seen 10+ seconds. During that time, the
   WebSocket in the devtools doesn't show any activity, but then suddenly
   the connection works again.

If LongPoll is active, at point 3, Phoenix instead tries a LongPoll
connection. In mobile Chrome, the HTTP request fails and Phoenix registers
this as 500 and tries again, backing off. If the user resumes the page -
depending on their luck - they might need to wait for the next backoff
attempt to fire, then the connection is established quickly.

This PR addresses this problem by checking the visibility state using
the `visibilitychange` event, which is recommended for registering a tab
going in the background (for example by a phone going to sleep). We set
a flag that we're hidden and in the case that there's an error, we don't
try to reconnect immediately (since we're hidden). Instead, we reconnect
as soon as the visibilitychange fires again with the page not hidden any
more.

With this change, the connection is immediately established in both mobile
Chrome (Android) and Mobile Safari (iOS), most of the time not seeing the
"trying to reconnect" flash at all.

Also, this PR addresses a problem where the `connectWithFallback` function
would accumulate `onOpen` listeners, causing duplicate callbacks.

Hopefully closes phoenixframework/phoenix_live_view#3896.
Hopefully closes #6149.
Copy link
Member

@josevalim josevalim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful!

@SteffenDE SteffenDE merged commit 2575a6b into main Nov 19, 2025
17 of 30 checks passed
@SteffenDE SteffenDE deleted the sd-reconnect-visibility branch November 19, 2025 18:15
filipecabaco pushed a commit to supabase/phoenix that referenced this pull request Nov 26, 2025
* Stop reconnecting when page is hidden

This tries to address the problem of seeing the "attempting to reconnect"
flash lingering around for a very long time when resuming a LiveView tab
on mobile. In my tests, this mostly happens when fallback to longpoll is
disabled. In this case, the following happens:

1. User puts their phone to sleep.
2. After a couple of seconds, the browser kills the websocket connection
   and Phoenix registers this as an error.
3. Phoenix tries to establish a new WebSocket connection. This hangs.
4. User opens the page again. Instead of immediately establishing the
   connection, the WebSocket is stalled for a while. The exact amount
   seems a bit random, but I've seen 10+ seconds. During that time, the
   WebSocket in the devtools doesn't show any activity, but then suddenly
   the connection works again.

If LongPoll is active, at point 3, Phoenix instead tries a LongPoll
connection. In mobile Chrome, the HTTP request fails and Phoenix registers
this as 500 and tries again, backing off. If the user resumes the page -
depending on their luck - they might need to wait for the next backoff
attempt to fire, then the connection is established quickly.

This PR addresses this problem by checking the visibility state using
the `visibilitychange` event, which is recommended for registering a tab
going in the background (for example by a phone going to sleep). We set
a flag that we're hidden and in the case that there's an error, we don't
try to reconnect immediately (since we're hidden). Instead, we reconnect
as soon as the visibilitychange fires again with the page not hidden any
more.

With this change, the connection is immediately established in both mobile
Chrome (Android) and Mobile Safari (iOS), most of the time not seeing the
"trying to reconnect" flash at all.

Also, this PR addresses a problem where the `connectWithFallback` function
would accumulate `onOpen` listeners, causing duplicate callbacks.

Hopefully closes phoenixframework/phoenix_live_view#3896.
Hopefully closes phoenixframework#6149.

* make tests happy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mobile Tab switching triggers full page reload and error flash Websocket connection doesn't reconnect after a device wakes up from sleep

3 participants