5 real-time UX patterns we reach for when a WebSocket drops
Solid reconnection logic on the backend doesn't matter much if the interface doesn't tell users what's happening while it runs. These are five UX patterns we reach for at 137Foundry whenever we're building a live dashboard or collaborative feature, patterns that keep users oriented during the seconds or minutes a connection is quietly recovering in the background.
1. A visible connection-state indicator
Not a toast that flashes once and disappears, a persistent, small indicator (a dot, a label, a subtle color change) somewhere consistent in the interface that reflects one of three states: connected, reconnecting, disconnected. Users glance at it the same way they glance at a wifi icon on their phone, quickly and without thinking, and it answers the question "can I trust what I'm looking at right now" before they even have to ask it out loud.
The Nielsen Norman Group's writing on system status visibility covers the broader usability principle this pattern is built on, and it applies just as directly to real-time connection state as it does to any other kind of background system status.
2. Timestamping stale data explicitly
If a dashboard stops updating, showing "last updated 2 minutes ago" next to the data is far more honest than showing the data with no context at all. Users can make their own judgment about whether two-minute-old data is fine for their purposes or not, but only if the interface actually tells them the data is old instead of presenting it as fresh.
3. Disabling actions that assume a live connection
If a feature depends on real-time acknowledgment, sending a chat message, submitting a collaborative edit, confirming a trade, disable the affected controls or clearly mark them as queued while disconnected, rather than letting users act into a void and wonder later whether anything actually happened on the other end.
4. A manual retry option after enough automatic attempts fail
Automatic reconnection with backoff is the right default, but after several failed attempts, our threshold is usually five or six, give the user a manual "try again" button instead of leaving them staring at an indefinite spinner with no way to intervene. Sometimes the underlying issue needs a page reload or a network switch on the user's end, and a manual control communicates that possibility instead of implying the app will eventually sort itself out on its own.
5. Optimistic local state with a rollback path
For actions taken while reconnecting, queued messages, in-progress edits, show them in the UI immediately as pending, then reconcile once the connection returns and the server confirms or rejects them. Silently discarding a user's in-progress action because the network hiccuped is one of the fastest ways to lose someone's trust in a product, especially if it happens more than once.
A pattern we almost shipped and pulled back
Early on, we tried a version of the connection-state indicator that auto-hid itself after a few seconds once the connection recovered, on the theory that users didn't need to keep seeing a "reconnected" confirmation once things were back to normal. In practice, users who'd noticed the earlier "reconnecting" state and then glanced away came back to a UI that looked exactly like it had before anything happened, with no way to confirm whether the gap had actually been resolved or whether they were still looking at stale information. We ended up keeping a brief, deliberate "reconnected, you're up to date" confirmation state for a couple of seconds rather than snapping straight back to the normal connected indicator, specifically to close that loop for anyone who'd noticed the disruption in the first place.
Photo by Walls.io on Pexels
The lesson generalized past this one feature: any state your UI shows during a failure needs a matching, equally visible state confirming resolution. An interface that announces trouble loudly and then recovers silently trains users to distrust the "everything's fine" state just as much as the "something's wrong" one, since they have no way to tell the difference between actual recovery and the indicator simply timing out. It's a small detail, but it's the kind of detail that separates a product that feels trustworthy under real-world network conditions from one that only feels trustworthy in a demo on office wifi.
How we decide which patterns a given feature actually needs
Not every real-time feature needs all five patterns at once. A internal analytics dashboard viewed by a handful of employees can probably get away with just the connection-state indicator and stale-data timestamping, since the cost of someone glancing at slightly outdated numbers for a minute is low. A collaborative editing feature or anything involving money or irreversible actions needs the full set, including optimistic state with a real rollback path, because the cost of a silently lost or duplicated action is much higher. We size the UX investment to the actual cost of getting it wrong for that specific feature, rather than applying a single fixed checklist to every real-time surface in a product regardless of stakes.
Where these patterns live in our own process
We keep this list as a literal design review checklist for any new real-time feature, reviewed before the first line of frontend code gets written, not retrofitted after launch once someone notices the UX feels confusing during an outage. Retrofitting is possible but always costs more, both in engineering time and in however much user trust got spent during the period the feature shipped without it.
What we measure to know if the patterns are actually working
We track a simple support-ticket tag for anything mentioning stale data, a frozen dashboard, or a lost action, and watch the rate of that tag over time as we roll these patterns out to a given feature. A dropping rate after we add the connection-state indicator or the optimistic-with-rollback pattern to a specific surface is a much better signal than any amount of internal confidence that the UX is now "handled." User-reported confusion is the ground truth here, not whether our own team finds the reconnecting state adequately communicated.
Making these patterns actually reliable
None of these patterns require the backend to be perfect. They require the frontend to be honest about what the backend currently knows, which is a genuinely different design goal than making the reconnection process invisible. Trying to hide reconnection entirely from the user is usually the wrong instinct: a brief, clearly communicated "reconnecting" state builds more trust than a UI that pretends nothing happened, especially once a user notices the data was stale for longer than the interface admitted.
Material Design's guidance on communicating offline states is worth a look if you want a broader design-system perspective on these same ideas applied outside the specific case of WebSockets, and it lines up closely with the patterns above even though it wasn't written with real-time dashboards specifically in mind.
We put these UX patterns together with the actual backoff, heartbeat, and message-replay mechanics that make the "reconnecting" state trustworthy rather than purely cosmetic, in our guide to WebSocket reconnection strategy, if you want the full technical pattern behind the UI states described here. More of our writing on real-time products and web engineering lives at 137foundry.com.