Measure first (React DevTools Profiler + Chrome Performance) — don't guess. Common culprits: derived data recomputed on every render, large lists not virtualized, charts re-rendering on unrelated state changes, expensive layout on every filter change, fetching too eagerly. Fixes: memoize derived data, move heavy computation off the render path (worker / server), virtualize lists, debounce filter input, scope React keys/state to avoid wholesale re-renders.
Diagnose via the changelog and the actual error; in the immediate term pin/rollback to unblock. Long term: lockfiles, semver discipline, isolating third-party deps behind wrappers, automated dependency updates with CI, and a regular upgrade cadence so changes are small.
Redux: a single store, state changed only via dispatched actions run through pure reducers, predictable and debuggable. Redux Toolkit (RTK) is the modern standard — slices, Immer, built-in thunks, less boilerplate. Know when you actually need it vs Context/Zustand/server-state libs.
Strict semver: breaking change → major. Use Changesets for changelog discipline. Mark APIs experimental with `@experimental` JSDoc. Deprecate with console.warn (or peer-dep typing) for one minor cycle before removing. Ship codemods for non-trivial migrations. Maintain release branches for the last 1–2 majors. Type-test with `tsd` / `expect-type` to catch unintended type changes.
BFS (queue): shortest path in an unweighted graph, level-order, 'closest match'. DFS (stack/recursion): connectivity, cycle detection, topological sort, path enumeration, backtracking. BFS uses more memory (frontier width); DFS uses less but can recurse deep. Both must track visited.
Classic: find a target in a sorted array in O(log n). Variants: first/last occurrence, lower/upper bound, search in rotated sorted array, search the answer (binary search on a predicate over a range — capacity, threshold). The pattern is 'monotonic predicate over an index range'.
Pick by need: cookies (sent to server, small, can be HttpOnly/Secure for auth); localStorage (persistent key-value, ~5MB, JS-only, sync API); sessionStorage (same but per-tab session); IndexedDB (large structured async storage, indexable, transactions). Tokens → HttpOnly cookies. UI prefs → localStorage. Big or structured data → IndexedDB.
Render a list of header/panel pairs; track which panel(s) are open in state. Support single vs multi-expand via a prop, animate height, and make it accessible — button headers, aria-expanded, aria-controls, region roles, and keyboard support.
Multiple-choice quiz: given a country, pick the right capital from 4 options. State: questions list, current index, selected answer, score. Generate distractors from other countries' capitals (random, avoid the answer). Show feedback after select, then advance. End with a score summary + restart. Keyboard support and accessibility (radiogroup).
Feature-first folders, data/logic/presentation separation, and deliberate state placement: local by default, lift when shared, server-cache library for server state, Context for low-frequency globals, a store for complex global client state, URL for shareable state.
Render a recursive tree component; track expanded nodes and checkbox state. Checkboxes are tri-state: checking a folder cascades to children, and a parent shows indeterminate when children are mixed. Keep node state in a normalized structure and make it keyboard-accessible.
An open-ended take-home/architecture prompt. Walk the structure: folder/component architecture, a data layer (React Query for server state) vs UI state, routing, all async UI states, responsive design, error boundaries, accessibility, testing, and performance. Show breadth and judgment, not one feature.
Capture pointer position on `pointermove` over a container; render a custom cursor element following with `transform: translate(x, y)` via rAF (not React state — avoid re-render storms). Handle enter/leave to show/hide; use pointer events for cross-input (mouse/pen/touch); throttle to rAF; cleanup listeners on unmount.
Controlled-or-uncontrolled value; a trigger button with `aria-haspopup`/`aria-expanded` and a listbox of options with `role='option'`/`aria-selected`; keyboard support (ArrowUp/Down, Home/End, Enter, Esc, type-ahead); outside-click + Escape to close; focus management; portal for stacking; configurable rendering of items.
CSS custom properties for theme tokens, toggled via a data-attribute/class on <html>. Theme state in Context, persisted to localStorage, defaulting to prefers-color-scheme. Critical detail: apply the theme before first paint (inline script) to avoid a flash of the wrong theme (FOUC).
Controlled input + debounced effect: store the raw input in state, run a debounced effect that fires the search N ms after typing stops. Cancel in-flight requests on new input (AbortController), handle race conditions (ignore stale responses), show loading/empty/error states, and make the listbox accessible.
Two approaches: the native HTML Drag and Drop API (draggable, onDragStart/Over/Drop) or pointer-events math. State tracks the dragged item and drop target; reorder the list immutably on drop. Mention accessibility (keyboard reordering) and that a library (dnd-kit) is the production choice.
A recursive tree: a Node component that renders itself for children. State tracks which folders are expanded (a Set of ids). Folders toggle open/closed, files are leaves. Watch: recursion, expanded-state management, accessibility (role=tree/treeitem, keyboard nav), and lazy-loading large trees.
An open-ended live-coding prompt — clarify the scope first (it likely means a follow-ups/comments/replies thread or a follow-up tasks list). Then model the data, build a recursive or flat-with-parentId structure, handle add/edit/delete, and cover loading/empty/error and accessibility.
A take-home/live exercise judging structure, not just 'does it work'. Set up tooling, organize by feature, separate data/logic/presentation, build small composable components, manage state deliberately, handle loading/error/empty states, write meaningful tests, and document decisions.
State = current index. Prev/next wrap or clamp; dots/thumbnails jump. Add: autoplay with pause-on-hover, keyboard arrows, swipe on touch, lazy-loading images, and accessibility (aria-roledescription, live region, focus management). Use transform for the slide animation.
Controlled inputs for email/password, validation (on blur + submit), per-field error state, submit handling with loading/error states, disabled submit while submitting. Use a <form> with onSubmit, proper input types/labels, and never trust client validation alone — the server re-validates.
Structure a login form as a controlled (or RHF-managed) component: labels tied to inputs, validation on blur + submit with errors linked via aria-describedby and aria-invalid, a loading/disabled state on submit, no secrets in client code, HTTPS-only, tokens in httpOnly cookies (not localStorage), generic error messages to avoid user enumeration, and rate limiting on the server.
Render into a portal, trap focus inside while open and restore it on close, close on Escape and overlay click, lock body scroll, use role=dialog + aria-modal + aria-labelledby, and support controlled open/close. Accessibility is the hard part, not the visuals.
Lift the whole form's data above the steps (parent state / reducer / context) so it survives step navigation. Track currentStep; validate the current step's slice before advancing; keep per-step errors in shared state; allow free Back; persist for refresh-safety; assemble and submit once at the end.
Lazy-init state from localStorage, sync to it with useEffect on change. Wrap reads/writes in try/catch (JSON parse errors, quota, private mode), debounce writes if frequent, version the schema, and sync across tabs with the storage event. Best packaged as a useLocalStorage hook.
Above-the-fold + critical data: high priority, fetched immediately. Below-the-fold: low priority, fetched on idle / when visible. Use `priority` flag on requests, leverage `fetchpriority` attribute for resources, a queue that releases high before low, IntersectionObserver to upgrade priority when a component nears viewport, AbortController to cancel low-priority work when high-priority arrives.
Extends the modal manager: each enqueued modal has a priority (number or enum). Modals queue but only the highest-priority one is shown; a new higher-priority modal preempts the current one (or stacks on top depending on policy). Useful for cohesive UX when multiple systems want to show modals (auth, billing, onboarding, errors).
Polymorphic Button with intent + size + state variants via CVA (class-variance-authority), accessible defaults (real `<button>`, focus ring, disabled semantics, loading state with aria-busy), `asChild` for polymorphism, forwarded ref, icon left/right slots, and theme via CSS variables. Avoid the boolean-prop explosion.
Generic `<Search items={...} getKey filterFn renderItem onSelect />` (or a hook `useSearch`) — controlled input + debounced filter, supports sync or async sources, keyboard nav (Arrow/Enter/Esc), highlighting, loading/empty/error states, and accessible combobox semantics. Decouples the input from how items are sourced and rendered.
Controlled input → debounced query → fetch with AbortController → results. Must handle: debouncing the derived value (not the input), cancelling stale requests to fix out-of-order races, min query length, loading/empty/error states, and not re-creating the debounced fn each render.
A CRUD task UI: add/edit/delete/toggle tasks, filter by status, maybe priority/due date. State is an array of {id, title, done, ...}; immutable updates; stable id keys. Watch for: controlled inputs, derived (not stored) filtered list, edit-in-place mode, and persistence.
State = the rating value; a hover state for preview. Render N stars, fill based on hover ?? value. Support half-stars, read-only mode, keyboard (arrows), and accessibility (radiogroup or a slider role). The trap: use real buttons/inputs, not divs, and make hover preview not clobber the committed value.
List of steps with statuses (done | current | upcoming | error), driven by a `currentStep` prop or URL. Render an accessible `<ol>` with `aria-current='step'` on the active one. Allow click navigation to completed steps; lock forward navigation behind validation; show progress visually with line/circle states.
Track elapsed time from timestamps (not by counting ticks), drive UI updates with an interval or requestAnimationFrame, support start/pause/resume/reset/lap, clean up the timer on unmount, and stay accurate when the tab is backgrounded by computing elapsed = now - startTime.
Keep all form state in one place above the tabs so switching tabs never loses input; render tabs as accessible tablist/tabpanel; validate per-tab but submit the whole form; show per-tab validity/error indicators; and don't unmount inactive tab content if it would drop state.
State = the active tab id. Render a tablist of buttons and the active panel. The grading is accessibility: role=tablist/tab/tabpanel, aria-selected, roving tabindex, and arrow-key navigation between tabs. Often built as a compound component (Tabs/Tab/TabPanel) sharing state via Context.
The full todo app: array of {id, text, done}, add/edit/delete/toggle done, filter all/active/completed. Stable id keys, immutable updates, controlled inputs in a form, edit-in-place mode, derived (not stored) filtered list, empty states, localStorage persistence. useReducer once it grows.
Show clean component decomposition (App → List → Item, plus Input + Filter), a single source of truth for state, immutable updates, keys on list items, controlled inputs, basic accessibility, and persistence to localStorage. Then layer features (edit-in-place, filter all/active/completed, count).
State machine: idle → review → confirming → submitting → success | failure | timeout. Show a clear summary on review; require explicit confirm with an idempotency key; lock UI during submit; handle slow/failed network with retry; show distinct success/failure states with next actions. Disable double-submit. Server is source of truth — never trust a client 'success' flag.
Validate at the right moments (on blur / on submit, then on change once a field is touched/errored), show clear inline messages near the field, never block typing, surface a submit-time summary, mirror server validation, and make errors accessible (aria-invalid, aria-describedby, focus management).
Render only the rows visible in the viewport + a small overscan buffer. Compute visible window from scrollTop / itemHeight. For variable heights, measure rows + use a positions cache. Total scroll height is a spacer div equal to total content height. Adopt TanStack Virtual or react-window — rolling your own is a rabbit hole.
A small loader script injects an iframe hosting your checkout UI. The iframe isolates CSS/JS from the host page; postMessage (with strict origin checks) is the host↔widget channel. Concerns: namespace isolation, security (never trust the host), payment confirmation server-side, responsive overlay, versioning.
An array of N single-char inputs. Key behaviors: auto-advance focus on entry, backspace moves to the previous box, paste fills all boxes at once, accept only digits, expose the joined value. Manage refs in an array, keep state as a string[].
Scalability: code that survives growth in features, contributors, and data. Patterns: feature-folder structure, typed contracts at boundaries, small modules with single responsibilities, dependency direction (UI → domain → infra), monitoring, perf budgets, design-system tokens. Reuse: compose, don't configure; ship primitives + presets; document; favor narrow APIs over deep config.
Beyond layout: responsive means adapting layout, content, interactions, media, and performance to the device. Mobile-first fluid layouts, container queries for components, responsive images, touch vs pointer handling, and shipping less to constrained devices.
No — arrow functions have no own 'this'; they capture it lexically from the enclosing scope, and call/apply/bind cannot override it. They also have no [[Construct]] internal method, so using 'new' with an arrow function throws a TypeError.
`^1.2.3` allows any 1.x.x ≥ 1.2.3 (compatible-with-1.2.3). `~1.2.3` allows any 1.2.x ≥ 1.2.3 (patch only). Both stop at the next significant boundary; the difference is whether minors are allowed.
Class lifecycles split a single concern across multiple methods (`componentDidMount` + `componentDidUpdate` + `componentWillUnmount`). `useEffect` lets you co-locate the whole effect — setup + cleanup — and run it whenever its dependencies change. Effects are about synchronization, not lifecycle moments.
A closure is a function that retains access to variables from the scope it was defined in, even after that outer scope has returned. Lexical scoping means scope is determined by where code is written, not where it's called. Powers module patterns, factories, currying, React hooks' captured values, and event handler state.
Reviews exist to catch defects, share knowledge, and maintain consistency — not to gatekeep. Automate the objective stuff (lint, format, types, tests, CI), keep PRs small, review for correctness/design/readability, give kind specific actionable feedback, and distinguish blocking issues from preferences.
Use `reduce` to chain promises: start from `Promise.resolve([])`, await previous, run next factory, append result. Or use a `for...of` loop with `await` — cleaner. Tasks must be passed as factory functions, not pre-created promises (those already started).
Context: built-in, no extra deps, but every consumer re-renders on any value change — best for low-frequency cross-tree config (theme, auth user). Redux/RTK: predictable + devtools + middleware ecosystem, opinionated boilerplate, best for complex client domains. Zustand: minimal API, selector-based subscriptions, ~1kb, best default for small/medium apps. Server state? None of these — use React Query.
Walk both lists in lockstep with two pointers; at each step compare values; return false on first mismatch or unequal length. O(min(m,n)) time, O(1) space. Variants: compare for value-equality regardless of node identity; deep-compare lists of lists (recursion); compare cyclic lists (detect cycle with Floyd's, then compare lengths + values).
Delay the search request until typing pauses for ~300ms. Implement with a setTimeout that gets cleared on each keystroke — in React, wrap the value in a `useDebouncedValue` hook and fire the request from a `useEffect` that depends on the debounced value. Cancel inflight requests on change.
Build mobile-first with fluid layouts (flex/grid), relative units, and a small set of meaningful breakpoints driven by content — not devices. Add the viewport meta tag, use responsive images, and test on real constraints.
Build a headless, accessible combobox: controlled value, filterable options, single/multi-select via props, full keyboard support (arrows, Enter, Escape, type-ahead), proper ARIA (listbox/option/aria-activedescendant), click-outside dismiss, and virtualization for large option sets.
The 'hello world' of React interviews. State: an array of {id, text} todos plus controlled input. Add appends, delete filters by id. Watch the details: stable keys (not index), controlled input, trim/empty validation, form submit for Enter support.
HTML → DOM. CSS → CSSOM. DOM + CSSOM → Render tree. Layout (compute geometry). Paint (rasterize). Composite (layers). Each step blocks the next on the main thread. Optimize by minimizing render-blocking CSS, deferring non-critical JS, sizing media, and isolating animations to transform/opacity for compositor-only paint.
Currying: transform an n-arg function into a chain of unary functions — `f(a, b, c)` becomes `f(a)(b)(c)`. Partial application: pre-fix some args, returning a function expecting the rest — `f.bind(null, a)`. Both rely on closures. Useful for point-free style, event handlers per item, and composition. Overused, they hurt readability.
Behavioral STAR-style: name a real incident (e.g., chunk hash mismatch after deploy → blank page; service-worker caching stale bundle → users stuck on broken version; CSS purge dropping a runtime-only class). Cover detection (monitoring alert / user reports), immediate mitigation (rollback / kill-switch), root cause, fix, and what changed in the process so it won't recur.
Decide where filtering happens: server-side for truly large data (the right answer at scale), client-side only when the dataset is small enough to ship. Client-side opts: debounce input, memoize results, index/precompute, virtualize the rendered list, and consider Web Workers for heavy filtering.
Assume good intent, get to the shared goal, separate positions from underlying interests, communicate early with data and options, find a tradeoff or escalate constructively, and disagree-and-commit once decided. Use a concrete STAR example.
Core: a store holding state, dispatch(action) → pure reducer → new state, and a subscribe mechanism. Add selectors with reference-equality memoization, middleware for async/side effects, and a React binding via useSyncExternalStore. RTK adds slices + Immer for ergonomics.
Cursor-based pagination + an IntersectionObserver sentinel that triggers loading the next page. Accumulate pages, track hasMore/nextCursor and loading state, virtualize the list, handle errors with retry, and address scroll restoration. Cursor (not offset) keeps it stable under inserts.
Mostly a backend-leaning design: transaction flow (auth → capture), idempotency keys to prevent double-charges, a double-entry ledger as source of truth, state machines for transaction status, async webhooks for settlement, retries with exponential backoff, and reconciliation. Frontend role: never trust the client; confirm server-side.
A client-side limiter that throttles outgoing requests: a token-bucket or sliding-window counter to cap request rate, a queue for excess requests, request deduplication and caching to reduce volume, plus respecting server 429s with backoff. The client limiter is UX/protection, not security.
The core problem is conflict resolution on concurrent edits: OT (Operational Transformation) or CRDTs. Plus: a sync server (WebSockets), local-first optimistic edits, presence/cursors, document model + persistence, offline support, and undo/redo per-user. CRDTs (e.g. Yjs) are the modern go-to.
A context/store holding an array of toasts, an imperative API (toast.success(...)), a portal-rendered container, auto-dismiss timers, and per-toast config (type, duration, action). Discuss queueing, positioning, accessibility (aria-live), and animations.
OAuth 2.0 Authorization Code flow with PKCE: redirect to the provider, user consents, provider redirects back with a code, your backend exchanges it for tokens. Issue your own session (httpOnly cookie or short access + refresh token). Frontend never holds provider secrets; server validates everything.
Vertical infinite-scroll feed of posts (image/video, caption, likes, comments). Cursor-paginated, virtualized for memory; aggressive image/video lazy-load with placeholders and autoplay-on-visible; optimistic likes/comments; stories carousel above feed; pull-to-refresh; double-tap-to-like; SSR/SSG home for SEO; service worker for offline shell.
OOP LLD with clear responsibilities: `ATM` (state machine: idle → authenticated → operating), `Card` (number, expiry, validate), `BankAccount` (balance, withdraw, deposit, transactions), `Transaction` (type, amount, status, ts), `CashDispenser` (inventory by denomination, dispense algorithm), plus a bank service for auth/balance. State machine on the ATM and transactional consistency are the depth pieces.
Combine cursor-based pagination (fetch in pages as the user nears the end) with virtualization (render only the visible window). Use IntersectionObserver to trigger loads, cache pages, handle loading/error/end states, and preserve scroll position. Never hold all items in the DOM or in one fetch.
Model columns and cards as normalized state, render columns mapping over ordered card-id lists, implement drag-and-drop with optimistic reordering, persist moves to the server, and handle real-time multi-user sync, virtualization for big boards, and conflict resolution.
Page composes `<Filters>`, `<ProductGrid>`, `<Pagination>`. URL is the source of truth for filters/sort/page (shareable, back-button safe). Server-side fetch + SSR/streaming for SEO; React Query handles client refetches on filter change. Skeleton states, optimistic UI on add-to-cart, image lazy-load + responsive srcset. A11y: filter region landmarks, announce result count.
Optimistic insert with a tempId → POST → reconcile on response. Local outbox queues failed sends with retry/backoff and offline support. WebSocket fan-out for receiving. Composer state separate from message list. Markdown / mentions / file uploads as composer plugins. Idempotency key on the request so retries don't duplicate.
AI is the broad field of systems performing tasks that need human-like intelligence (classification, prediction, recommendation, planning). Generative AI is a subset whose models create new content — text, images, code, audio — typically via large neural networks trained to model and sample from data distributions.
Chunking is the bundler mechanism of dividing output into multiple files (chunks). Code splitting is the strategy/decision of where to split so code loads on demand. Chunking is the 'how', code splitting is the 'why/where' — every code split produces chunks, but the bundler also chunks automatically.
React.createElement(type, props, ...children) creates a brand-new element from scratch — JSX compiles to it. React.cloneElement(element, props, ...children) copies an EXISTING element, shallow-merging new props over its original ones. cloneElement is for injecting props into children you received.
display:none removes the element entirely — no box, no space taken, not in layout, not announced to screen readers, can't be focused. visibility:hidden hides it visually but it STILL occupies its space in layout and affects siblings. opacity:0 is a third option (visible to a11y/events). Toggling display triggers reflow.
|| falls back when the left side is FALSY (0, '', false, NaN, null, undefined). ?? falls back ONLY when the left side is null or undefined. Use ?? for defaults when 0, '', or false are valid values — || would wrongly discard them.
Promise.all (all succeed or reject on first failure), Promise.allSettled (wait for all, get every outcome), Promise.race (first to settle, success or failure), Promise.any (first to succeed). Plus sequential await-in-loop vs parallel. Choose by: need all? tolerate failures? need just one?
Track page/cursor, page size, items, total/hasMore, plus discrete status (idle/loading/success/error). Distinguish initial load (skeleton) from page change (spinner) from background refetch. Offset vs cursor pagination. A query library handles most of this; know what it's doing.
Pick a project with real technical and/or collaboration difficulty, use STAR, focus on YOUR specific contribution and decisions, be concrete about the challenges and how you resolved them, quantify the impact, and end with what you learned.
Three approaches: (1) sort and index — O(n log n), simple; (2) min-heap of size k — O(n log k); (3) Quickselect — average O(n), worst O(n²). For most interviews say 'min-heap of size k' (clean, predictable) or Quickselect (best average).
ESM (import/export) is statically analyzable → tree-shakable, the modern standard, works in browsers/bundlers. CJS (require/module.exports) is dynamic, the legacy Node format, not tree-shakable. Best practice: ship BOTH via package.json `exports` conditional exports, plus type declarations.
Events propagate in three phases: capture (root → target), target, bubble (target → root). Delegation = listen on a single ancestor instead of N children; use `event.target.closest(selector)` to identify the actual hit. Cheaper memory, works for dynamic children. `stopPropagation` short-circuits; `preventDefault` is unrelated (cancels the default action).
CSR: server ships a near-empty HTML + JS bundle; browser fetches data and renders. Pros: rich interactivity, cheap hosting (static), great for app-shell UIs after first paint. Cons: slow first paint, SEO challenges, bigger JS, blank-screen risk on slow networks. Right for authenticated app dashboards; wrong for content/SEO pages.
A PWA is a web app that uses a service worker, web app manifest, and HTTPS to behave like a native app — installable, offline-capable, push notifications, fast repeat loads. Benefits: reach + install without an app store, offline resilience, one codebase. Limits: less OS integration than native.
CSR renders in the browser (good for dynamic/personalized, bad for first paint/SEO). SSR renders per request on the server (fresh, SEO-friendly, slower TTFB). SSG pre-renders at build time (fastest, cacheable, stale). AI content is dynamic and slow — usually CSR/streaming SSR, not SSG.
CRP: bytes → DOM (HTML parser) + CSSOM (CSS parser) → render tree (matched rules + DOM nodes) → layout → paint → composite. CSSOM is built from all CSS — render-blocking until complete. Specificity, cascade, and inheritance are resolved here. Avoid `@import`, ship minimal critical CSS, defer non-critical styles.
Class components use ES6 classes, this, lifecycle methods, and this.state/setState. Function components are plain functions using Hooks for state and effects. Functions + Hooks are the modern standard — less boilerplate, better logic reuse, no `this` confusion. Both can do the same things.
A controlled component's value lives in React state and is driven by props (value + onChange) — React is the single source of truth. An uncontrolled component keeps its own state in the DOM, read via a ref when needed (defaultValue). Controlled = predictable; uncontrolled = less code, fewer re-renders.
Both are package managers for the npm registry. Yarn originally fixed npm's speed, lockfile, and determinism gaps; npm has since caught up (package-lock, npm ci, workspaces). Today differences are small — Yarn Berry adds PnP/zero-installs; pnpm is the notable alternative with a content-addressed store.
REST: multiple endpoints, fixed response shapes, prone to over/under-fetching, easy HTTP caching. GraphQL: one endpoint, client specifies exactly the fields it needs, great for complex/nested data, but caching and rate-limiting are harder. Neither is universally better.
useEffect runs after render, making it the classic place to fetch data on mount or when dependencies change. You manage loading/error/data state, clean up to avoid race conditions and setState-after-unmount, and re-fetch when deps change. But for real apps, prefer React Query/SWR or framework data loading.
Trees model hierarchy (DOM, file systems, comment threads, nested menus, category trees, virtual DOM). BSTs come up rarely in frontend; you're more often using ordered structures like indexed/keyed maps. Most frontend tree work is traversal (DFS/BFS), recursion, and tree-flatten/find/transform.
Webpack starts from entry points, builds a dependency graph by resolving every import, transforms non-JS files through loaders, applies plugins across the lifecycle, then bundles modules into optimized output chunks. Key concepts: entry, output, loaders, plugins, mode, code splitting.
WeakMap/WeakSet hold their keys/values WEAKLY — if nothing else references a key, it can be garbage-collected, and the entry vanishes. Keys must be objects. They're not iterable and have no size. Use cases: private data per object, caching/memoization keyed by object, and tracking objects without leaking memory.
Frame every decision as: requirement → options → trade-off → choice → constraint check. Lead with what the design is optimizing for; name the alternatives you rejected and why; call out where you'd revisit. Avoid being defensive — interviewers want to see reasoning, not certainty. Practice a 60-second 'pitch' for one project so you can do it cold.
Use a flag service (LaunchDarkly/Unleash/Flagsmith or in-house) with typed flag definitions, evaluate flags with sensible defaults, support targeting/gradual rollout/kill switches, evaluate server-side where possible to avoid flicker, and enforce flag lifecycle hygiene so they don't become permanent tech debt.
Use Array.prototype.reduce for an iterative, idiomatic one-liner, or recursion (head + sum of tail) for the functional approach. Know the tradeoffs: reduce is O(n)/O(1) and safe; naive recursion is O(n) stack space and risks overflow on large inputs.
Count character frequencies in one pass, then scan the string again and return the first character with a count of 1. O(n) time, O(k) space where k is the alphabet size.
Fixed-size sliding window: compute the first window's sum, then slide by adding the incoming element and subtracting the outgoing one. O(n) time, O(1) space — no need to recompute each window.
Three approaches: (a) sort and slice — O(n log n), simplest, fine for small n; (b) min-heap of size k — O(n log k), best when n ≫ k; (c) Quickselect — O(n) average, O(n²) worst, in-place. Pick by data size + whether you need elements in sorted order.
FCP = when the first text/image/SVG paints. Blocked by: slow TTFB, render-blocking CSS, render-blocking synchronous JS in <head>, late-arriving HTML, large HTML. Fixes: CDN + SSR/SSG for fast HTML; inline critical CSS; defer JS; preconnect to third parties; minimize HTML; preload key fonts with font-display: swap.
Walk the array breadth-first: emit all primitives at the current level, queue arrays to process next. This yields top-level values before deeper ones — different from `Array.prototype.flat(Infinity)` which is depth-first. Iterative BFS with a queue is the cleanest implementation.
Naive: nested loop, O(n²). Optimal: process right-to-left maintaining a sorted structure (BIT/Fenwick tree or merge-sort during count) for O(n log n). Explain both; the brute force is usually accepted but mention the optimization.
Covered in depth at [[frontend-system-design-design-a-news-feed-facebook]]. The headline architecture: cursor-paginated server-ranked feed, virtualized list, memoized post components, optimistic likes/comments with idempotency keys, lazy + responsive media, real-time updates via WebSocket or polling, SSR/SSG for first paint and SEO, service worker shell for offline-friendly repeat loads.
WebSocket for real-time messaging; optimistic send with idempotency keys and per-message status (sending/sent/delivered/read); virtualized message list with cursor pagination (load older on scroll up); typing indicators via throttled events; presence via heartbeat; attachments uploaded separately and referenced by id; service worker for offline send queue; push notifications; e2e encryption optional but increasingly expected.
Infinite-scroll feed with cursor pagination; virtualized list for large scrolls; per-post components with mixed media; optimistic likes/comments; ranking served by backend; client caches via React Query keyed on cursor; image/video lazy-load; offline / poor-network resilience; accessibility for the post structure and live updates.
Embeddable widget showing a question + options; user picks one, sees results with percentages and bar visuals. Optimistic UI on vote; idempotent vote (one per user/session, server-enforced); show results after voting or after deadline; real-time updates via polling or WebSocket; handle anonymous vs authed users; rate limit; embed via iframe or script.
Masonry-grid pinboard with infinite scroll over cursor-paginated server feed; variable-height tiles requiring a packing algorithm (CSS columns or JS positioning); virtualization for long boards; aggressive image optimization with placeholders; pin/save interactions optimistic; SSR/SSG for SEO; accessible grid + keyboard nav.
HLS/DASH adaptive bitrate streaming via a player (Shaka/Video.js) sourced from a CDN; manifest + segmented chunks; ABR algorithm picks quality based on bandwidth + buffer; preload posters and metadata; SSR/SSG the catalog pages for SEO and fast LCP; per-row carousels; auth-gated playback; accessibility (captions, focus, keyboard).
Real-time messaging UI: WebSocket connection for live messages, a normalized client store of chats/messages, optimistic sending with delivery states, virtualized message lists, offline queue + local persistence (IndexedDB), pagination of history, presence/typing, and reconnection handling.
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.
Server-side filtering/search/pagination for scale; filters synced to the URL as the source of truth; product listing with cursor or page pagination; aggressive caching (React Query) per filter combination; virtualization or paginated grids; image optimization; and SSR/SSG for SEO and fast first paint.
Two-step: in-order traversal produces a sorted array of node values, then build a balanced BST from that array by recursively picking the middle as the root. O(n) time, O(n) space. Result is a height-balanced BST (not necessarily an AVL/RB) — height ⌈log₂(n+1)⌉.
Yes — frame it around when and why: Zustand for lightweight global client state with selector-based subscriptions and minimal boilerplate; Redux Toolkit for large apps needing structure/middleware/devtools; Jotai for atomic state; and React Query for server state (a different problem).
Axios is a promise-based HTTP client. Interceptors are hooks that run on every request before it's sent or every response before it resolves — used for attaching auth tokens, logging, global error handling, token refresh, and loading indicators. They centralize cross-cutting HTTP concerns.
Structure: business context → user flows → architecture (rendering, state, routing, auth) → data layer (APIs, caching, realtime) → observability → key tradeoffs → what you'd change. Talk about decisions and constraints, not features. Bring a diagram.
Declarations are 'hoisted' to the top of their scope. `var` is hoisted and initialized to `undefined` (no TDZ). `let`/`const` are hoisted but in a Temporal Dead Zone (TDZ) until the declaration line — reading them throws ReferenceError. Function declarations are fully hoisted (callable before the line); function expressions and arrow functions follow the `var`/`let`/`const` rules of whatever declares them.
useCallback memoizes a function so its reference is stable across renders — unless a dependency changes. Closures inside it capture values from the render where it was created; stale deps mean stale values. The function identity matters for child memoization and effect deps.
Create an AbortController, pass controller.signal to fetch, call controller.abort() to cancel. The fetch rejects with an AbortError you should catch and ignore. In React, abort in the useEffect cleanup to kill stale requests and prevent setState-after-unmount and out-of-order responses.
The modern answer is Flexbox (display:flex; justify-content:center; align-items:center) or Grid (display:grid; place-items:center) on the parent. Older/edge approaches: absolute + transform translate(-50%,-50%), or margin:auto inside a flex/grid container. Know which works without knowing the element's size.
Lead with the business impact, not the implementation. Translate tech into outcomes (cost, time, risk, user experience), use analogies, present options with tradeoffs, avoid jargon, tailor to the audience, and confirm understanding. Frame decisions in terms they own.
Minimize what reaches the client, never store secrets in localStorage, use HttpOnly+Secure+SameSite cookies for tokens, enforce HTTPS, prevent XSS (sanitize, CSP, framework escaping), avoid logging sensitive data, mask in the UI, and remember the client is never a trust boundary.
Wrap everything in an IIFE/module so internals stay private via closure; expose exactly ONE namespaced, frozen global (window.Razorpay) whose methods are the public API. Guard against double-injection, avoid prototype pollution, and treat the global's shape as a stable contract.
Prevent hitting limits (debounce, dedup, cache, batch), respect 429s and Retry-After with exponential backoff + jitter, queue or throttle outgoing requests client-side, degrade gracefully in the UI, and surface clear feedback rather than silent failures.
Authentication = who you are (login, tokens/sessions); authorization = what you can do (roles/permissions). Store tokens safely (httpOnly cookies > localStorage), refresh them, guard routes, hide unauthorized UI — but the real enforcement is always server-side. The frontend only reflects auth state.
Lead with curiosity not advocacy — understand their reasoning first. Anchor on shared goals and objective criteria (data, prototypes, constraints). Disagree-and-commit once a decision is made. Use a STAR story showing you changed your mind or supported a decision you lost.
Don't fetch it all — paginate or stream. On the client: virtualize rendering, normalize and cache, process heavy work in a Web Worker, select only needed fields, and use server-side filtering/sorting/aggregation. Push as much data work to the server as possible.
Branch per feature, small focused commits with clear messages, PRs with review and CI, a branching strategy (trunk-based or git-flow), rebase/merge hygiene, and conflict resolution. The collaboration part: communication, small PRs, and not breaking main.
createContext → a Provider component holding state (useState/useReducer) → consumers read via useContext. Key pitfalls: every consumer re-renders when the value changes, the value object must be stable, and unrelated state should be split into separate contexts.
Never call the AI API directly from the browser — proxy through your own backend so the API key stays secret. The backend handles auth, rate limiting, prompt construction, and streaming; the frontend streams the response, renders incrementally, and handles loading/errors/cancellation.
Fluid layouts (flex/grid + %), relative units (`rem`/`em`/`ch`/`vw`/`vh`), breakpoints via `@media (min-width)` (mobile-first), `clamp()` for fluid type, container queries for component-local responsiveness, `<picture>`/srcset for images, and a viewport meta tag. Layer with logical properties (`margin-inline`) for i18n/RTL.
Classify state and place it well (server-cache lib for server state, colocate local state, selector stores for global), keep state low and narrow, split contexts, stabilize references, memoize selectively, and measure with the Profiler. Structure beats sprinkling memo everywhere.
Clarify scope and the real constraint, ruthlessly prioritize must-haves vs nice-to-haves, communicate tradeoffs early, cut scope before cutting quality on critical paths, and protect against burnout-driven bugs. Use STAR with a concrete example.
Serve modern formats (AVIF/WebP), responsive sizes via srcset/sizes, lazy-load offscreen images, prioritize the LCP image, reserve space with width/height or aspect-ratio to prevent CLS, compress and right-size, use a CDN/image service, and consider blur-up placeholders.
Never trust client-side events for money. The browser-side defense is origin-checking postMessage and framing protections, but the real answer: payment success must be confirmed server-to-server (webhook / verify call), not via a postMessage the parent can forge.
Treat it as a portfolio: features deliver visible value; debt delivers velocity. Make debt visible (a backlog of cards with cost and impact, not abstract grumbling); attach debt to feature work when possible ('paying down to ship the new thing'); allocate a steady fraction of capacity (~20%) as ongoing maintenance; escalate when debt actively harms users or shipping speed (the 'unable to deploy on Fridays' moment).
Design tokens + accessible primitives + clear ownership. Single source of truth for tokens (JSON → CSS vars + platform exports), unopinionated headless primitives, contribution model with RFCs + versioning, governance (a small core team), Storybook + visual regression, and automated migration codemods. Treat the DS as a product with its own roadmap, not a side project.
A pipeline: on push → lint/test/build; on merge → deploy. Multiple environments (dev/staging/prod) each with its own config via env vars (not baked secrets). Branch/tag-based promotion, build-once-deploy-many, preview deploys per PR, and prod gated by approvals/checks.
Options: Module Federation shared dependencies (share React etc. as singletons), a versioned internal npm package / monorepo workspace, or import maps. Trade-offs: avoid duplicate bundles and version conflicts vs. coupling. Singletons matter for stateful libs like React; design-system and utils are good share candidates.
The API returns a streaming response (SSE or chunked fetch). Read the ReadableStream from response.body, decode chunks, parse the token deltas, and append to state as they arrive. Handle partial chunks, abort/cancel, errors mid-stream, auto-scroll, and a 'stop generating' control.
Make it reproducible first — gather environment data, use RUM and session replay, test across real browsers. Intermittent + cross-browser usually means a race condition, a browser-engine difference, or a third-party script/extension. Isolate by binary search, instrument the suspect, fix the root cause, add a regression test.
=== is strict equality: same type AND same value, no conversion. == is loose equality: it coerces operands to a common type first, with surprising rules (null == undefined, '' == 0, [] == ![]). Rule: always use === except the one idiom `x == null`.
Destructuring pattern-matches a value's shape: arrays by index, objects by key. Supports defaults (`= 0`), renaming (`{a: b}`), rest (`...rest`), nested patterns, and works in parameters. Under the hood, it desugars to indexed/property access — array destructuring uses the iterator protocol.
At scale, small percentages are huge absolute numbers and the cost of a bug is real money + trust. It changes the bar: gradual rollouts/canaries, feature flags, monitoring before/after, backwards compatibility, idempotency, graceful degradation, more testing, and a bias for reversible, incremental changes.
HTML → DOM, CSS → CSSOM, combined into the Render Tree → Layout (geometry) → Paint (pixels) → Composite (layers). JS can block parsing; CSS blocks rendering. Optimizing the CRP = minimize/defer blocking resources, inline critical CSS, reduce bytes — the foundation of fast first paint.
Tree shaking is dead-code elimination enabled by ES module static structure: imports/exports are statically analyzable, so the bundler builds a dependency graph, marks which exports are actually used, and drops the rest. Requires ESM (not CommonJS), and is hindered by side effects — hence `sideEffects: false`.
Identify what's variable (data, behavior, presentation) vs fixed (the pattern). Lift variable bits to props: typed data, callbacks for behavior, slot props or `children` / `renderItem` for presentation. Keep the API minimal — fewer props that compose well beat many flags. Style via tokens or `className` passthrough. Document with stories/examples.
Debounce delays running a function until it stops being called for a set wait time — each call resets a timer. Implement with a closure over a timer id: clear the previous timeout and set a new one. Used for search-as-you-type, autosave, and resize handlers.
Hold auth state in Context (or Redux), wrap private routes in a guard component that checks auth status, redirect unauthenticated users to login (preserving the intended destination), handle the loading state during the auth check, and remember client-side guards are UX — the server enforces real access.
Throttle = call the function at most once per N ms regardless of how often the trigger fires. Two flavors: leading (fire immediately, then ignore until cooldown ends) and trailing (fire on the last call within the window). Track lastCall timestamp; compare to now; schedule a trailing call if needed.
Measure with Lighthouse (lab) plus field data (CrUX/RUM), and read the categories: Performance (Core Web Vitals — LCP, CLS, TBT), Accessibility, Best Practices, SEO. Improve by fixing the specific opportunities/diagnostics it lists — but optimize the real experience, not the score.
When you detach a method (pass it as a callback), `this` is lost. Preserve it with: .bind(obj) to create a permanently-bound function, an arrow wrapper () => obj.method() that calls it as a method, or arrow class fields. bind is the canonical tool; arrow wrappers are common in callbacks/JSX.
Props — the primary, one-way mechanism: parent passes values/objects/functions as attributes, child receives them as a props object. Data flows down; for child-to-parent, pass a callback prop. For deep trees, Context avoids prop-drilling. children is a special prop for nesting.
In React you don't call store.subscribe directly — useSelector subscribes a component to the slice it reads and re-renders it on change. Under the hood the <Provider> and useSelector use store.subscribe. Vanilla Redux exposes store.subscribe(listener) returning an unsubscribe function.
Same `...` syntax, opposite jobs. Spread EXPANDS an iterable/object into individual elements (copying arrays/objects, passing args). Rest COLLECTS multiple elements into one array (variadic params, destructuring). Both do shallow copies — nested objects are still shared.
Used with forwardRef, useImperativeHandle(ref, createHandle, deps) lets a child customize the value the parent's ref points to — exposing a curated API ({ focus, reset }) instead of the raw DOM node. It runs during commit. It's an escape hatch — prefer declarative props when possible.
Responsive, mobile-first design with fluid layouts and breakpoints; a shared component library; adaptive (not just responsive) where capability differs; progressive enhancement; touch + pointer + keyboard input; performance budgets for low-end devices; and decide native vs web vs PWA per requirements.
Don't keep what the user can't see in the DOM. Virtualize long lists (render only the visible window + buffer), lazy-mount below-the-fold sections, use content-visibility: auto, and defer offscreen images with loading=lazy.
Open a WebSocket scoped to the order, render status from a server-pushed event stream, handle reconnection with backoff and state resync, fall back to SSE/polling, and keep the UI optimistic but reconcilable. Clean up the socket on unmount.
Scaffold with package.json, build to ESM (+ CJS) with TypeScript declarations, set exports/main/module/types fields and the files allowlist, version with semver, test and lint, then npm publish (with CI, provenance, and a changelog). Keep the API small and well-documented.
Keep the input controlled and responsive; debounce the *derived effect* (API call / expensive filter), not the keystrokes. In React: a debounced value via useEffect + timeout, or a stable debounced callback via useMemo/useRef — never re-create the debounced fn each render.
Layer error boundaries: a top-level boundary as the last resort, per-route boundaries for isolation, and granular boundaries around risky widgets. Pair with a window error/unhandledrejection handler, a logging service, user-friendly fallbacks with retry, and recovery on navigation.
Virtualize rows (and columns if wide), paginate/window the data, push heavy sort/filter/aggregation to the server or a Web Worker, apply real-time updates as targeted patches with batching/throttling, and keep the UI responsive with memoization and stable references.
Headless core (state + plugins) + thin UI shell. Composable APIs for column defs, sort, filter, pagination, selection, resizing. Server-side modes for big data. Virtualization for long lists. Adopt TanStack Table for the engine; build the design-system-styled UI on top. Avoid a god-component with 60 props.
Make accessibility the default, not a review-time gate: accessible design-system components, automated linting + axe in CI, accessible-by-default patterns, training, and manual audits (keyboard, screen reader) for what automation can't catch. Bake it into the process, not heroics.
Lift the whole multi-step form's state to a parent (or context/reducer) above the step components, not inside each step. Each step reads/writes its slice; steps unmount without losing data. Persist to storage for refresh-safety, validate per step, and submit the aggregated object at the end.
Prefer declarative props (controlled value + event callbacks) over imperative APIs. When imperative access is genuinely needed (focus, scroll, play), expose a minimal, intentional handle via forwardRef + useImperativeHandle. Compound components for structural APIs.
Measure rows as they render, cache their heights, and maintain a running offset index (prefix sums) to map scroll position to row index. Use estimated heights for unmeasured rows and a ResizeObserver to catch changes after mount.
Default to local state and lift only when sharing is needed. Distinguish server state (cache it with React Query/SWR) from client state. Use Context for low-frequency global values and a store (Zustand/Redux) for complex, high-frequency global state.
Model status as an enum (idle/loading/success/error/empty), not a boolean. Use skeleton screens over spinners for content, match the skeleton to the real layout to avoid shift, debounce indicators for fast loads, and use Suspense for code/data loading. Always handle empty and error too.
Broadcast lightweight ephemeral events (cursor x/y, typing, presence) over a WebSocket, throttle cursor updates (~30-60ms) and interpolate on the receiving side, use heartbeats + timeouts for presence, and keep this transient data out of your persistent document state.
Debounce for 'wait until the user stops' (search-as-you-type); throttle for 'at most once per interval' (scroll, resize). Plus: cancel stale in-flight requests (AbortController), cache/dedupe (React Query), batch, and paginate. Pick the technique by the event's nature.
Capture errors (window handlers + boundaries), performance (Core Web Vitals via RUM), and structured logs/breadcrumbs; enrich with context (user, route, release, session); sample and rate-limit; route to a backend (Sentry/Datadog); add session replay and alerting on SLOs. Mind privacy.
Gate variants behind feature flags / an experimentation platform, assign users to stable buckets via consistent hashing, evaluate server-side (or pre-paint) to avoid flicker, keep the control path unchanged, instrument metrics, and ensure a clean kill switch and exposure logging.
Short-lived access token + long-lived refresh token. Store them safely (httpOnly cookies preferred over localStorage). On a 401, use the refresh token to get a new access token transparently, queueing in-flight requests; if refresh fails, log out. Never trust the client — the server validates the token.
INP measures the worst end-to-end latency from any user interaction to the next paint. Improvements: split long tasks (`< 50ms`), defer non-urgent work (`requestIdleCallback`, React 18 `startTransition`), debounce/throttle handlers, offload to Web Workers, virtualize long lists, avoid layout thrash, and reduce hydration cost (RSC, partial hydration). p75 INP < 200ms is good.
Measure first with the React DevTools Profiler and the 'highlight updates' / why-did-you-render tooling. Identify the cause (unstable props, context, parent re-render), then fix with memoization, state colocation, splitting context, or stabilizing references — not by sprinkling React.memo everywhere.
Options: the `storage` event (fires in OTHER tabs when localStorage changes), the BroadcastChannel API (purpose-built tab-to-tab messaging), or a SharedWorker. Common uses: sync auth/logout, theme, cart. Watch ordering, the originating tab not receiving its own event, and serialization.
Use a form library (React Hook Form) for state, validation, and field arrays rather than hand-rolling. Validate with a schema (Zod/Yup). Track per-field touched/dirty/error; validate on blur + submit. For dynamic inputs use field arrays with stable ids. Always re-validate on the server.
Track currentStep in state; gate 'Next' on validating the current step's slice. Keep per-step errors in shared state, allow free Back navigation, mark steps visited/valid, sync step to the URL for deep-linking and refresh, and block final submit until all steps are valid.
Central queue with a max visible cap, priorities (`error > warning > info > success`), and a dedupe key to merge repeat messages. Important toasts (errors) preempt; low-priority toasts wait. Each toast tracks its own timer; pause on hover; aria-live for screen readers. Use the Sonner / Radix Toast primitives instead of rolling your own.
Decide where filtering lives: server-side for large data (filters become query params, debounced) or client-side for small data (memoized derive from a source list — don't store filtered results as separate state). Sync filters to the URL, handle loading/empty/error per filter change, and cancel stale requests.
Extract the shared logic into a reusable custom hook or a query library so each component/route doesn't re-implement it. Centralize cross-cutting state (auth, theme) in context/store, code-split per route, and use a layout/loader pattern so routes share fetching, loading, and error handling consistently.
Organize by feature, not by file type. Separate presentational from container/logic concerns, extract reusable primitives into a shared UI layer, keep components small and single-responsibility, and colocate related files (component, styles, test, hooks).
Persist locally in localStorage for instant application (read before first paint to avoid FOUC), and sync to the user's server profile so it follows them across devices. Respect prefers-color-scheme as the default, and apply via a data-attribute/class on the root.
Layered: server enforces (authoritative — API endpoints check role on every request); router-level guard component on the client checks role and redirects/denies; conditionally render UI elements (hide admin buttons for non-admins). Never trust the client — the client guard is UX only. Bake role into the auth token; refresh it when roles change.
A ProtectedRoute wrapper checks auth state; if unauthenticated, redirect to /login (preserving the intended URL for post-login return). Handle the loading state while auth resolves to avoid a flash. But remember: route guards are UX — the server must still authorize every request.
Keep two lists: visible (capped at N) and a queue. When a toast is dismissed, promote the next queued one. Track timers per visible toast. Optionally de-dupe and prioritize errors. The key is separating 'added' from 'shown'.
Subscribe to the window resize event in a useEffect, store the dimension in state so a change triggers a re-render, and clean up the listener on unmount. Throttle/debounce the handler for performance. Extract it into a reusable useWindowSize hook; consider ResizeObserver for element-level sizing.
Split into layers: data fetching/state in hooks or container components, pure presentational components that render data via props, and UI controls that raise events upward. Keep rendering components dumb and testable; keep data logic reusable and isolated.
Wrap each route in its own Suspense boundary (loading fallback) and error boundary (error fallback). Use route-level skeletons matching the page layout, and a shared layout that stays mounted while the route content suspends.
Nest boundaries: a global root boundary catches anything that escapes, page-level boundaries isolate each route so one page's crash doesn't blank the app, and optional widget-level boundaries contain risky sections. Inner boundaries catch first; outer is the safety net.
Store dynamic fields as an array of objects, each with a STABLE unique id (not the index). Add = append, remove = filter by id, update = map by id. The id is the React key and the lookup handle — using the index breaks values/focus on removal. A form library's useFieldArray does exactly this.
Fetch inside useEffect with the right dependency array; in the cleanup function, abort the request (AbortController) or set an `ignore` flag so a stale/late response can't update an unmounted or superseded component. Handle loading/error states; consider a query library for real apps.
Typing a URL triggers: DNS resolution → TCP/TLS connection → HTTP request → server response → browser parses HTML, builds the DOM/CSSOM, runs JS, fetches subresources, renders, and paints. Underneath: the client-server model over HTTP, DNS, IP routing, and TCP/IP.
Split by route (lazy-load each page), by component (heavy/below-the-fold/modal components), and by vendor chunks. Use dynamic import() + React.lazy/Suspense, prefetch likely-next chunks, and measure with bundle analysis. Goal: small initial bundle, load the rest on demand.
Memoize a component when it re-renders often (parent re-renders frequently), its props are stable or can be made stable, and its render is non-trivial. Don't memoize cheap components, ones whose props change every render anyway, or as a blanket policy. Always profile first.
Incident-response question. First: mitigate, don't debug — acknowledge, assess blast radius, communicate. Roll back / flip the feature flag to restore service FIRST, then root-cause. Show calm prioritization: stop revenue loss, keep stakeholders informed, only diagnose once users are unblocked.
No — localStorage is scoped by **origin** (scheme + host + port). `https://a.com` and `https://b.com` see different storage. But same origin includes subdomains only if you configure document.domain (deprecated) or use postMessage from an embedded iframe of the original origin. Caveat: XSS on the original site reads it freely.
A closure over a timer id: each call clears the pending timeout and schedules a new one, so the function runs only after calls stop for the wait period. Preserve this/arguments with apply, and add a cancel method for cleanup.
Model the cart as line items with quantities; compute totals as derived state. Apply optimistic UI on user actions, reconcile prices/availability/promotions with the server (the source of truth for money), and push live changes via WebSocket/polling. Handle conflicts gracefully.
Method chaining with deferred async actions: each method enqueues an operation and returns `this` synchronously; an internal promise chain awaits each step. Common interview ask: `driver.start().drive(10).stop().wait(5).honk()` — each call appended to a queue, executed in order, with errors propagating cleanly.
A node tree: `{ tag, attrs, children, parent }`. Operations: `createNode`, `append`, `remove`, `find` (by tag/id/predicate via DFS), `traverse`, `toHTML` (serialize). Mirror the DOM's parent/child invariants — appending a node detaches it from its previous parent. Useful for templating engines, virtual DOM exercises, and tree manipulation problems.
A `Fetcher` with an internal `Map<id, value>`. `get(id)` throws if id absent. `post(id, x)` throws if id already exists; otherwise stores. Tests the basics of class state + invariants. Extensions: `update`/`delete`, custom error types, async simulation, typed generics, eviction.
Recursively flatten a nested array. Show the recursive reduce solution, an iterative stack version (no recursion-depth limit), and mention Array.prototype.flat(Infinity) as the built-in. Discuss the depth parameter.
A recursive descent parser: tokenize the JSON string, then parse values by type (object, array, string, number, true/false/null). Handle whitespace, escape sequences, nesting, and throw SyntaxError on malformed input. The realistic answer is explaining the parser structure, not a flawless full implementation.
Recursively serialize by type: primitives (string→quoted, number/boolean→String, null→'null'), arrays → [...], objects → {...} with quoted keys. Skip undefined/functions/symbols in objects (→ omitted) but render them as null in arrays. Handle the gotchas: NaN/Infinity→null, toJSON(), circular refs throw.
Object.create(proto) makes a new object whose [[Prototype]] is proto. Classic polyfill: a temp constructor function F whose prototype is set to proto, then return new F(). Handle null proto and the optional propertiesDescriptor second argument via Object.defineProperties.
Implement setInterval using recursive setTimeout: schedule a timeout that runs the callback then reschedules itself. This is actually BETTER than native setInterval — it guarantees a full gap between executions and never stacks callbacks. Return a handle with a clear method.
There's no pure-JS way to create a real timer — setTimeout is a host API. The realistic 'polyfill' is a wrapper that adds features (cancellable handle, arg forwarding) or a custom scheduler built on requestAnimationFrame comparing timestamps. The interview is about understanding the event loop and that timers are host-provided.
`useCallback(fn, deps)` returns a stable function reference until `deps` change; equivalent to `useMemo(() => fn, deps)`. Polyfill via the same hook slot machinery: store [fn, prevDeps] across renders; if deps unchanged, return the previous fn; else store and return the new one.
`useContext` reads the value from the nearest `<Context.Provider>` ancestor. Polyfill conceptually: each Context owns a stack of currently-rendering Provider values; useContext returns the top. In React's real implementation it's woven into fiber — but for an interview, a minimal store + Provider that pushes/pops via a render-tracking mechanism suffices.
`useEffect(fn, deps)` runs `fn` after commit if deps changed (or every commit if no deps); calls the previous cleanup before each re-run and on unmount. Polyfill: store `{ deps, cleanup }` in the hook slot; in a 'after-render' phase, compare deps; if changed, run cleanup then fn, save new cleanup.
useImperativeHandle customizes the value a parent's ref sees when used with forwardRef. A polyfill: a hook that, given a ref and a factory, assigns the factory's result to ref.current in a layout effect and cleans up on unmount — keyed on deps.
useLayoutEffect runs synchronously after DOM mutations but before the browser paints — unlike useEffect which runs after paint. A 'polyfill' isn't a userland thing (it needs the commit phase); the real answer is explaining the timing and that on the server it must fall back to useEffect to avoid warnings.
`useMemo(factory, deps)` runs `factory()` on first render and again only when deps change (by `Object.is`); otherwise returns the cached value. Polyfill: store `{ value, deps }` in the component's hook slot array; on render, compare deps to previous; reuse or recompute.
useReducer can be built on useState: hold state in useState, and dispatch = a stable callback (useCallback/useRef) that calls setState(prev => reducer(prev, action)). Support the lazy init (third arg). The insight: useReducer is useState with the update logic centralized in a pure reducer.
useRef returns a stable mutable object { current } that persists across renders and does NOT trigger a re-render when mutated. The polyfill: use useState's lazy initializer once to create and hold a single { current } object — the trick is reusing the SAME object every render.
`useState(initial)` returns `[value, setter]`. Polyfill via the component's hook slot array: on first render, store initial; on subsequent renders, return whatever's in the slot. The setter schedules a re-render (with batching) and writes the new value into the slot.
A flag evaluator: flags defined with rules (boolean, user/role targeting, percentage rollout, environment, date windows), evaluated against a context (user, env). Expose via a provider + useFlag hook. Cache the evaluated flags, support remote config, and default safely when evaluation fails.
A pub/sub keeps a Set of subscribers and a state; `subscribe(fn)` returns an unsubscribe; `setState(updater)` produces next state and notifies. A selector layer wraps it: each subscriber registers a selector + equalityFn (default `Object.is`) and is only re-notified when its selected slice changes — the kernel of Redux/Zustand.
A queue with a configurable max concurrency: `add(task)` returns a promise; running count is tracked; when one completes, the next pending task starts. Supports success/error callbacks, custom executors, and `drain` / `idle` events. Core data structure: a FIFO queue + a `running` counter + a `dequeue()` step on settle.
A `Map<eventName, Set<listener>>` with `on`/`off`/`emit`/`once`. `on` returns an unsubscribe function. `emit` calls listeners with the payload (synchronously by default; isolate errors so one bad listener doesn't break the rest). Supports wildcard or namespacing as extensions. Foundation for pub/sub, custom stores, and decoupled module communication.
Standard debounce: a closure holding a timer, cleared and rescheduled on each call, fires after quiet time. On a payment form the risk is real money: debounce on the SUBMIT can drop a click or fire late; the right tools there are disable-on-submit + idempotency keys, not debounce. Debounce validation, not the charge.
IntersectionObserver on a sentinel near the bottom triggers loading the next page; cursor pagination (offset breaks under inserts); accumulate pages; show loading/empty/error states; restore scroll on back-navigation via cached pages (React Query); virtualize once the list is long. Bonus: 'load on hover near end' for desktop polish.
`limitConcurrency(tasks, limit)` runs N workers in parallel; each pulls the next task index until done; returns results in original order. Cleaner than queueing tasks one by one. Pattern: a shared index, N async worker functions started in parallel, each looping until index is past the end.
Explicit coercion is when you deliberately convert a type (Number(x), String(x), Boolean(x)). Implicit coercion is when JS auto-converts during operations (==, +, if conditions, template literals). Implicit is the source of many bugs — prefer explicit conversion and ===.
Classic 'parent removes an item, child rendering that item crashes during the same render' bug. Fixes (best to worst): guard with conditional rendering in the parent (don't render the child if the item is gone); read the item by id in the child and bail to null if undefined; use a key that changes on removal so the child unmounts cleanly; move the read to a selector that returns undefined safely.
Key decisions: a small, intuitive surface; sensible defaults with progressive disclosure; consistency and naming; controlled vs uncontrolled; composition over configuration; TypeScript types as the contract; semver discipline; tree-shakability; minimal peer deps; good errors; and docs. Optimize for the consumer, and for change.
Sliding window with a Map<char, lastIndex>. Move right pointer through the string; when the current char's last index is within the window, jump left to one past it; track max window size as you go. O(n) time, O(min(n, alphabet)) space.
Make debt visible and tracked, distinguish deliberate from accidental debt, quantify its cost in business terms, pay it down continuously (boy-scout rule + dedicated capacity) rather than via mythical big rewrites, and prevent new debt with standards and reviews.
Mentoring: pair on real PRs, run weekly 1:1s, set growth goals tied to outcomes, give specific feedback close to the moment, model good debugging by thinking aloud. Interviews: structured rubric, calibrate across the panel, focus on signal not trivia, write detailed notes, separate observation from judgment, take a debrief seriously.
Strangler-fig pattern: strangle the legacy app route by route or feature by feature, not big-bang. Run old and new side-by-side behind a reverse proxy or feature flag; share auth/session via cookies; migrate the highest-value pages first; back the migration with metrics (perf, bug rate, conversion); kill the legacy code path only when usage is zero.
Layer it: error tracking (Sentry) for exceptions + source maps, RUM for real-user performance (Core Web Vitals), product analytics for behavior, and synthetic/uptime checks. Add error boundaries, global handlers, alerting with thresholds, release tracking, and PII scrubbing. The goal: know it broke before users tell you.
A general technique question: start with a correct brute force, analyze its complexity to find the bottleneck, then apply a pattern — hashing for lookups, sorting, two pointers, sliding window, precomputation/prefix sums, memoization/DP, or better data structures — to cut redundant work.
It depends entirely on HOW the method is called, not where it's defined. obj.print() → 'this' is obj. But pull the method off (const p = obj.print; p()) and 'this' is lost (undefined/global). Arrow methods capture lexical 'this', not obj. The lesson: 'this' is determined at call time.
A peerDependency declares 'I need this, but the host app provides it' — used for shared singletons (React, a router) where multiple copies break things. The host installs one shared version; you don't bundle it. Use a wide version range; the host is responsible for satisfying it.
For each node in a binary tree, point its `next` to the node immediately to its right at the same level (or null if last). Two approaches: BFS using a queue (O(n) time, O(n) space); level-by-level traversal using the `next` pointers built in previous levels for O(1) extra space (the canonical perfect-tree solution).
WebSockets for bidirectional, low-latency interaction (chat, collab, presence); SSE for one-way server→client streams (notifications, feeds, live status) — simpler, auto-reconnecting, over HTTP; polling as the low-effort fallback. Choose by directionality, then handle reconnection, resync, and scale.
**Reflow (layout)**: recompute box geometry — triggered by anything that changes size/position (width, font-size, DOM insertion, viewport resize). Expensive. **Repaint**: redraw pixels without re-layout — triggered by color/visibility changes. Cheap-ish. **Composite-only** (transform/opacity on a GPU-promoted layer) avoids both. Animate with transform + opacity for 60fps.
CSR: server sends shell + JS; client renders. Fastest TTFB, slowest meaningful paint, weak SEO. SSR: server renders HTML per request; better LCP/SEO, higher origin cost. SSG: pre-rendered at build, served from CDN; fastest globally, but stale until rebuild. Streaming SSR: server flushes HTML in chunks as it's generated — fast FCP + good LCP. RSC: server components ship zero JS for non-interactive parts.
Batch incoming messages instead of one setState per message; React 18 auto-batches but a buffer + flush on rAF/interval helps for bursts. Virtualize the message list. Keep updates off the critical path: process/parse in chunks or a Web Worker, use stable keys, and memoize message rows.
Run a single BFS/DFS from any node and check whether every node was visited. O(V+E). For directed graphs, distinguish weak vs strong connectivity (strong needs Kosaraju/Tarjan or a check from one node on both the graph and its transpose).
A service worker is a background script that proxies network requests for its scope. Use cases: offline support, asset caching for instant repeat loads, background sync of queued mutations, push notifications. Lifecycle: install → waiting → activate → fetch interception. Update gotcha: the new SW activates only after old tabs close — handle with skipWaiting + a 'reload to update' UX.
Find the max in every window of size k. Use a deque storing indices in decreasing-value order: each element is pushed and popped at most once, giving O(n) time, O(k) space — versus O(n*k) brute force or O(n log n) with a heap.
Maintain a window [l, r] over an array/string; expand by moving r, shrink by moving l when the window violates a constraint. Turns O(n²) brute force into O(n) for problems like 'longest substring without repeating chars', 'min window substring', and 'max sum of subarray of size k'.
Drive the form from a schema/config, not hardcoded JSX. Compute visible fields from current values, validate conditionally (only visible fields), clear or preserve hidden-field values intentionally, and use a form library (React Hook Form) for performance and field arrays.
TTFB = time from navigation request to first byte of response. Target: < 200ms cached, < 600ms uncached. Drivers: DNS, TCP/TLS, server response time, redirect chain. Fix: CDN edge caching, geographically close origin, HTTP/2/3, reduce origin work (caching layer, async work moved off the critical path), eliminate redirects, persistent connections.
TBT = sum of (long task duration − 50ms) between FCP and TTI. Measures how long the main thread is blocked, blocking input. Drivers: large JS bundles, heavy parse/execute, long-running event handlers, sync work in effects. Fixes: code split, defer/lazy JS, move heavy work to workers, break long tasks into chunks (yielding to the event loop), debounce work in handlers.
Recursively walk the structure; if a value is a function call it (with optional context), replace inline; if an array or object, recurse. Mind cycles with a `WeakSet`; mind async (await thenables) and class instances (don't recurse into them).
Ship ESM with named exports, mark `sideEffects: false` (or list the files that have them), avoid import-time side effects, prefer many small pure modules over barrels that pull everything, don't re-export huge namespaces, and keep modules pure so bundlers can drop what consumers don't use.
Use two pointers when scanning a sorted array or linked list and a nested loop would be O(n²). Variants: opposite ends converging (sorted two-sum, palindrome, container with most water), and fast/slow (cycle detection, middle of list). Turns O(n²) into O(n) with O(1) space.
`typeof` returns a primitive type string but is wrong for null ("object") and arrays. `instanceof` checks the prototype chain but fails across realms (iframes). The reliable cross-realm check is `Object.prototype.toString.call(x)` → "[object Array]" etc. For primitives use `typeof`; for arrays `Array.isArray`; for null `=== null`; for plain objects there's no perfect check.
Reproduce systematically — gather environment data, use RUM/session replay, test across browsers. Intermittent + browser-specific points to race conditions, CSS/JS engine differences, third-party scripts, or extensions. Isolate, instrument, and narrow with a binary search.
A STAR-style ownership question. Pick a real bug, own it without blame, explain detection → mitigation → root cause → fix, then focus on what CHANGED systemically afterward: a test, a process, a guardrail. The interviewer wants accountability + a learning/prevention mindset, not the bug's technical depth.
Map/Set variants that hold their keys/values weakly — if nothing else references the object, it can be garbage-collected and the entry disappears. Keys must be objects; not iterable; no size. Use for: per-object private data, object-keyed caches that self-clean, and marking objects without leaking memory.
Three client-side storage mechanisms with different lifetimes, scopes, capacities, and access models. localStorage persists indefinitely (~5MB, same-origin), sessionStorage clears on tab close, cookies (~4KB) are sent on every HTTP request and are the only one usable by the server.
`dependencies` are needed at runtime; `devDependencies` are only needed for building, testing, or linting. Consumers of your package install dependencies but skip devDependencies.
import() is a function-like expression that loads a module on demand and returns a Promise. It enables code splitting, lazy loading, conditional/feature-flagged loading, and reduced initial bundle size — the foundation of React.lazy and route-based splitting.
Three unrelated terms. Cache-Control: HTTP header dictating if/how long a response is cached. ETag: a content fingerprint for conditional revalidation (304 Not Modified). DocumentFragment: a lightweight, off-DOM container for batching DOM nodes so insertion triggers one reflow.
A higher-order function takes a function as an argument, returns a function, or both. Examples: map/filter/reduce, setTimeout, event listeners, and function factories. They're the basis of composition, currying, and decorators in JS.
LLMs (Large Language Models) are very large neural networks trained on massive text corpora for broad, capable language tasks. SLMs (Small Language Models) are compact models — fewer parameters — that trade some capability for lower latency, lower cost, on-device/edge deployment, and privacy.
Pseudo-classes (:hover, :focus, :nth-child) style elements in a particular STATE or position — single colon. Pseudo-elements (::before, ::after, ::first-line) style or create a specific PART of an element — double colon. One targets state, the other targets sub-parts.
Resources the browser must download and process before it can render the page — primarily CSS (blocks rendering) and synchronous JS in <head> (blocks parsing). They delay first paint. Fixes: inline critical CSS, defer/async JS, load non-critical CSS lazily, minify, and use font-display.
Five OOP design principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion. They aim for maintainable, decoupled code. In frontend they map to: focused components/hooks, extension via props/composition, consistent contracts, minimal prop interfaces, and depending on abstractions.
Two stacks (undo and redo) holding either inverse-operation commands or state snapshots. Push to undo on each action and clear redo; undo pops to redo and vice versa. Use the Command pattern for memory efficiency; cap history size and consider patches for large state.
`forwardRef` forwards a ref through a component so parents can access the underlying DOM node or imperative API. `useImperativeHandle` customizes what the ref exposes — handy when you need to expose specific methods (`focus`, `scrollIntoView`, `open()`) without leaking the DOM node. In React 19, regular function components accept ref as a prop, removing the need for forwardRef.
Load lifecycle events: DOMContentLoaded (DOM parsed, before images/stylesheets finish), load (everything including subresources done), beforeunload/unload/pagehide (leaving), readystatechange. Plus the modern way: Performance API / PerformanceObserver and Core Web Vitals (LCP, FCP, CLS) for real measurement.
URL → DNS lookup → TCP + TLS handshake → HTTP request → server responds with HTML → browser parses HTML→DOM, CSS→CSSOM, runs JS → render tree → layout → paint → composite (the Critical Rendering Path). Know the network phase AND the rendering phase, and what blocks what.
Extract hooks where logic is duplicated, stateful, or involves effects/subscriptions: data fetching, form handling, debounced values, media queries, local storage sync, intersection observers, pagination, and event listeners. Extract for reuse or clarity — not prematurely.
Both integrate changes from one branch into another. Merge creates a merge commit joining two histories (non-destructive, preserves the actual history). Rebase replays your commits on top of the target branch, creating a linear history but rewriting commits. Never rebase shared/public branches.
A backlog is the prioritized list of work not yet done. The product/feature backlog holds upcoming features, stories, bugs, and tech debt — owned by the PM and continuously groomed. The sprint backlog is the slice pulled into the current sprint. Backlog grooming/refinement keeps it estimated and prioritized.
Passing props through multiple intermediate components that don't use them, just to reach a deep descendant. It makes code verbose, tightly couples components, and is fragile to refactor. Fixes: Context, composition (children/slots), or a state library — pick by how the data is used.
Redux is a predictable global state container based on a single store, read-only state, and pure reducers updating state via dispatched actions. Modern Redux uses Redux Toolkit: createSlice generates the reducer + action creators with Immer-powered 'mutating' syntax.
Put state as close as possible to where it's used. The wrong default — 'lift everything to App' — re-renders the whole tree for any change and obscures ownership. Colocation: input state in the input, modal-open state in the modal trigger, filter state in the filter panel. Lift only when multiple siblings need it; even then, lift to the lowest common ancestor.
px is an absolute, fixed unit. em is relative to the current element's font-size and compounds when nested. rem is relative to the root (html) font-size — consistent, no compounding. Use rem for scalable, accessible sizing; em for things that should scale with their local context.
Props are inputs passed into a component by its parent — read-only from the component's view. State is data the component owns and manages internally, and can change over time. Props flow down and are immutable; state is local, mutable (via setState), and triggers re-renders.
alt provides a text alternative for an image: it's read by screen readers, shown if the image fails to load, used by search engines, and helps when images are disabled. Write it to convey the image's purpose/content; use empty alt="" for purely decorative images.
Match the fallback to the error: 404 = friendly 'not found' with search + popular links; 500 = apologetic error with retry and a status link; network = inline retry with cached content if available; auth (401/403) = redirect to login / show 'no access'; chunk-load failure = soft reload prompt. Always: clear copy, a next action, no stack traces, log to monitoring.
Build a layered set: design-system primitives (Button, Input, Modal, Toast), composite components (DataTable, Form, Dropdown), layout primitives (Stack, Grid), and behavioral patterns/hooks (useDisclosure, compound components, render props). Reusability comes from good APIs, not just existence.
Serve assets from a CDN with edge locations worldwide, render/respond from the edge or regional servers, minimize and split bundles, optimize the critical rendering path, cache aggressively, optimize images, and measure with field data segmented by region.
Vitest/Jest + React Testing Library for unit/integration tests focused on user behavior, Playwright/Cypress for end-to-end critical flows, MSW for mocking the network. Follow the testing trophy — invest most in integration tests.
For collaborative editing, use CRDTs (Yjs/Automerge) or Operational Transformation to merge concurrent edits without conflicts. Send operations not snapshots, keep edits commutative/convergent, apply optimistic local updates, and reconcile via a server. For simpler cases, last-write-wins or field-level locking.
Reflow (layout): recalculating element geometry — positions and sizes. Repaint: redrawing pixels (colors, visibility) with geometry unchanged. Reflow is more expensive and triggers a repaint; repaint alone doesn't trigger reflow. ('Rework' isn't a real browser term.) Composite-only props (transform/opacity) skip both.
Use a caching data layer (React Query/SWR) for dedup, caching, and background refresh; avoid waterfalls by fetching in parallel or co-locating with the router; fetch only what you need; paginate/virtualize large sets; prefetch likely-next data; and apply optimistic updates for mutations.
Memoization (memo/useMemo/useCallback) helps when it prevents an expensive computation or a costly subtree re-render, AND the deps are actually stable. It hurts when the work is cheap, deps change every render, or you wrap everything reflexively — then you pay comparison + memory cost for nothing.
Avoid functional iteration when: you need early exit (use `for` / `for..of` / `find` / `some`), you're chaining many passes over a large array (multiple traversals + allocations — use one for loop), the side effect is the point (`forEach`, but `for..of` reads better), or readability suffers from a tangled `reduce`. Idiomatic for transforms; not a universal hammer.
Don't use useEffect for: deriving state (compute during render); transforming data for rendering (compute or useMemo); responding to user events (do it in the handler); resetting state on prop change (use a `key`); chaining state updates (combine into one). Effects are for *synchronizing with external systems* — DOM, subscriptions, APIs — not for in-app data flow.
`target='_blank'` opens a new tab/window where the new page gets a `window.opener` reference back to your page — letting it run `window.opener.location = 'phishing-url'` (reverse tabnabbing). `rel='noopener'` blocks that. `rel='noreferrer'` additionally strips the `Referer` header. Modern browsers add `noopener` implicitly, but the rel attribute remains best practice.
An index isn't tied to the item — it's tied to the position. Insert, delete, reorder, or filter the list and the same index now points to different data, so React reuses the wrong DOM node and component state: wrong input values, misplaced focus, stale state, broken animations. Use a stable id.
A judgment/communication question (often after a take-home or system design). Show you decide deliberately: state the requirements/constraints, the options considered, the trade-offs, why you picked this one, and what you'd revisit. The wrong answer is 'it's what I know' or no trade-off awareness.
With empty deps, the callback closes over the state value from its first render — forever stale. setCount(c => c + 1) reads the latest state from React's updater queue instead of the captured variable, so the callback stays correct without listing state as a dependency.
The dependency array tells React WHEN to re-run the effect — it compares deps between renders and re-runs only if one changed. No array = every render; []= once on mount; [a,b] = when a or b changes. It also defines which values the effect 'sees'; omitting deps causes stale closures or infinite loops.
Connect specific things about Razorpay (fintech problem space, scale, product breadth, engineering culture) to your own goals and strengths. Be concrete and researched — avoid generic 'great company' answers.
`useCallback(() => setCount(count + 1), [])` captures `count` from the render the callback was created (closure). Empty deps → it never updates → always uses the initial `count`. Fix: include `count` in deps (callback identity changes), or use the functional setter `setCount(c => c + 1)` (doesn't read outer `count`), or a ref for 'latest'.
Without the viewport meta tag, mobile browsers render at a ~980px virtual width and scale down — text becomes tiny, taps misalign, and media queries never trigger because the layout viewport ≠ visual viewport. The standard tag `<meta name="viewport" content="width=device-width, initial-scale=1">` ties layout width to device-independent pixels so responsive CSS works as designed.
Smaller initial bundle → faster parse/execute → better LCP/INP. Users only download code for the route they hit; rest loads on demand or prefetched on hover. Cuts bytes 30–80% on first paint typically. Tradeoff: cold-load delay when navigating to a non-prefetched route, so prefetch likely-next routes.
Keys give list items a stable identity so React's reconciler can match elements across renders. Good keys = correct minimal updates. Bad keys (index, or random each render) cause wrong state/DOM reuse, lost focus and input values, broken animations, and unnecessary re-renders.
React detects changes by reference (Object.is) — mutating state in place keeps the same reference, so React doesn't know it changed and skips the re-render. Mutation also breaks memo/PureComponent, corrupts previous-state snapshots, and makes time-travel/debugging unreliable. Always create a new object/array.
Given a string `s` and a dictionary, return all ways to segment `s` into dictionary words. DFS/backtracking with memoization on the suffix: for each position, try every dictionary word that matches the prefix and recurse on the remainder; cache results by start index. Time worst-case exponential; memoization makes practical inputs tractable.
Global theming as the foundation (design tokens via CSS custom properties on the root), with scoped overrides where genuinely needed — a subtree can redefine tokens locally. Per-component theming should be the exception, layered on the global system, not a parallel system.
Default to a battle-tested library (react-window, @tanstack/react-virtual, virtua) — they handle the gnarly edge cases. Build your own only when you have unusual requirements the libraries can't meet, and understand the core algorithm either way.
Use the two-pointer technique: one pointer from each end moving inward, comparing characters. O(n) time, O(1) space — no reversal, no extra allocation. Clarify whether to normalize case/whitespace/punctuation first.
Currying via closures: each call returns a function that captures the running product. The trick is the function must also coerce to its value — implement valueOf/toString so mul(2)(3)(4) evaluates to 24 when used as a number.
Avoid a big-bang rewrite. Assess and set goals, then migrate incrementally with the strangler-fig pattern — run old and new side by side, migrate route-by-route or feature-by-feature, keep shipping product, add tests as a safety net, and measure progress.
Don't render it all: virtualize the visible window, paginate/infinite-scroll the data. For heavy computation, move it off the main thread (Web Worker) or chunk it across frames; use React's startTransition/useDeferredValue to keep input responsive. Stream and process incrementally.
Lighthouse is a synthetic lab test of initial page load. It misses runtime/interaction performance, real-device and real-network variance, post-load JS jank, slow API responses, and the specific user flows that feel slow. Measure with field data (RUM) and profile the actual interactions.