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.
A Disconnect Creates an Invisible Event Gap
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 againIn 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
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.
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
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
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.



