0% прочитано

After a WebSocket Reconnect, the Client Should Reconcile with the Server

A restored WebSocket connection does not guarantee that the client received every event during the gap. This article explains why the client should reconcile with canonical server state after reconnect and how to do it without duplicates or UI jumps.

31 жовтня 2026 р. 9 хв читанняLaravel

WebSockets are excellent for delivering changes quickly, but the connection itself is not a guarantee of complete event history.

A user may close a laptop, lose connectivity, switch networks, or experience a short disconnect while the server continues changing state.

When the client reconnects, a connected status means only that the channel is available again. It does not prove that nothing was missed between the old and new connections.

After reconnect, the client should not assume that every event arrived. It should reconcile with canonical server state.

A Disconnect Creates an Invisible Event Gap

Server state keeps changing during the disconnect
12:00:00 connected12:00:05 disconnected 12:00:08 notification created12:00:12 message read12:00:18 another notification created 12:00:25 connected again

In this scenario, the client may know nothing about two new notifications and a changed message state.

New events will arrive after reconnect, but the missed interval does not repair itself.

Realtime Events Accelerate the UI, but Canonical State Remains on the Server

If the server already stores notifications, messages, read_at, unread counts, or other trustworthy state, that state should be the basis for restoring the client.

A WebSocket event exists for speed. It lets the UI react immediately without waiting for the next HTTP request.

After an unknown offline interval, however, fetching server state is safer than guessing which events may have been missed.

Reconnect Needs an Explicit Reconciliation Step

After the channel reconnects, the client reads server state again
echo.connector.pusher.connection.bind(    'connected',    async () => {        await refreshUnreadCount();        await refreshRecentNotifications();    },);

The exact code depends on the WebSocket client and library version, but the principle is stable: reconnect should trigger reconciliation.

This may be one compact snapshot endpoint or several small requests for the parts of the UI that actually depend on realtime state.

Reconciliation Does Not Need to Reload the Whole Page

Reconciliation does not have to become a full application reload.

For notifications, it is often enough to fetch:

  • the current unread count;
  • the most recent notifications;
  • an identifier or timestamp for the latest known change;
  • when needed, the current state of items that are open on screen.

Merge the Snapshot Without Creating Duplicates

During reconnect, the same notification can arrive through two paths: first in an HTTP snapshot and a moment later as a realtime event.

The client list therefore needs a stable identifier and should update items by id rather than blindly appending everything.

One function for both snapshots and realtime events
function upsertNotification(    notification,) {    const index =        notifications.value.findIndex(            (item) =>                item.id === notification.id,        );     if (index === -1) {        notifications.value.unshift(            notification,        );         return;    }

Replace Counters with Server Values Instead of Guessing the Delta

After reconnect, it is unsafe to say something like "we probably missed two events, so add two."

The client does not know how many items were created, read, or removed during the gap.

If the server can return unreadCount = 7, the client should set 7 instead of trying to reconstruct every missed increment.

An Active Chat May Need Its Own Message Reconciliation

If the user remained on a specific conversation while disconnected, the active thread may need reconciliation too.

Instead of reloading the full history, the client can request messages after the last known message_id or another stable cursor.

This is not just a UI refresh. It restores a missing segment of history.

Every Technical Reconnect Should Not Trigger an HTTP Request Storm

An unstable network may move the connection between disconnected and connected several times in a short period.

Reconciliation should therefore be centralized, not duplicated across components, and protected from overlapping executions when necessary.

One reconnect coordinator is safer than ten components independently deciding when to refetch server state.

Initial Load and Reconnect Can Share the Same Synchronization Path

One path for initial load and reconnect synchronization
async function syncNotificationState() {    const state =        await api.getNotificationState();     unreadCount.value =        state.unreadCount;     mergeNotifications(        state.notifications,    );} onMounted(    syncNotificationState,); 

Returning to a Browser Tab Is Another Good Reconciliation Point

Browsers may throttle background tabs, network events may be delayed, and the WebSocket can reconnect without a specific component noticing.

For important counters and notifications, a cheap additional trigger is useful: when the tab becomes visible again, verify current server state.

This works well together with reconnect reconciliation, not as a replacement for it.

What to Test

Minimal scenario coverage
1. A missed notification appears after reconnect reconciliation.2. Unread count is replaced with the server value.3. The same notification from snapshot and realtime is not duplicated.4. An active thread fetches messages after the last known cursor.5. Multiple reconnect events do not start overlapping sync requests.6. A failed reconciliation can be retried safely.7. Reconciliation does not clear newer realtime state.8. Returning to a visible browser tab can refresh stale counters.9. Initial load and reconnect use the same canonical synchronization path.10. WebSocket events continue to update the UI after reconciliation finishes.

Reconnect Restores the Channel, Reconciliation Restores the State

WebSocket reconnect is necessary so the client can start receiving new events again.

But that is not enough to make the UI correct. The client should verify what changed during the gap using canonical server state.

Realtime provides responsiveness. Server reconciliation restores trustworthy state after an unknown period without connectivity.

After reconnect, I trust the state not because the connection is green again, but because the client reconciled with the server.