You've been writing React for 5 years. You can center a div (most days). But these 12 questions still trip up senior frontend developers in interviews.
I compiled these from 40+ real interview experiences — not theoretical gotchas, but questions that actually separate "senior" from "truly senior."
1. Event Loop: Predict the Output
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
Answer: 1, 4, 3, 2
Why seniors get it wrong: They know setTimeout is async but forget microtasks (Promises) execute before macrotasks (setTimeout). The event loop processes the entire microtask queue before moving to the next macrotask.
Interview tip: Draw the call stack, microtask queue, and macrotask queue. Interviewers love visual thinkers.
2. CSS Specificity: Which Style Wins?
#header .nav li a { color: blue; } /* Specificity: 0-1-2-1 */
.nav-link.active { color: red; } /* Specificity: 0-0-2-0 */
Answer: Blue wins (ID selector trumps any number of classes).
The trap: Many seniors think "more classes = higher specificity." Wrong. One ID beats 100 classes.
| Selector | IDs | Classes | Elements |
|---|---|---|---|
#header .nav li a |
1 | 1 | 2 |
.nav-link.active |
0 | 2 | 0 |
Complexity: Specificity is calculated as a tuple, not a single number.
3. Closure Memory Leak
function createButtons() {
const buttons = [];
for (var i = 0; i < 5; i++) {
buttons.push(() => console.log(i));
}
return buttons;
}
createButtons().forEach(btn => btn());
Output: 5, 5, 5, 5, 5 (not 0,1,2,3,4)
Fix: Use let instead of var, or create an IIFE.
// Fix 1: let (block scoping)
for (let i = 0; i < 5; i++) { ... }
// Fix 2: IIFE (classic)
for (var i = 0; i < 5; i++) {
(function(j) {
buttons.push(() => console.log(j));
})(i);
}
Why it matters: This reveals whether you understand scope chains vs. block scoping — critical for debugging production memory leaks.
4. this in Arrow Functions
const obj = {
name: 'Widget',
getName: () => this.name,
getNameFn: function() { return this.name; }
};
console.log(obj.getName()); // undefined
console.log(obj.getNameFn()); // 'Widget'
Why: Arrow functions don't have their own this — they inherit from the enclosing lexical scope (which is the module/global scope here, not obj).
Senior trap: "I always use arrow functions" → but do you know when not to?
5. Virtual DOM: When It's Actually Slower
Many seniors say "Virtual DOM makes React fast." That's... not quite right.
The truth:
- Virtual DOM adds overhead (diffing cost)
- Direct DOM manipulation is faster for simple, targeted updates
- VDOM shines when you don't know what changed (declarative updates)
- Svelte proves you don't need VDOM for great performance
Interview answer: "VDOM is a tradeoff — it gives us declarative UI at the cost of diffing overhead. It's not inherently fast; it's fast enough while being developer-friendly."
6. Web Vitals: What Actually Matters
LCP (Largest Contentful Paint) → Loading performance
FID (First Input Delay) → Interactivity
CLS (Cumulative Layout Shift) → Visual stability
INP (Interaction to Next Paint) → Responsiveness (replaced FID)
Senior trap: Knowing the acronyms but not how to fix them.
Quick wins:
- LCP: Preload hero image, use
fetchpriority="high" - CLS: Set explicit width/height on images/videos
- INP: Break long tasks with
scheduler.yield()
Complexity: INP replaced FID in March 2024. If you still mention FID as current, the interviewer knows you're outdated.
7. Promise.all vs Promise.allSettled
// Promise.all — fails fast
Promise.all([fetch('/a'), fetch('/b'), fetch('/c')])
.catch(err => /* ONE failure kills ALL */)
// Promise.allSettled — always completes
Promise.allSettled([fetch('/a'), fetch('/b'), fetch('/c')])
.then(results => results.filter(r => r.status === 'fulfilled'))
When to use which:
-
all: All-or-nothing operations (e.g., loading initial app data) -
allSettled: Partial success is acceptable (e.g., dashboard widgets) -
race: First response wins (e.g., fastest CDN) -
any: First success wins (e.g., fallback APIs)
8. CSS Container Queries vs Media Queries
/* Media Query: based on VIEWPORT */
@media (min-width: 768px) { .card { flex-direction: row; } }
/* Container Query: based on PARENT */
@container (min-width: 400px) { .card { flex-direction: row; } }
Why it matters: Components should respond to their container, not the viewport. A card in a sidebar should be narrow even on a wide screen.
Senior trap: Still using only media queries in 2026. Container queries have 95%+ browser support.
9. WeakMap and WeakRef: When You Need Them
// Regular Map → memory leak
const cache = new Map();
function process(obj) {
cache.set(obj, expensiveComputation(obj));
// obj can never be garbage collected!
}
// WeakMap → no memory leak
const cache = new WeakMap();
function process(obj) {
cache.set(obj, expensiveComputation(obj));
// obj can be GC'd when no other references exist
}
Use cases: DOM element metadata, private class fields, caching without preventing GC.
10. Critical Rendering Path
Can you explain what happens between typing a URL and seeing pixels?
DNS → TCP → TLS → HTTP → HTML parsing → CSSOM → Render Tree
→ Layout → Paint → Composite
Key insight: CSS blocks rendering. JavaScript blocks HTML parsing (unless defer/async).
Performance pattern:
<link rel="preload" href="critical.css" as="style">
<script defer src="app.js"></script>
11. Hydration: The Cost Nobody Talks About
SSR HTML arrives → Browser shows content (fast FCP!)
→ JavaScript downloads → Framework "hydrates"
→ Page becomes interactive (slow TTI...)
The gap between FCP and TTI is the hydration tax.
Modern solutions:
- Partial hydration (Astro islands)
- Resumability (Qwik — zero hydration)
- Streaming SSR (React 18 Suspense)
- Progressive hydration (load JS for visible components first)
Senior answer: "I'd evaluate whether we even need client-side JS for this component."
12. Tree Shaking: Why Your Bundle Is Still Fat
// ❌ This imports EVERYTHING
import _ from 'lodash'; // 71KB gzipped
// ✅ This imports only what you use
import debounce from 'lodash-es/debounce'; // 1KB
Why tree shaking fails:
- CommonJS (
require) can't be statically analyzed - Side effects in modules prevent elimination
- Missing
"sideEffects": falsein package.json - Barrel files (
index.jsre-exports) defeat tree shaking
Check your bundle: npx webpack-bundle-analyzer
Want the Full 40-Question Set?
Want more? Check out my developer resources on Gumroad — covering React internals, TypeScript advanced patterns, system design for frontends, and accessibility gotchas.
More cheat sheets:
More by MaxMini
🛠️ 27+ Free Developer Tools — JSON formatter, UUID generator, password analyzer, and more. All browser-based, no signup.
🎮 27 Browser Games — Built with vanilla JS. Play instantly, no install.
📚 Developer Resources on Gumroad — AI prompt packs, automation playbooks, and productivity guides.
💰 DonFlow — Free budget tracker. Plan vs reality, zero backend.
🏦 Building something? Check out DonFlow — a budget drift detector that runs entirely in your browser. Zero backend, zero signup, your data never leaves your device.
Top comments (0)