What separates a static web app from a “live” one is rarely a single feature; it is a collection of small UX patterns that signal to the user “your work is safe, the system is responsive, and you are not alone.” Senior frontend interviews increasingly probe for understanding of these patterns. This guide covers what works in 2026.
Saving indicators
The Notion / Linear / Figma pattern:
- “Saved” by default (subtle, gray text)
- “Saving…” while a debounced save is in flight (subtle, slightly darker)
- “Failed to save — Retry” if the save errors (red, with action)
- Persist across navigation; do not let the user leave with unsaved changes silently
Critical: the user should never wonder if their change was saved. The indicator is the entire safety story for an autosave product.
Optimistic updates
The pattern:
- User performs action (toggle, type, drag)
- UI updates immediately as if it succeeded
- Request fires in background
- On success: confirm visually (subtle); replace temp ID with server ID
- On failure: revert with a clear toast and retry option
Junior implementations skip the failure path. The reversion UX is what makes optimistic updates trustworthy rather than confusing.
Presence indicators
- Avatars of other viewers in a corner
- Cursors with names (Figma-style)
- Selected ranges with their color (Google Docs)
- “X is typing…” in chat / comments
- Subtle animations; loud animations break flow
Design rule: presence should answer “am I working alone or with others?” without competing for attention.
Activity feed
- “Alice edited the homepage 2 minutes ago”
- Subtle, sidebar or non-blocking panel
- Clickable to jump to the change
- Aggregated when noisy (“3 edits in the last hour by Alice”)
Connection state
- Visible indicator when connection drops
- “Reconnecting…” with subtle pulse
- “You are offline; changes will sync when you reconnect” if offline-tolerant
- Restored state on reconnect — cursor, selection, draft
Loading states beyond spinners
- Skeleton: shape of the content, gray; works for first-load
- Streaming: render content as it arrives (LLM-generated text, partial responses)
- Progressive: low-quality first, hi-quality after (image LQIP)
- Optimistic: show the eventual content immediately, mutate on response
Spinners are last resort. Modern apps lean on skeletons and streaming.
Conflict UI
What happens when your action collides with someone else’s?
- Last-write-wins is fine for many cases (chat, comments)
- For documents, surface the conflict — “Alice also edited this paragraph”
- Provide a clear path: keep mine, keep theirs, merge
- For collaborative editors with CRDT, conflicts are usually invisible (handled by the data structure)
Empty states
- Empty list — “No tasks yet. Create your first one →”
- Empty after filtering — “No matches. Clear filters“
- Empty after action — “Done. Nothing left in your inbox.”
Empty states are part of real-time UX because they are the state right after action; the right empty state confirms “yes, you did the thing.”
Toast and notification balance
- Toast for ephemeral confirmations (“Saved”, “Copied”)
- Persistent banner for actionable issues (“Your subscription is expiring”)
- Modal only for blocking decisions
- Avoid stacking — collapse multiple toasts
- Auto-dismiss for confirmations; require dismissal for errors
Animation discipline
- Subtle micro-interactions confirm actions (small scale, small fade)
- Functional animation (sliding new content into place) more than decorative
- Respect
prefers-reduced-motionmedia query - Cap durations; nothing critical should be locked behind a 1-second animation
Latency masking
- Pre-fetch on hover for likely-next navigation
- Render shell immediately; stream data into it
- Show interactive UI before all data arrives (Suspense pattern)
- Predictive UI for common flows (route preloading, query prediction)
Mobile-specific patterns
- Pull-to-refresh with elastic feedback
- Swipe gestures with rubber-band
- Bottom-sheet patterns for modal interactions
- Haptic feedback on iOS for confirmations
What separates senior from staff
Senior: implements optimistic updates and saving indicators correctly. Staff: thinks systematically about the full set of real-time signals, designs the conflict-resolution UX, and articulates the consistency model the user perceives. Principal: links the UX to the underlying data architecture (CRDT vs OT, eventual vs strong consistency).
Frequently Asked Questions
How do I handle the offline case for a real-time app?
Persist to IndexedDB optimistically. Show “Offline — changes will sync” banner. On reconnect, replay queued operations; reconcile any conflicts.
What library handles real-time presence?
Liveblocks, Yjs (with y-presence), and Automerge are the standards. For simple presence, a WebSocket channel and small state map suffice.
Should I show every keystroke in saved indicator?
Debounce to “Unsaved changes” → trigger save → “Saving…” → “Saved”. Don’t toggle the indicator on every key.