Frontend system design: Designing browser-like features
Designing features like tabs, history/back-forward, bookmarks, find-in-page, or a mini-renderer. Model the state machine, choose the right data structures (stacks for history, trees for the DOM), handle persistence and lifecycle, manage memory, and reason about performance and edge cases.
"Browser-like features" — tabs, navigation history, bookmarks, find-in-page, a tab manager, a mini DOM renderer — test whether you can model a stateful system, pick data structures, and reason about lifecycle, memory, and edge cases. Approach it like any system design: clarify scope, model state, then go deep.
1. Clarify scope
Browsers are huge — pin down which feature and which behaviors. "Tabs" could mean just switching, or also drag-reorder, persistence, lazy-loading, and crash recovery. Agree on the subset.
2. Model the state + data structures
Each feature has a natural structure:
- Navigation history (back/forward) — two stacks (back, forward). Navigate → push current to back, clear forward. Back → pop back, push to forward. Same shape as undo/redo.
- Tabs — an ordered list of tab objects
{ id, url, title, state, lastActive }+ anactiveTabId. Reorder = array move. - DOM / mini-renderer — a tree; rendering = recursive traversal.
- Find-in-page — text index + a list of match ranges + a current-match pointer.
- Bookmarks — a tree (folders) or flat list with tags.
Naming the right structure quickly is the core signal.
3. Lifecycle & persistence
- Persistence — tabs/history/bookmarks survive restarts → localStorage/IndexedDB; restore on load.
- Lifecycle — tabs mount/unmount; suspend inactive tabs (don't keep 100 live) and restore on activation.
- Crash recovery — periodically snapshot session state.
4. Memory management — the part that separates levels
A browser can't keep everything live:
- Lazy-load — don't render a tab's content until activated.
- Suspend / evict — serialize inactive tabs' state, free their DOM/memory, rehydrate on return (this is exactly what real browsers do).
- Cap in-memory instances; LRU-evict the rest to storage.
5. Performance
- Virtualize if there can be thousands of items (a huge history list, a tab overflow menu).
- Debounce expensive work (find-in-page search on input).
- Keep switching instant — pre-warm the likely-next tab.
6. Edge cases (always raise these)
- History: navigating mid-stack truncates forward; duplicate entries; max depth.
- Tabs: closing the active tab (which becomes active next?), closing the last tab, restoring a closed tab.
- Find: zero matches, wrapping past the last match, matches inside collapsed/hidden content, live DOM changes.
- Persistence: corrupted/stale stored state; storage quota exceeded.
The framework
Scope it down → model state with the right data structure → handle lifecycle & persistence → manage memory (lazy/suspend/evict) → performance → edge cases. Browser-like features are really "stateful systems with constrained resources" — show you can reason about all of it, not just the happy path.
Follow-up questions
- •How would you model browser back/forward history?
- •How do real browsers keep memory bounded with many open tabs?
- •How do you persist and restore session state across restarts?
- •What edge cases matter for a find-in-page feature?
Common mistakes
- •Not scoping the feature down — trying to design 'a browser'.
- •Picking the wrong data structure (e.g. not using two stacks for history).
- •Keeping every tab fully live in memory.
- •Ignoring persistence, restoration, and crash recovery.
- •Designing only the happy path.
Performance considerations
- •Memory is the constraint — lazy-load, suspend, and LRU-evict inactive instances to storage. Virtualize large lists (history, tabs). Debounce search. Keep tab switching instant via pre-warming.
Edge cases
- •Navigating from the middle of history (forward stack truncation).
- •Closing the active or last tab.
- •Find-in-page with zero matches or live DOM changes.
- •Corrupted or quota-exceeded persisted state.
Real-world examples
- •Browser tab suspension/discarding to bound memory.
- •Back/forward as two stacks; session restore after a crash.