Frontend and browser interview questions
Browser and React fundamentals that frontend interviews return to: rendering, security, storage, workers, reconciliation, effects, rendering strategies, Core Web Vitals and accessibility.
Answer out loud or on paper before revealing. If your answer misses a key point, revisit the linked guide or explainer.
What happens between the browser receiving HTML and the first pixels on screen? What blocks rendering?
Reveal model answer
The browser parses HTML into the DOM and CSS into the CSSOM, combines them into a render tree of visible nodes with computed styles, runs layout to compute geometry, then paints and composites layers. CSS is render-blocking because the browser will not paint without styles, and classic synchronous scripts are parser-blocking because they may modify the DOM and must wait for preceding CSS. You speed it up by shipping less critical CSS, using `defer`, `async` or module scripts, preloading the LCP resource, and reducing bytes and round trips.
WHAT A STRONG ANSWER COVERS
- DOM + CSSOM, render tree, layout, paint, composite.
- CSS blocks rendering; synchronous scripts block parsing.
- `defer` and module scripts run after parsing in order; `async` runs whenever it arrives.
- `display: none` elements are not in the render tree; `visibility: hidden` ones are.
- Preload or prioritise the LCP image and avoid lazy-loading it.
RED FLAGS
- Saying the browser waits for all JavaScript and images before painting anything.
- Putting large synchronous scripts in the head without defer.
LIKELY FOLLOW-UPS
- What is the difference between async and defer?
- How would you find what is delaying LCP on a real page?
SOURCES
What is the difference between reflow and repaint, and what is wrong with `for (const el of items) el.style.width = container.offsetWidth + "px";`?
Reveal model answer
Reflow (layout) recomputes element geometry and can cascade through the tree; repaint redraws pixels for visual changes such as colour without changing geometry; compositor-only changes like transform and opacity can skip both. The loop alternates a layout read (`offsetWidth`) with a style write, so each iteration forces a synchronous layout: layout thrashing. Read once before the loop and then do all the writes, or batch reads and writes per frame.
WHAT A STRONG ANSWER COVERS
- Layout is the expensive step; paint is cheaper; composite-only is cheapest.
- Reading geometry after a write forces synchronous layout.
- Batch reads before writes; use requestAnimationFrame for visual updates.
- Animate transform and opacity rather than width, height or top.
- Large DOMs make every layout slower, which affects INP.
RED FLAGS
- Thinking repaint and reflow are the same thing.
- Animating layout properties with JavaScript on every frame.
LIKELY FOLLOW-UPS
- Which DevTools panel shows forced reflows?
- What does `content-visibility: auto` do for long pages?
SOURCES
What is CORS, when does a preflight happen, and why does `fetch(url, { credentials: "include" })` fail against a server that returns `Access-Control-Allow-Origin: *`?
Reveal model answer
CORS is a browser-enforced protocol that lets a server opt in to letting other origins read its responses; it relaxes the same-origin policy and does not protect the server from non-browser clients. Requests that are not 'simple', such as PUT or DELETE, custom headers, or a JSON Content-Type, trigger an OPTIONS preflight that must be answered with matching Allow-Methods and Allow-Headers. Credentialed requests cannot use wildcards: the server must echo the exact origin and send `Access-Control-Allow-Credentials: true`, so `*` makes the browser block the response.
WHAT A STRONG ANSWER COVERS
- Enforced by the browser; curl and servers ignore it.
- Simple requests skip preflight; JSON bodies and custom headers do not.
- Credentials require an explicit origin, not `*`, plus Allow-Credentials.
- Fix on the server (or a same-origin proxy), not with a browser extension.
- Add `Vary: Origin` when the allowed origin is dynamic.
RED FLAGS
- Thinking CORS is a server-side security control.
- Reflecting any Origin header with credentials allowed.
- Using `mode: 'no-cors'` to 'fix' it, which yields an unreadable response.
LIKELY FOLLOW-UPS
- Why can a CORS error hide a 500 from the server?
- How does Access-Control-Max-Age help?
SOURCES
Compare cookies, localStorage, sessionStorage and IndexedDB. Where would you keep a session token?
Reveal model answer
Cookies are small, sent automatically with every matching request, and can be HttpOnly, Secure and SameSite, which makes them the right place for session identifiers. localStorage and sessionStorage are synchronous string key-value stores of a few megabytes per origin; localStorage persists, sessionStorage is per tab, and both are readable by any script on the page, so an XSS can steal anything in them. IndexedDB is asynchronous, transactional, stores structured data and blobs, works in workers, and suits offline data and caches.
WHAT A STRONG ANSWER COVERS
- Session tokens: HttpOnly, Secure, SameSite cookies.
- Web Storage is synchronous, string-only and exposed to XSS.
- IndexedDB is async, larger, structured and available in workers.
- Cookies add bytes to every request; keep them small.
- All storage is per origin and can be evicted or cleared.
RED FLAGS
- Storing long-lived access tokens in localStorage without understanding the XSS risk.
- Storing large data in cookies.
LIKELY FOLLOW-UPS
- How would you persist offline edits and sync them later?
- What does the `__Host-` cookie prefix guarantee?
SOURCES
Explain XSS and CSRF, how they differ, and where Content Security Policy fits.
Reveal model answer
XSS is attacker-controlled script running in your origin, usually from untrusted data reaching an HTML sink like innerHTML or an unescaped template; it can do anything the user can. Defend with context-aware output encoding (frameworks escape by default), sanitising any HTML you must render, and a strict CSP using nonces or hashes as defence in depth. CSRF is another site making the user's browser send a credentialed request to you; defend with SameSite cookies, CSRF tokens or Fetch Metadata checks, and never change state on GET. XSS defeats CSRF defences, because script in your origin can read tokens.
WHAT A STRONG ANSWER COVERS
- XSS: injected script in your origin; CSRF: forged cross-site request riding on cookies.
- Encode output by context; sanitise HTML with a vetted library; avoid dangerouslySetInnerHTML with user data.
- Strict CSP (nonces or hashes, no unsafe-inline) limits the damage of XSS; roll out with Report-Only.
- SameSite=Lax is the browser default but weaker than Strict or tokens.
- Sec-Fetch-Site headers allow server-side cross-site request rejection.
RED FLAGS
- Saying CORS prevents CSRF.
- Relying on CSP alone instead of fixing the injection.
- Thinking React makes XSS impossible.
LIKELY FOLLOW-UPS
- What are Trusted Types?
- Why can't you put a nonce-based CSP on a fully static, cached HTML page easily?
SOURCES
What is the difference between a web worker and a service worker?
Reveal model answer
A dedicated web worker is a background thread owned by one page for CPU-heavy work such as parsing, image processing or search indexing; it has no DOM access and communicates through postMessage with copied or transferred data. A service worker is an event-driven proxy registered for an origin and scope that sits between pages and the network: it intercepts fetches, manages caches, enables offline support, push and background sync, requires HTTPS, has an install and activate lifecycle, and can be stopped when idle, so it must not keep state in globals.
WHAT A STRONG ANSWER COVERS
- Web worker: compute off the main thread, lifetime tied to the page.
- Service worker: network proxy and cache controller for an origin scope.
- Neither can touch the DOM; both use message passing.
- Service worker updates wait until old clients close unless skipWaiting/clients.claim are used.
- Service workers are HTTPS-only (localhost excepted).
RED FLAGS
- Saying a service worker makes JavaScript run faster in general.
- Storing state in service worker global variables.
LIKELY FOLLOW-UPS
- How can a buggy service worker keep serving an old version of your app?
- When would you use a SharedWorker?
SOURCES
What does 'rendering' mean in React, what triggers a re-render, and how does reconciliation decide what to change in the DOM?
Reveal model answer
Rendering is React calling your component functions to compute what the UI should be; it is triggered by the initial render or a state update, and by default re-renders that component and its children. React then reconciles the new element tree against the previous one: at each position, the same component type keeps its state and is updated, a different type unmounts the old subtree and mounts a new one, and keys identify children in lists. In the commit phase React touches only the DOM nodes that actually changed.
WHAT A STRONG ANSWER COVERS
- Trigger, render (pure calculation), commit (DOM changes).
- A parent re-render re-renders children unless memoised; re-rendering is not the same as DOM updates.
- State is tied to component type and position in the tree.
- Changing the element type at a position resets the subtree's state.
- Defining components inside other components creates a new type every render and loses state.
RED FLAGS
- Saying a re-render always repaints the whole DOM.
- Wrapping everything in memo/useMemo without measuring.
LIKELY FOLLOW-UPS
- When does React.memo actually help?
- Why must rendering be pure?
SOURCES
Why does React need keys in lists? What goes wrong with `key={index}` or `key={Math.random()}`?
Reveal model answer
Keys tell React which item is which between renders so it can preserve state and DOM for the same item when the list is reordered, filtered or inserted into. With index keys, inserting or removing an item shifts identities, so state such as input text or checkbox values ends up attached to the wrong row. Random keys change every render, so every item remounts, losing state and focus and costing performance. Use stable unique IDs from the data, and remember a key can also be used deliberately to reset a component.
WHAT A STRONG ANSWER COVERS
- Keys are identity among siblings, not props.
- Index keys break when order changes; fine only for static lists.
- Random keys force remounts every render.
- Use database IDs or IDs generated once at creation time.
- Changing a key on purpose resets component state.
RED FLAGS
- Thinking keys only exist to silence a console warning.
- Generating keys during render.
LIKELY FOLLOW-UPS
- How would you reset a form when the selected user changes?
- How do keys interact with list virtualisation?
SOURCES
What is wrong with `useEffect(() => { setFullName(first + " " + last); }, [first, last]);`, and what other useEffect mistakes do you look for in review?
Reveal model answer
It stores derived data in state and syncs it in an effect, causing an extra render with a stale value in between; compute `fullName` during render instead. Effects are for synchronising with external systems, not for reacting to events or transforming data. Other common mistakes: missing or lying dependencies that create stale closures, objects or functions in dependencies that change every render and loop, fetches without cleanup that let an older response overwrite a newer one, and user actions handled in effects instead of event handlers.
WHAT A STRONG ANSWER COVERS
- If it can be computed from props or state, compute it during render.
- Event-driven logic belongs in event handlers.
- Every subscription, timer or request needs cleanup; use an ignore flag or AbortController for fetches.
- Dependencies must be honest; fix the code rather than silencing the lint rule.
- Strict Mode runs effects twice in development to expose missing cleanup.
RED FLAGS
- Disabling exhaustive-deps to stop a loop.
- Chains of effects that set state to trigger other effects.
LIKELY FOLLOW-UPS
- How would you fetch data without race conditions?
- Why do data-fetching libraries or framework loaders reduce the need for effects?
SOURCES
Compare client-side rendering, server-side rendering and static generation. What is hydration, and what causes hydration mismatches?
Reveal model answer
CSR ships a mostly empty HTML shell and builds the UI in the browser, which is simple to host but delays content and grows JavaScript cost. SSR renders HTML per request for fast first content and SEO, at the cost of server work and TTFB; SSG renders at build time for the fastest, cacheable delivery but only for content known ahead of time. Hydration attaches event handlers and state to server-rendered HTML by rendering the same tree on the client; a mismatch happens when the client render differs, for example from Date.now(), random values, locale formatting, reading window or localStorage during render, or invalid HTML nesting.
WHAT A STRONG ANSWER COVERS
- CSR: slow first content, heavy JS; SSR: fast content, server cost; SSG: fastest, build-time data.
- Hydrated pages look ready before they are interactive.
- Mismatch causes: non-deterministic values, browser-only APIs during render, invalid nesting, extensions mutating DOM.
- Fix by making render deterministic or deferring client-only values to an effect.
- Islands, streaming and server components reduce how much needs hydrating.
RED FLAGS
- Using suppressHydrationWarning as a general fix.
- Assuming SSR is always faster for users.
LIKELY FOLLOW-UPS
- How does incremental or on-demand regeneration change SSG?
- What does React 19 report on a hydration mismatch?
SOURCES
What are the Core Web Vitals today, what are the 'good' thresholds, and how would you improve each?
Reveal model answer
Largest Contentful Paint measures loading (good is 2.5 s or less), Interaction to Next Paint measures responsiveness (200 ms or less), and Cumulative Layout Shift measures visual stability (0.1 or less), each assessed at the 75th percentile of real page loads. INP replaced First Input Delay in March 2024 because it measures the latency of all interactions, including processing and the next paint, not just the first input's delay. Improve LCP by prioritising the hero resource and fast server responses, INP by breaking up long tasks and reducing render work, and CLS by reserving space for images, ads and late content.
WHAT A STRONG ANSWER COVERS
- LCP 2.5 s, INP 200 ms, CLS 0.1 at p75, measured in the field.
- INP replaced FID on 12 March 2024.
- INP needs real interactions, so rely on field data (RUM, CrUX) rather than a page-load lab score.
- Long main-thread tasks are the main INP cause.
- Set width and height or aspect-ratio on media to avoid layout shift.
RED FLAGS
- Still naming FID as a Core Web Vital.
- Treating a Lighthouse score as the user experience.
LIKELY FOLLOW-UPS
- How would you find which interaction is causing poor INP?
- Why can lazy-loading the hero image hurt LCP?
SOURCES
A teammate builds a button as `<div onClick={save}>Save</div>`. What is wrong, and what accessibility basics do you check in review?
Reveal model answer
A div has no role, is not focusable and does not respond to Enter or Space, so keyboard and screen reader users cannot use it; a native `<button>` gives all of that for free. In review I check semantic elements for their purpose, labels associated with form controls, meaningful alt text (empty for decorative images), a logical heading structure, visible focus and full keyboard operability, sufficient colour contrast, and that ARIA is only used when no native element fits. Then I test with the keyboard and a screen reader rather than only with an automated checker.
WHAT A STRONG ANSWER COVERS
- Native elements bring role, focus and keyboard behaviour.
- Every input needs a programmatic label.
- Alt text describes purpose; decorative images get alt="".
- Visible focus, logical tab order, no keyboard traps.
- ARIA supplements semantics and cannot add behaviour; wrong ARIA is worse than none.
RED FLAGS
- Fixing the div by adding role="button" only, without tabindex and key handling.
- Removing focus outlines for aesthetics.
- Relying solely on automated audits.
LIKELY FOLLOW-UPS
- How do you manage focus when opening and closing a modal?
- How do you announce async results to screen readers?
SOURCES
When would you choose web components over framework components such as React components?
Reveal model answer
Web components are built on platform standards: custom elements, shadow DOM for style and DOM encapsulation, and templates with slots. They work in any framework or none, which makes them a good fit for design systems shared across teams with different stacks, embeddable widgets and microfrontends. Framework components offer richer state management, data flow and tooling within one app. Trade-offs of web components include server rendering (declarative shadow DOM), form participation, theming through the shadow boundary, and attribute-versus-property passing, although React 19 now supports custom elements fully.
WHAT A STRONG ANSWER COVERS
- Custom elements, shadow DOM, templates and slots.
- Framework-agnostic and long-lived: good for shared design systems and embeds.
- Shadow DOM encapsulation complicates global styling and theming (use CSS custom properties, parts).
- SSR and forms need declarative shadow DOM and form-associated elements.
- React 19 passes props as properties to custom elements when they exist.
RED FLAGS
- Claiming web components replace frameworks entirely.
- Ignoring SSR, accessibility and form behaviour across the shadow boundary.
LIKELY FOLLOW-UPS
- How would you theme a web component library?
- How do events cross the shadow boundary?
SOURCES