DEV Community

Cover image for 12 Frontend Interview Questions That Senior Devs Still Get Wrong
MaxxMini
MaxxMini

Posted on • Edited on

12 Frontend Interview Questions That Senior Devs Still Get Wrong

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');
Enter fullscreen mode Exit fullscreen mode

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 */
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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'))
Enter fullscreen mode Exit fullscreen mode

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; } }
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

11. Hydration: The Cost Nobody Talks About

SSR HTML arrives → Browser shows content (fast FCP!)
→ JavaScript downloads → Framework "hydrates"
→ Page becomes interactive (slow TTI...)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Why tree shaking fails:

  • CommonJS (require) can't be statically analyzed
  • Side effects in modules prevent elimination
  • Missing "sideEffects": false in package.json
  • Barrel files (index.js re-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)