The frontend developer interview has broadened far beyond "center this div." In 2026, frontend engineers are expected to know HTML and CSS fundamentals cold, reason deeply about JavaScript, know at least one component framework inside out, write performant and accessible UI, and increasingly handle frontend system design. This guide covers the frontend interview questions that actually come up — HTML, CSS, JavaScript, React, Angular, Vue, performance, accessibility, and system design — organized by area, with a real answer for each one and a note on what it's actually testing.
HTML fundamentals
What's the difference between an inline and a block element?
Block elements (div, p, section) start on a new line and take the full available width by default; inline elements (span, a, strong) flow within text and only take the width of their content. Block elements can contain other blocks and inlines; inline elements generally shouldn't contain block elements. The practical interview follow-up is usually "how do you change one into the other" — display: inline-block or display: flex/grid on the parent.
Why do semantic elements (header, main, nav, article, aside) matter?
<div class="header"> and <header> render identically, but semantic tags tell the browser, screen readers, and search engines what the content means. Screen readers use them to build a page outline a blind user can jump through ("skip to main", "next section"). Search engines weight content inside <article> and <h1>–<h6> differently than generic divs. The interviewer is checking whether you think about HTML as structure-with-meaning, not just a styling hook.
What is the DOCTYPE declaration and why does it matter?
<!DOCTYPE html> tells the browser to render in standards mode rather than "quirks mode," an old compatibility mode that emulates buggy 1990s browser behavior (box model, sizing). Omitting it can silently break layout in ways that are hard to diagnose — a classic "why does this only break in this one browser" bug.
Explain the difference between localStorage, sessionStorage, and cookies.
All three persist data client-side, but differently: localStorage persists until explicitly cleared and has no expiry; sessionStorage clears when the tab closes; cookies persist based on an expiry you set, are capped at ~4KB, and — critically — are sent with every HTTP request to the same domain, which is why auth tokens often live in (HttpOnly, Secure) cookies rather than localStorage. Interviewers are listening for whether you know cookies are the only one of the three the server can read, and the only one vulnerable to CSRF (vs. localStorage's XSS exposure).
What's the difference between <script>, <script async>, and <script defer>?
A plain <script> blocks HTML parsing while it downloads and executes. async downloads in parallel but executes as soon as it's ready, possibly interrupting parsing — fine for independent scripts like analytics. defer downloads in parallel and executes only after parsing completes, in document order — the right default for scripts that touch the DOM. Getting this wrong is a common cause of "works locally, breaks in prod when the network is slower" bugs.
JavaScript fundamentals
Explain closures and give a real use case.
A closure is a function that "remembers" the variables from the scope it was created in, even after that outer scope has finished executing. The classic real use case is a counter factory: function makeCounter() { let count = 0; return () => ++count; } — each call to makeCounter() creates an independent count that the returned function can still access and mutate, with no other code able to touch it. This is also how React hooks, debounce/throttle, and module-private state all work under the hood. Interviewers want to hear you connect closures to something you've actually built, not just recite the definition.
What is the difference between == and ===, and between var, let, and const?
== coerces types before comparing ("5" == 5 is true); === compares value and type with no coercion, which is why it's the default you should reach for. var is function-scoped and hoisted with an undefined value; let and const are block-scoped and live in the "temporal dead zone" until their declaration line runs. const prevents reassignment of the binding (not deep immutability — you can still mutate an object's properties).
How does the event loop work? Explain the microtask vs macrotask queue.
JavaScript is single-threaded, but the event loop lets it handle async work without blocking. After the current synchronous code finishes, the engine first drains the microtask queue (Promise callbacks, queueMicrotask) completely, then processes one task from the macrotask queue (setTimeout, setInterval, I/O), then checks microtasks again, and repeats. This is why Promise.resolve().then(...) always runs before a setTimeout(fn, 0), even though both are scheduled "after" the current code — a question interviewers love because it reveals whether you've actually debugged async ordering bugs, not just memorized "JS is single-threaded."
What is hoisting? What is the temporal dead zone?
Hoisting means variable and function declarations are processed before code executes, so you can reference a function declared later in the file. var declarations are hoisted and initialized to undefined, so reading them early gives undefined rather than an error. let and const are hoisted too, but they sit in the "temporal dead zone" — a span where the variable exists but accessing it throws a ReferenceError — until the actual declaration line runs. This was a deliberate design fix to catch the kind of bugs var hoisting used to hide silently.
Explain this binding, and the difference between arrow and regular functions.
In a regular function, this is determined by how the function is called (the call site) — as a method (this = the object), standalone (this = undefined in strict mode, global object otherwise), or via call/apply/bind. Arrow functions don't have their own this; they capture this lexically from the enclosing scope at definition time. That's exactly why arrow functions are the default for callbacks inside class methods or React components — you don't want this flipping to undefined inside a setTimeout or event handler.
What are promises, and how do async/await differ from .then()?
A promise represents a value that will exist eventually, in one of three states — pending, fulfilled, or rejected — and .then()/.catch() register callbacks for when it settles. async/await is syntactic sugar over promises: an async function always returns a promise, and await pauses execution within that function until the promise settles, letting you write asynchronous code that reads top-to-bottom instead of nesting callbacks. The practical tradeoff interviewers probe: await in a loop runs sequentially (slow), while Promise.all([...]) runs the same calls concurrently — knowing when to use each is the real signal.
Implement debounce and throttle from scratch.
Both limit how often a function runs, but for different reasons. Debounce delays execution until a quiet period has passed — useful for a search-as-you-type box, so you don't fire a request on every keystroke:
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
Throttle guarantees a function runs at most once per interval, regardless of how many times it's called — useful for scroll or resize handlers:
function throttle(fn, limit) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
Being able to write these without looking them up is a strong, fast signal for an interviewer — it shows you understand closures, timers, and why each pattern exists, not just the syntax.
React (and component frameworks)
What is the virtual DOM and how does reconciliation work?
The virtual DOM is a lightweight JavaScript representation of the actual DOM. When state changes, React builds a new virtual DOM tree, diffs it against the previous one (reconciliation), and applies only the minimal set of real DOM mutations needed — because real DOM operations are expensive, and diffing JS objects is cheap. React's diffing assumes elements of the same type in the same position are related, which is exactly why keys matter (see below) — without them, React can misattribute state across re-renders.
Explain useEffect — dependency arrays, cleanup, and common bugs.
useEffect runs a function after the DOM has been painted, and re-runs it whenever any value in its dependency array changes. An empty array ([]) means "run once on mount." Returning a function from the effect registers cleanup, which runs before the next effect run and on unmount — critical for clearing intervals, removing event listeners, or aborting fetches to avoid memory leaks and "setState on an unmounted component" warnings. The single most common bug: omitting a dependency that the effect actually uses, causing it to run with stale values — eslint-plugin-react-hooks catches most of these, and interviewers will ask you to spot one in a code snippet.
What causes unnecessary re-renders, and how do you prevent them?
A component re-renders when its state changes, its parent re-renders, or its context value changes — even if the props it receives are unchanged. Common fixes: React.memo to skip re-rendering a component when its props are referentially equal; useMemo to cache an expensive computed value; useCallback to keep a function reference stable across renders so it doesn't break a child's memo. The nuance interviewers want: these are not free — they add comparison overhead, so reaching for them everywhere is itself an anti-pattern. Profile first, memoize second.
Controlled vs uncontrolled components — when to use each.
A controlled component's value lives in React state, updated via onChange — React is the single source of truth, which makes validation and conditional logic straightforward. An uncontrolled component reads its value via a ref only when needed (e.g., on submit), letting the DOM hold the state. Controlled is the default for forms with live validation or dependent fields; uncontrolled is lighter-weight and sometimes preferred for large forms or when integrating non-React widgets.
How does state management scale — Context vs Redux vs signals?
useState/useReducer handle local component state. Context avoids prop drilling for state a subtree shares, but every consumer re-renders on any context value change — fine for low-frequency data (theme, auth user), risky for high-frequency data. Redux (or Zustand/Jotai) centralizes state outside the component tree with explicit, traceable updates — valuable once state is shared across distant parts of a large app and you need devtools/time-travel debugging (dedicated Redux interview questions guide here). Newer signal-based approaches (as in Solid, or Preact signals) skip the virtual-DOM diff entirely by tracking fine-grained dependencies. The answer interviewers actually want is a decision framework, not a single "best" tool: start local, lift to Context for low-frequency shared state, reach for a store when updates are frequent and span the app.
What are keys in lists and why do they matter?
Keys give React a stable identity for each item across re-renders, so it can match old elements to new ones instead of re-creating everything from scratch. Using array index as a key works until the list is reordered, filtered, or items are inserted/removed — at which point React misattributes state (a classic bug: text typed into the wrong input after deleting a list item above it). The fix is a stable, unique ID from your data, never the index, unless the list is static and never reorders.
Angular and Vue — do you need to know them too?
Most product companies hiring "frontend developer" mean React in 2026, but Angular still dominates in large enterprises, banking, and many Indian service companies (TCS, Infosys, Capgemini, consulting shops building internal tools), and Vue shows up at startups that value its gentler learning curve. If a JD mentions Angular, expect questions on dependency injection (Angular's services are injected, not imported — a deliberate testability choice), RxJS observables vs promises (observables are cancellable and can emit multiple values over time), and change detection (Angular's zone-based dirty-checking vs React's explicit re-render model). For Vue, expect the Composition API vs Options API (Composition lets you organize code by feature rather than by option type, similar in spirit to React hooks), reactivity via Proxies (Vue 3 tracks property access/mutation automatically — no useState calls needed), and single-file components (.vue files combining template, script, and style). You don't need framework-hopping fluency for a React role, but knowing the one-sentence mental model for each shows breadth without faking expertise.
CSS and layout
Explain the box model and box-sizing.
Every element is a box made of content, padding, border, and margin, from inside out. By default (box-sizing: content-box), width/height apply only to the content area, so padding and border add on top of your declared size — a classic source of "why is this 20px wider than I set it" bugs. box-sizing: border-box makes width/height include padding and border, so the box stays the size you actually declared; most teams set * { box-sizing: border-box; } globally for exactly this reason.
Flexbox vs Grid — when do you reach for each?
Flexbox is one-dimensional — it excels at distributing space along a single row or column (navbars, button groups, centering one thing). Grid is two-dimensional — it excels when you need to control both rows and columns together (page layouts, card galleries, dashboards). The honest answer interviewers want: most real UIs use both — Grid for the overall page skeleton, Flexbox for the components living inside each grid cell.
How does specificity work, and how do you debug a stubborn CSS override?
Specificity is calculated by counting selector types: inline styles beat IDs, IDs beat classes/attributes/pseudo-classes, which beat element selectors, with !important overriding the whole hierarchy (and creating its own debugging headaches if overused). When a style "won't apply," open devtools, inspect the element, and look at which rule is shown crossed out — that tells you exactly what's winning and why, rather than guessing and adding more !important.
What is the stacking context, and why does z-index sometimes "not work"?
z-index only has meaning within a stacking context, and several CSS properties create a new one implicitly — position other than static combined with a z-index, opacity less than 1, transform, filter, and will-change, among others. A common bug: a child has z-index: 9999 but still renders behind another element, because its parent is trapped in a lower stacking context than the sibling it's competing with — no child z-index value can escape its parent's context. Interviewers ask this because it separates people who've memorized "z-index controls layering" from people who've actually debugged a stacking bug.
How do you build a responsive layout without a framework?
Mobile-first media queries (min-width breakpoints, building up from the smallest screen rather than down from the largest), relative units (rem for type so it respects user font-size preferences, %/vw/vh/fr for layout), clamp() for fluid typography and spacing that scales smoothly between a min and max instead of jumping at breakpoints, and Flexbox/Grid's intrinsic wrapping (flex-wrap, grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))) so the layout adapts without a media query at all. The signal interviewers want: framework-free doesn't mean hardcoding pixel breakpoints everywhere — it means reaching for CSS's own responsive primitives first.
Performance and accessibility
What are the Core Web Vitals (LCP, INP, CLS) and how do you improve them?
LCP (Largest Contentful Paint) measures how long the biggest visible element takes to render — improve it by optimizing/preloading the hero image, removing render-blocking resources, and using a CDN. INP (Interaction to Next Paint, which replaced FID in 2024) measures responsiveness to clicks/taps/keypresses across the page's whole lifecycle — improve it by breaking up long JavaScript tasks and avoiding heavy work on the main thread during interactions. CLS (Cumulative Layout Shift) measures unexpected layout movement — improve it by always reserving space for images/ads/embeds (explicit width/height or aspect-ratio) and avoiding inserting content above existing content after load.
Explain code splitting, lazy loading, and tree shaking.
Code splitting breaks one large JS bundle into smaller chunks loaded on demand (e.g., per route), so users download only what the current page needs. Lazy loading defers loading a resource — a route's code via React.lazy(), or an image via loading="lazy" — until it's actually needed, often "just before" the user scrolls to it. Tree shaking is a build-time step where the bundler statically analyzes imports and strips out exported code that's never used, shrinking the final bundle — it only works reliably with ES module syntax (import/export), not CommonJS (require), since static analysis needs imports to be determinable without running the code.
How do you reduce bundle size and time-to-interactive?
Audit first with a bundle analyzer to find what's actually heavy — it's often one or two oversized dependencies, not "everything." Then: code-split by route, lazy-load below-the-fold and rarely-used features, replace heavy libraries with lighter alternatives (date-fns over moment, native fetch over axios for simple cases), defer non-critical third-party scripts, and ship modern JS without unnecessary polyfills if your audience supports it. Time-to-interactive specifically suffers from large hydration costs in SSR apps — streaming SSR and islands/partial hydration architectures exist specifically to address this.
What makes a component accessible?
Semantic HTML first — a real <button> gets keyboard focus, Enter/Space activation, and screen-reader announcement for free; a <div onClick> gets none of that and requires you to manually reimplement all of it with ARIA and tabindex. Beyond semantics: ARIA attributes (aria-label, aria-expanded, aria-live) for cases HTML alone can't express, visible focus indicators (never outline: none without a replacement), logical tab order, sufficient color contrast, and focus management on dynamic UI — when a modal opens, focus should move into it, and return to the trigger element when it closes. The interview signal: can you name a specific bug you've caught or fixed, not just list WCAG categories.
Frontend system design
Senior frontend interviews now include a design round: "design a typeahead search," "design an infinite-scroll feed," "design a component library," "design a real-time collaborative text editor." Drive it like any design question — clarify requirements first, then work through data fetching/caching, state management, rendering strategy, error/loading states, and accessibility, talking trade-offs out loud rather than jumping straight to an answer.
Worked example — "design a typeahead search":
- Clarify scope. Search-as-you-type against what — a local list, or a remote API? What's the expected dataset size and latency budget?
- Network behavior. Debounce keystrokes (150–300ms) so you're not firing a request per character. Cancel in-flight requests when a newer one starts (
AbortController), so a slow earlier response can't overwrite a faster, more recent one — a real bug class interviewers specifically watch for. - Caching. Cache recent query results client-side so re-typing a previous query (common when users backspace and retype) is instant and doesn't re-hit the network.
- Rendering. Virtualize the results list if it can be long, so you're not mounting hundreds of DOM nodes per keystroke.
- States. Explicit loading, empty, and error states — and what happens if the request that started 400ms ago resolves after the user has already cleared the input.
- Accessibility. Results list needs
role="listbox"/role="option"and arrow-key navigation, with the currently highlighted option announced to screen readers viaaria-activedescendant.
The same skeleton — requirements, data flow, state, rendering, edge cases, accessibility — applies to infinite scroll (windowing + intersection observer + scroll position restoration), a component library (API design, theming, composition vs configuration), or a collaborative editor (operational transform/CRDT, conflict resolution, presence).
Practise explaining, not just memorizing
You can recognize every answer above on a flashcard and still stumble when asked to explain closures out loud to an interviewer, live, with someone watching you think. The interview is verbal, so practice has to be verbal too — reading an answer is a different skill from producing one under mild pressure with follow-up questions. Greenroom runs spoken frontend mock interviews, asks follow-ups on your real projects and GitHub repos, and gives feedback on how clearly you explain technical concepts, not just whether the final answer was "correct." Pair it with talking about your GitHub projects and coding-interview communication tips.
Company-specific loops differ a lot: see our Meta frontend interview questions and Google frontend interview questions guides for what each one actually grades.
For the design round specifically, see our frontend system design interview questions guide, with worked answers for a feed, an autocomplete and a live dashboard.
Frequently asked questions
What should I study for a frontend developer interview?
Start with HTML semantics and the box model, then JavaScript fundamentals (closures, the event loop, promises, this binding), your primary framework (React rendering, hooks, re-render performance — or Angular/Vue if that's what the role uses), CSS layout (flexbox, grid, specificity, stacking context), performance (Core Web Vitals, code splitting), accessibility, and increasingly frontend system design for senior roles.
What JavaScript questions come up most in frontend interviews?
The most common are explaining closures with a real use case, how the event loop and microtask/macrotask queues work, the differences between var, let, and const, how this binding works for regular vs arrow functions, promises versus async/await, and implementing debounce and throttle from scratch.
Do I need to know Angular or Vue if I'm interviewing for React roles?
No — most React-role interviewers won't ask Angular or Vue specifics. But if a job description lists Angular or Vue, or you're interviewing at a large enterprise/consulting company where Angular is common, know the one key differentiator of each: Angular's dependency injection and RxJS-based reactivity, and Vue's Composition API and Proxy-based reactivity. A one-sentence accurate mental model of "the other" frameworks shows breadth without you having to fake deep expertise you don't have.
Do frontend interviews include system design?
Yes, increasingly — especially for mid and senior roles. Frontend system design questions like "design a typeahead search" or "design an infinite-scroll feed" test how you handle data fetching, caching, state management, rendering strategy, loading/error states, and accessibility. Drive them with a clear framework (clarify requirements → data flow → state → rendering → edge cases → accessibility) and talk trade-offs out loud rather than jumping to a single answer.
What's the most common mistake candidates make in frontend interviews?
Reciting definitions instead of demonstrating understanding — saying "useEffect runs after render" without being able to describe a real bug it caused you, or naming Core Web Vitals without explaining how you'd actually improve one on a real page. The second most common mistake is over-memoizing React components (memo/useMemo/useCallback everywhere) without being able to explain why — interviewers read that as cargo-culting, not understanding.
How do I prepare for the verbal part of a frontend interview?
Practise explaining concepts out loud, not just recognizing them on flashcards. Interviewers care whether you understand why something works, so rehearse explaining things like closures, a re-render bug, or a project decision verbally — ideally with a mock interview that asks realistic follow-up questions, since the real interview rarely stops at your first answer.