Frontend engineer interviews test a different skill set from backend or full-stack interviews. The core of what gets evaluated is JavaScript internals, React concepts and performance, browser behaviour, and the ability to implement UI components and interactions cleanly under time pressure.
Most candidates preparing for frontend interviews over-prepare on React component APIs and under-prepare on JavaScript fundamentals and browser internals. These are the areas where experienced interviewers probe most deeply because they reveal whether a candidate understands how the tools they use actually work, not just how to use them.
If you want to practice these questions with a real engineer before your actual interview, book a mock interview on Intervue.io. The rest of this guide gives you the questions, the answers, and what interviewers are actually scoring.
What a Frontend Mock Interview Covers
Frontend interviews at FAANG and product companies test across four areas.
JavaScript fundamentals and internals cover how JavaScript works under the hood: the event loop, call stack, execution context, closures, prototype chain, and asynchronous patterns. These questions appear at every level and go deeper at senior level where interviewers probe the exact mechanics of event propagation, memory leaks, and the microtask queue.
React concepts and performance cover the React component model, hooks, the virtual DOM and reconciliation process, state management, and performance optimisation patterns. This is the most commonly tested area in product company and FAANG frontend interviews.
Browser and web platform knowledge covers how browsers parse and render pages, the DOM API, CSS specificity and the cascade, HTTP basics, and web security concepts like CORS and XSS. Senior frontend interviews go deep here.
Practical UI implementation asks you to build a component, implement a specific interaction, or solve a UI-related problem in real time. This is the coding equivalent of the algorithmic problem in backend interviews, but the problem involves DOM manipulation, event handling, or React component design.
JavaScript Fundamentals Questions
What is the event loop and how does it work?
JavaScript is single-threaded: it executes one piece of code at a time. The event loop is the mechanism that allows JavaScript to handle asynchronous operations without blocking execution.
The JavaScript runtime has three components that work together. The call stack executes synchronous code one function at a time. The Web APIs layer (in browsers) handles async operations like setTimeout, fetch, and event listeners. The task queue (also called the callback queue) holds callbacks from completed async operations waiting to be executed.
The event loop continuously checks: is the call stack empty? If yes, it takes the first item from the task queue and pushes it onto the call stack for execution.
There are actually two queues with different priorities. The microtask queue holds Promise callbacks and queueMicrotask callbacks. The task queue (macrotask queue) holds setTimeout callbacks, setInterval callbacks, and I/O callbacks. After each macrotask completes, the event loop drains the entire microtask queue before processing the next macrotask.
javascript
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output: 1, 4, 3, 2
// '1' and '4': synchronous, run immediately
// '3': microtask (Promise), runs before setTimeout
// '2': macrotask (setTimeout), runs last even with 0ms delay
This is one of the most frequently asked JavaScript interview questions because it reveals whether the candidate understands async execution or just uses it.
What is a closure and when would you use one?
A closure is a function that retains access to its outer scope even after that outer function has returned. The inner function closes over the variables of its enclosing scope.
javascript
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
The returned function has access to count even though createCounter has already returned. count lives in the closure.
Practical uses of closures: creating private state (as in the counter example above), function factories that generate specialised functions, memoization, and maintaining state in event handlers without global variables.
A common interview follow-up: "What is the classic closure bug in a loop?"
javascript
// Bug: all buttons log 3
for (var i = 0; i < 3; i++) {
document.querySelector(`#btn${i}`).addEventListener('click', function() {
console.log(i); // always 3, not 0, 1, 2
});
}
// Fix 1: use let instead of var (block scoping)
for (let i = 0; i < 3; i++) {
document.querySelector(`#btn${i}`).addEventListener('click', function() {
console.log(i); // correctly logs 0, 1, 2
});
}
// Fix 2: use an IIFE to capture i by value
for (var i = 0; i < 3; i++) {
(function(j) {
document.querySelector(`#btn${j}`).addEventListener('click', function() {
console.log(j);
});
})(i);
}
What is the difference between == and === in JavaScript?
== performs loose equality comparison with type coercion. Before comparing, JavaScript converts one or both operands to a common type.
=== performs strict equality comparison with no type coercion. Both value and type must match.
javascript
console.log(1 == '1'); // true: '1' is coerced to number 1
console.log(1 === '1'); // false: different types
console.log(null == undefined); // true: special case
console.log(null === undefined); // false: different types
console.log(0 == false); // true: false coerced to 0
console.log(0 === false); // false: different types
Always use === in production code. The type coercion rules for == are complex enough that even experienced developers get them wrong. The only legitimate use case for == is checking for both null and undefined simultaneously with value == null.
What is the difference between var, let, and const?
var is function-scoped and hoisted to the top of its enclosing function. It can be redeclared and reassigned. Declarations are hoisted but not initialisations, which leads to a common source of bugs.
let is block-scoped (limited to the nearest set of curly braces). It cannot be redeclared in the same scope but can be reassigned. It is hoisted but not initialised, creating a temporal dead zone between the start of the block and the declaration.
const is block-scoped like let. It cannot be redeclared or reassigned. The binding is constant, not the value: a const object can have its properties mutated.
javascript
// var hoisting
console.log(x); // undefined, not an error
var x = 5;
// let temporal dead zone
console.log(y); // ReferenceError
let y = 5;
// const object mutation
const obj = { name: 'Alice' };
obj.name = 'Bob'; // allowed: mutating the object
obj = {}; // TypeError: reassigning the binding
Use const by default. Use let when you need to reassign. Never use var in modern JavaScript.
What is prototypal inheritance in JavaScript?
Every JavaScript object has an internal link to another object called its prototype. When you access a property on an object, JavaScript first checks the object itself. If not found, it looks at the prototype. If still not found, it looks at the prototype's prototype, and so on up the chain until it reaches Object.prototype.
javascript
const animal = {
breathe() {
return 'breathing';
}
};
const dog = Object.create(animal); // dog's prototype is animal
dog.bark = function() {
return 'woof';
};
console.log(dog.bark()); // 'woof': own property
console.log(dog.breathe()); // 'breathing': inherited from prototype
console.log(dog.hasOwnProperty('bark')); // true
console.log(dog.hasOwnProperty('breathe')); // false
ES6 classes are syntactic sugar over prototypal inheritance. Under the hood, class syntax uses the same prototype chain mechanism.
React Questions
What is the virtual DOM and how does React reconciliation work?
The virtual DOM is a lightweight JavaScript representation of the actual DOM tree. React maintains this in memory and uses it to compute the minimum set of DOM updates needed when state changes.
When state or props change, React creates a new virtual DOM tree. It then compares the new tree against the previous one in a process called reconciliation (or diffing). The diff algorithm identifies which nodes changed and updates only those parts of the real DOM, rather than re-rendering the entire page.
The diffing algorithm uses two heuristics to run in O(n) rather than O(n^3). First, elements of different types produce completely different trees: if the root element changes type, React tears down the old tree and builds a new one. Second, when rendering lists, React uses the key prop to identify which items changed, were added, or were removed.
What this means in practice: always provide stable, unique keys when rendering lists. Using array index as a key causes problems when items are reordered because React cannot tell which items changed. Using a unique identifier from your data (like an id field) gives React the information it needs to reconcile correctly.
What is the difference between useMemo and useCallback?
Both are hooks for memoisation, but they memoize different things.
useMemo memoizes the result of a computation. It recalculates only when its dependencies change.
javascript
const expensiveResult = useMemo(() => {
return items.filter(item => item.active).length;
}, [items]); // only recomputes when items changes
useCallback memoizes a function reference. It returns the same function instance between renders as long as dependencies have not changed.
javascript
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]); // returns same function reference when count has not changed
When to use each: useMemo is for expensive computations whose results you want to cache. useCallback is for function references that you pass as props to child components wrapped in React.memo, preventing unnecessary re-renders of those children.
A common interview follow-up: "Does useMemo always improve performance?" No. Memoization has overhead: storing the cached value and comparing dependencies on every render. For inexpensive computations, the overhead of useMemo can exceed the cost of recomputing. Use it when profiling shows a specific performance problem, not as a default.
What are the rules of React Hooks and why do they exist?
Two rules: only call hooks at the top level (not inside loops, conditions, or nested functions), and only call hooks from React function components or custom hooks.
These rules exist because React tracks hook calls by order. Each render, React walks through the hook calls in the same order. If the order changes between renders (because a hook is inside a conditional that sometimes runs and sometimes does not), React cannot correctly match hook state from one render to the next.
javascript
// Wrong: conditional hook call
function Counter({ show }) {
if (show) {
const [count, setCount] = useState(0); // breaks hook order
}
}
// Correct: condition inside the hook logic
function Counter({ show }) {
const [count, setCount] = useState(0);
if (!show) return null;
return <div>{count}</div>;
}
What is the difference between useEffect and useLayoutEffect?
Both accept a callback and a dependency array. The difference is when they run.
useEffect runs asynchronously after the browser has painted the screen. This is the right choice for most side effects: data fetching, subscriptions, timers, and anything that does not need to block the visual update.
useLayoutEffect runs synchronously after React has computed the DOM changes but before the browser paints. This is the right choice when your effect reads or modifies DOM measurements and you need to prevent a visual flash. Reading element dimensions and positioning a tooltip based on them is a classic use case.
Using useLayoutEffect when useEffect would work is a performance mistake because it blocks painting. Only use useLayoutEffect when you have a specific reason to prevent a visual flash between the DOM update and the measurement.
What is React Fiber?
React Fiber is the reconciliation engine introduced in React 16. It replaced the original recursive, synchronous reconciliation algorithm with an architecture that can pause, resume, and prioritise work.
The key capability Fiber enables is concurrent rendering: React can interrupt rendering to handle higher-priority updates (like user input) and then resume the lower-priority work. This prevents long rendering tasks from blocking the main thread and making the UI feel unresponsive.
Fiber represents each node in the component tree as a unit of work. The reconciler can work through these units incrementally, yielding control to the browser between units to process events or paint frames.
What this enables in practice: features like Suspense (showing fallback UI while waiting for async data), transitions (marking updates as non-urgent so they do not block user interaction), and concurrent mode rendering.
Browser and Web Platform Questions
What is the difference between event bubbling and event capturing?
When an event occurs on a DOM element, it travels in two phases.
Capturing phase (top down): the event travels from the document root down through the DOM tree to the target element.
Bubbling phase (bottom up): after reaching the target, the event bubbles back up through the ancestor elements to the document root.
By default, event listeners are attached in the bubbling phase. To attach in the capturing phase, pass true as the third argument to addEventListener.
javascript
// This fires during bubbling (default)
parent.addEventListener('click', handler);
// This fires during capturing
parent.addEventListener('click', handler, true);
Event delegation uses bubbling: attach one listener on a parent element instead of many listeners on individual children. When a child is clicked, the event bubbles to the parent.
javascript
// Instead of attaching click handlers to every li
document.querySelector('ul').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
console.log('Clicked:', e.target.textContent);
}
});
What is CORS and how does it work?
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls which origins can make requests to a server from JavaScript code.
By default, browsers block cross-origin requests made by JavaScript. A request is cross-origin if the scheme, domain, or port differs from the page's origin. If your frontend at https://app.example.com tries to fetch from https://api.other.com, the browser blocks the request unless the server explicitly allows it.
Servers allow cross-origin requests by including specific response headers. The most important is Access-Control-Allow-Origin, which specifies which origins are permitted. A value of * allows any origin. A specific origin allows only that origin.
For requests with credentials (cookies, Authorization headers), the server must respond with Access-Control-Allow-Credentials: true and cannot use * for the origin header.
CORS is enforced by the browser, not the server. If you make the same request from a server-side script, there is no CORS check. CORS only applies to browser-initiated cross-origin requests.
What Interviewers Score in Frontend Interviews
In JavaScript rounds, they score depth of understanding: can you explain not just what a closure is but what the closure bug in a for loop looks like and why it happens? Can you trace through the event loop example with Promises and setTimeout and give the exact output in order?
In React rounds, they score practical judgment: do you know when to use useMemo versus when it adds overhead? Can you explain why the key prop matters for list reconciliation rather than just saying you should use it?
In UI implementation rounds, they score code quality and problem decomposition: is your component clean and readable? Do you handle edge cases (empty lists, loading states, error states) as part of your implementation rather than as an afterthought?
At senior level, they score architectural thinking: given a complex UI requirement, how do you structure state, where do you put side effects, and how do you make the component testable?
FAQs
How different is a frontend interview at a FAANG company versus a product company? At FAANG companies, the JavaScript fundamentals and browser internals depth is higher. At product companies, the practical React implementation and system-level thinking about component architecture are more emphasised. Both require strong JavaScript fundamentals. FAANG companies are more likely to ask about event loop internals, prototype chain, and memory management.
Is TypeScript required for frontend interviews? TypeScript knowledge is increasingly expected at senior level across FAANG and product companies. Most interviews are conducted in JavaScript but knowing TypeScript type system, generics, and utility types is a differentiator at senior level. At entry level, JavaScript fluency is sufficient.
Do frontend interviews include DSA questions? Yes, at most FAANG companies. Frontend interviews typically include at least one round of standard DSA problems alongside the frontend-specific rounds. The DSA problems are the same medium difficulty algorithmic questions as backend interviews. Do not skip DSA preparation for frontend roles.
What is the most commonly missed JavaScript topic in frontend interviews? The event loop and the difference between microtasks and macrotasks. Most candidates know that Promises are async and that setTimeout is async. Far fewer can correctly predict the execution order of a mixed synchronous, Promise, and setTimeout sequence. Practice this until it is automatic.
How important is CSS knowledge in frontend interviews? At most product companies, CSS is tested lightly: specificity, the box model, flexbox and grid basics, and positioning. At companies with a stronger design focus, CSS knowledge goes deeper. FAANG companies typically focus more on JavaScript and React than CSS depth.
Summary
Frontend interviews test JavaScript internals and event loop mechanics, React concepts and performance patterns, browser and web platform knowledge, and practical UI implementation. The areas most candidates underinvest in are JavaScript fundamentals and browser internals, which is exactly where experienced interviewers probe most.
Book a frontend mock interview on Intervue.io to practice with an engineer who knows what the bar looks like at the company you are targeting and will give you specific feedback on where your answers need more depth.
Visit intervue.io




