Frontend Development Interview Questions
35+ frontend interview questions organized by topic — HTML & semantics, CSS & layout, JavaScript fundamentals, the DOM, async JS, accessibility, and performance. Click “Show Answer” to reveal detailed answers.
HTML & Semantics
Q: What is semantic HTML and why does it matter?
Semantic HTML uses elements that describe their meaning rather than just appearance. <header>, <nav>, <main>, <article>, <section>, <aside>, <footer> all communicate the role of content.
It matters for: (1) Accessibility — screen readers use semantics to navigate. (2) SEO — search engines understand page structure. (3) Maintainability — developers understand the code faster.
Q: What is the difference between <section> and <div>?
<section> represents a thematic grouping of content, typically with a heading. Screen readers announce it as a region. <div> is a generic container with no semantic meaning — use it purely for styling.
Rule of thumb: if the group would make sense as an entry in a table of contents, use <section>. Otherwise, use <div>.
Q: What is the purpose of the <meta viewport> tag?
<meta name="viewport" content="width=device-width, initial-scale=1.0"> tells mobile browsers to set the viewport width to the device width and not zoom out. Without it, mobile browsers render the page at ~980px wide and scale it down, making text tiny and unreadable.
Q: What are data-* attributes used for?
data-* attributes store custom data on HTML elements that JavaScript can read via element.dataset. Example: <div data-user-id="42"> is accessible as element.dataset.userId. They’re useful for passing data from HTML to JS without polluting standard attributes.
Q: What is the difference between <script>, <script defer>, and <script async>?
Default: blocks HTML parsing — browser stops parsing, downloads and executes JS, then continues. defer: downloads in parallel with HTML parsing, executes after parsing completes (in order). async: downloads in parallel, executes as soon as downloaded (out of order). Use defer for most scripts (preserves order). Use async for independent scripts like analytics.
CSS & Layout
Q: Explain the CSS box model.
Every element is a rectangular box with four layers (inside out): content (text/image), padding (space inside the border), border, and margin (space outside the border). By default (content-box), width only sets content width. With border-box, width includes padding and border. Most projects use *, *::before, *::after { box-sizing: border-box; }.
Q: What is CSS specificity and how is it calculated?
Specificity determines which CSS rule wins. It’s a 3-part score: (IDs, Classes, Elements). ID selectors (#id) = 1-0-0. Class/attribute/pseudo-class (.class, :hover) = 0-1-0. Element/pseudo-element (div, ::before) = 0-0-1. Scores are compared left to right. #nav .link a = 1-1-1. When equal, the last rule wins.
Q: When would you use Flexbox vs CSS Grid?
Flexbox: one-dimensional (row OR column). Best for navigation bars, centering content, aligning items in a row. Grid: two-dimensional (rows AND columns). Best for page layouts, dashboards, card grids. They’re complementary — use Grid for the page layout, Flexbox for components within grid cells.
Q: What is the difference between position: relative, absolute, fixed, and sticky?
relative: positioned relative to its normal position (still in document flow). absolute: positioned relative to nearest positioned ancestor (removed from flow). fixed: positioned relative to the viewport (stays on screen during scroll). sticky: acts like relative until a scroll threshold, then acts like fixed. Use sticky for headers that stick on scroll.
Q: What is margin collapse and when does it NOT happen?
When vertical margins of adjacent block elements meet, they combine into one margin (the larger wins). It does NOT happen: (1) in Flexbox/Grid containers, (2) with horizontal margins, (3) when there’s a border or padding between parent/child, (4) with inline-block or floated elements.
Q: How do you center a div vertically and horizontally?
Modern Flexbox: display: flex; justify-content: center; align-items: center; on the parent. Grid shorthand: display: grid; place-items: center;. The parent needs a defined height (e.g., min-height: 100vh).
JavaScript Fundamentals
Q: What is a closure?
A closure is a function that retains access to variables from its outer (enclosing) scope, even after the outer function has returned. It’s JavaScript’s way of creating private state.
function createCounter() {
let count = 0; // private — only accessible via the returned function
return function() { return ++count; };
}
const counter = createCounter();
counter(); // 1
counter(); // 2
Q: Explain the event loop.
JavaScript is single-threaded. The event loop enables async behavior: (1) Synchronous code runs on the call stack. (2) Async callbacks go to a task queue (setTimeout, events). (3) Promise callbacks go to the microtask queue (higher priority). (4) When the call stack is empty, the event loop processes all microtasks, then one task from the task queue.
This is why Promise.resolve().then(fn) runs before setTimeout(fn, 0).
Q: What is the difference between == and ===?
== performs type coercion before comparing: '5' == 5 is true. === compares both value and type: '5' === 5 is false. Always use === to avoid unexpected coercion bugs.
Q: What are let, const, and var?
var: function-scoped, hoisted (initialized as undefined). let: block-scoped, can be reassigned. const: block-scoped, cannot be reassigned (but objects/arrays are still mutable). Best practice: const by default, let when reassignment needed, never var.
Q: What is hoisting?
Hoisting moves declarations to the top of their scope during compilation. var declarations are hoisted and initialized to undefined. let/const are hoisted but NOT initialized (accessing before declaration throws ReferenceError — the “temporal dead zone”). Function declarations are fully hoisted (can be called before the line they’re written on). Arrow functions/function expressions are NOT hoisted.
Q: What is this in JavaScript?
this depends on how a function is called: (1) Global: window (or undefined in strict mode). (2) Method: the object before the dot (obj.method() → this === obj). (3) Constructor: the new instance. (4) call/apply/bind: whatever you pass. (5) Arrow function: inherits this from the enclosing scope (lexical this).
Q: What is the difference between null and undefined?
undefined means a variable was declared but not assigned a value. It’s the default for uninitialized variables, missing function arguments, and non-existent properties. null is an intentional absence of value — explicitly set by a developer to mean “nothing here.” typeof undefined === 'undefined'. typeof null === 'object' (a historical bug).
DOM & Events
Q: What is event delegation?
Instead of attaching event listeners to individual child elements, attach one listener to a parent element and use event.target to identify which child was interacted with. Benefits: (1) fewer listeners = better memory, (2) automatically works for dynamically added elements, (3) cleaner code.
// One listener for all cards
document.querySelector('.grid').addEventListener('click', function(e) {
const card = e.target.closest('.card');
if (card) handleClick(card.dataset.id);
});
Q: What is event bubbling vs capturing?
Events have three phases: (1) Capturing — event travels from document down to the target. (2) Target — event reaches the clicked element. (3) Bubbling — event travels back up to document. By default, addEventListener listens during the bubbling phase. Pass true as the third argument to listen during capture. Most of the time you want bubbling.
Q: What is the difference between event.preventDefault() and event.stopPropagation()?
preventDefault() stops the browser’s default action (form submission, link navigation, checkbox toggle). stopPropagation() stops the event from bubbling to parent elements. They serve different purposes. Use preventDefault() for forms; avoid stopPropagation() unless you have a good reason (it breaks event delegation).
Q: What is the difference between textContent and innerHTML?
textContent gets/sets plain text, escaping all HTML. Safe from XSS. innerHTML gets/sets HTML markup, parsing tags. Vulnerable to XSS if used with user input. Use textContent for text, innerHTML only with trusted content, and createElement for dynamic HTML from untrusted sources.
Async JavaScript
Q: What is a Promise?
A Promise represents a future value — the result of an async operation that hasn’t completed yet. It has three states: pending (in progress), fulfilled (succeeded with a value), or rejected (failed with an error). You handle results with .then()/.catch() or async/await.
Q: What is the difference between Promise.all(), Promise.allSettled(), and Promise.race()?
Promise.all(): resolves when ALL promises resolve; rejects immediately if ANY rejects. Promise.allSettled(): waits for ALL to complete (resolve or reject) and returns status of each. Promise.race(): resolves/rejects as soon as the FIRST promise settles. Use all for parallel tasks that all need to succeed, allSettled when you want results regardless of failure, race for timeouts.
Q: Why does fetch() not reject on 404 or 500 errors?
fetch() considers any completed HTTP request a “success” from a network perspective. It only rejects on network failures (server unreachable, DNS error, CORS block). For HTTP errors, check response.ok (which is false for 4xx/5xx) and throw manually.
Q: What is the difference between async/await and .then()/.catch()?
They both handle Promises. async/await is syntactic sugar that makes async code look synchronous, improving readability. .then()/.catch() is the original chaining syntax. Under the hood, await is equivalent to .then(). Use async/await for sequential async logic, .then() when chaining or with Promise.all().
Accessibility
Q: What is ARIA and when should you use it?
ARIA (Accessible Rich Internet Applications) is a set of attributes that add accessibility information to HTML. The first rule of ARIA is don’t use it if a native HTML element does the job. Use ARIA for: (1) custom widgets (modals, tabs, sliders), (2) live regions (aria-live), (3) state information (aria-expanded, aria-selected), (4) labeling (aria-label, aria-describedby).
Q: How do you make a custom dropdown accessible?
Key requirements: (1) Use role="listbox" on the dropdown and role="option" on items. (2) Add aria-expanded on the trigger button. (3) Support keyboard: Enter/Space to toggle, Arrow keys to navigate options, Escape to close. (4) Manage focus: move focus into the dropdown when opened, return to trigger when closed. (5) Use aria-activedescendant to indicate the current option. Or better yet, use a native <select> element.
Q: What is a skip link and why is it important?
A skip link is a hidden link (usually the first focusable element on the page) that lets keyboard and screen reader users skip past repeated navigation and jump directly to the main content. Without it, users must Tab through every nav link on every page. It’s visually hidden until focused: <a href="#main" class="skip-link">Skip to content</a>.
Q: What WCAG contrast requirements should you follow?
WCAG 2.1 Level AA requires: 4.5:1 contrast ratio for normal text, 3:1 for large text (18px bold or 24px regular). Level AAA requires 7:1 and 4.5:1 respectively. Additionally, don’t rely on color alone to convey information — always provide text labels or icons alongside color indicators.
Performance
Q: What are Core Web Vitals?
Three metrics Google uses for page experience ranking: LCP (Largest Contentful Paint, < 2.5s) — how fast main content loads. INP (Interaction to Next Paint, < 200ms) — how fast the page responds to input. CLS (Cumulative Layout Shift, < 0.1) — how stable the layout is during load. Measure with Lighthouse, PageSpeed Insights, or Chrome DevTools.
Q: What is the critical rendering path?
The sequence of steps the browser takes to convert HTML, CSS, and JS into pixels: (1) Parse HTML → DOM tree. (2) Parse CSS → CSSOM tree. (3) Combine into render tree. (4) Layout (calculate positions/sizes). (5) Paint (draw pixels). (6) Composite (layer them). CSS blocks rendering. JS blocks HTML parsing (unless defer/async). Optimizing this path = faster first paint.
Q: What is the difference between defer and async on scripts?
Both download scripts in parallel with HTML parsing. defer: executes after HTML parsing completes, in order. async: executes as soon as downloaded, out of order. Use defer for scripts that depend on DOM or each other. Use async for independent scripts (analytics, ads).
Q: How do you optimize images for the web?
Key strategies: (1) Use modern formats (WebP or AVIF, 30-50% smaller than JPEG). (2) Lazy load below-fold images (loading="lazy"). (3) Serve responsive sizes with srcset. (4) Set width/height to prevent CLS. (5) Compress images (tools: Squoosh, Sharp). (6) Use CDN for delivery. (7) Consider CSS for simple graphics instead of images.
Q: What is debouncing vs throttling?
Debounce: delays execution until the user stops triggering the event for a set time. Use for: search input (wait until user stops typing). Throttle: limits execution to at most once per time interval. Use for: scroll, resize events (fire continuously but process at most every 100ms). Both prevent performance issues from rapid-fire events.
Scenario-Based Questions
Q: A user reports that a button on your site doesn’t work on mobile. How do you debug it?
Debugging steps: (1) Open Chrome DevTools device mode to reproduce. (2) Check if the button is covered by another element (z-index, overflow). (3) Check if the click target is too small (< 44x44px). (4) Check for hover-only interactions that don’t work on touch. (5) Check the console for errors. (6) Test on an actual device — emulators miss touch event nuances. (7) Check if a touchstart handler has a 300ms delay (use touch-action: manipulation).
Q: Your site’s Lighthouse performance score dropped from 95 to 60. How do you diagnose and fix it?
(1) Run Lighthouse and read the specific diagnostics. (2) Check LCP: is a large image/font blocking render? Preload critical resources. (3) Check CLS: new content without reserved space? Add dimensions. (4) Check INP/TBT: heavy JS blocking the main thread? Code-split, defer non-critical JS. (5) Check the Network tab waterfall for blocking resources. (6) Compare to a previous commit with git bisect to find what changed.
Q: How would you build an infinite scroll feed?
Use the Intersection Observer API: (1) Place a sentinel element at the bottom of the list. (2) Observe it with IntersectionObserver. (3) When it enters the viewport, fetch the next page of data. (4) Render new items and move the sentinel below them. Key considerations: debounce or add a loading state to prevent duplicate fetches, add a “Load More” button as fallback, handle end-of-data state, and consider virtualization for very long lists (only render visible items).