FrontendJavaScriptReactSystem DesignFAANG Prep

Frontend Interview โ€” The Complete Guide

Everything you need to crack frontend interviews at top companies. From HTML semantics to system design, organized by priority and depth.

120 min read12 sections
01

HTML โ€” Structure & Semantics

HTML is the skeleton of every web page. Interviewers test it less often than JS, but when they do, they expect you to know semantics and accessibility cold. Getting these right signals maturity.

๐Ÿ”ฅ Semantic Tags โ€” Must Know

Semantic HTML means using tags that describe their content: <header>, <nav>, <main>, <article>, <section>, <aside>, <footer>. Screen readers and search engines rely on these to understand page structure.

Why it matters in interviews

Interviewers check if you default to <div> for everything or use meaningful tags. Using semantic HTML shows you care about accessibility and SEO โ€” both are table stakes at top companies.

Accessibility Basics

  • Always use alt on images (empty string for decorative)
  • Use aria-label for icon-only buttons
  • Ensure keyboard navigation works (focus management)
  • Use role attributes when semantic tags aren't enough
  • Color contrast ratio โ‰ฅ 4.5:1 for normal text

Forms & Input Types

Know the difference between type="text", email, number, tel, date. Mobile browsers show different keyboards based on type. Use <label> with htmlFor for accessibility. Controlled vs uncontrolled forms come up in React interviews.

SEO Basics

  • One <h1> per page, hierarchical headings
  • Meta tags: title, description, og:image
  • Semantic structure helps crawlers understand content
  • SSR/SSG improves SEO (covered in Next.js section)

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Semantic tags: header, nav, main, article, section, aside, footer โ€” use them instead of divs

โ–ธ

Accessibility: alt text, aria-label, keyboard nav, focus management, color contrast

โ–ธ

Forms: Use correct input types, label with htmlFor, validation attributes

โ–ธ

SEO: One h1, meta tags, semantic structure, SSR for crawlability

Common Interview Questions

Q:What's the difference between <section> and <div>?

A: <section> is semantic โ€” it represents a thematic grouping of content with a heading. <div> is a generic container with no semantic meaning. Use <section> when the content forms a logical group; use <div> for styling/layout purposes only.

Q:How do you make a website accessible?

A: Use semantic HTML, add alt text to images, ensure keyboard navigation, manage focus for modals/dialogs, use ARIA attributes when needed, maintain sufficient color contrast, and test with screen readers.

Q:What is the purpose of the <meta> viewport tag?

A: It controls how the page scales on mobile devices. Without it, mobile browsers render the page at desktop width and zoom out. The standard value is width=device-width, initial-scale=1.

02

CSS โ€” Layout & Rendering

CSS questions test your understanding of layout mechanics. You won't be asked to style a pixel-perfect design, but you will be asked to explain how the box model works or when to use flexbox vs grid.

๐Ÿ”ฅ Box Model โ€” Must Know

Every element is a box: content โ†’ padding โ†’ border โ†’ margin. The key gotcha: by default, width only sets the content width. Use box-sizing: border-box to include padding and border in the width calculation. Every modern CSS reset does this.

Flexbox vs Grid

FeatureFlexboxGrid
Dimension1D (row OR column)2D (rows AND columns)
Best forNavbars, card rows, centeringPage layouts, dashboards, complex grids
Alignmentjustify-content, align-itemsjustify-items, align-items, place-items
Sizingflex-grow, flex-shrink, flex-basisfr units, minmax(), auto-fill/auto-fit
When to pickContent flows in one directionYou need precise row + column control

Positioning

  • static โ€” default, normal flow
  • relative โ€” offset from normal position, still in flow
  • absolute โ€” removed from flow, positioned relative to nearest positioned ancestor
  • fixed โ€” removed from flow, positioned relative to viewport
  • sticky โ€” hybrid: relative until a scroll threshold, then fixed

Specificity & Cascade

Specificity determines which CSS rule wins when multiple rules target the same element. The hierarchy: !important > inline styles > #id > .class > element. Equal specificity? Last rule wins. Understanding this prevents "why isn't my style applying?" debugging nightmares.

๐Ÿ’ก Pro Tip

In interviews, if asked "how would you center a div," give the flexbox answer first (display: flex; justify-content: center; align-items: center), then mention grid (display: grid; place-items: center) as an alternative. Shows you know both.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Box model: content โ†’ padding โ†’ border โ†’ margin. Use border-box.

โ–ธ

Flexbox: 1D layout. justify-content for main axis, align-items for cross axis.

โ–ธ

Grid: 2D layout. fr units, grid-template-columns/rows, gap.

โ–ธ

Specificity: !important > inline > #id > .class > element. Last rule wins on tie.

โ–ธ

Centering: Flexbox: justify-content + align-items. Grid: place-items: center.

Common Interview Questions

Q:Explain the CSS box model.

A: Every element is a rectangular box with four layers: content (the actual text/image), padding (space between content and border), border (the visible edge), and margin (space between this element and neighbors). box-sizing: border-box makes width/height include padding and border.

Q:When would you use Grid over Flexbox?

A: Use Grid when you need control over both rows and columns simultaneously โ€” dashboards, image galleries, page layouts. Use Flexbox for single-axis layouts โ€” navbars, card rows, centering content. They're complementary, not competing.

03

JavaScript โ€” Core (Most Important)

๐Ÿ”ฅ This is the highest-weight section

JavaScript fundamentals make or break frontend interviews. Expect 2-3 questions on closures, async behavior, or the event loop in every single interview. Master this section first.

๐Ÿ”ฅ Closures, Scope & this โ€” Must Know

A closure is a function that remembers the variables from its outer scope even after that scope has finished executing. Every function in JS creates a closure. This is how data privacy, callbacks, and module patterns work.

closure-example.tstypescript
function createCounter() {
  let count = 0; // closed over by the returned function
  return {
    increment: () => ++count,
    getCount: () => count,
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2
// 'count' is private โ€” can't be accessed directly

Scope chain: JS looks up variables from inner โ†’ outer โ†’ global. this depends on HOW a function is called, not where it's defined. Arrow functions inherit this from their enclosing scope (lexical binding).

๐Ÿ”ฅ Async JS & Event Loop โ€” VERY IMPORTANT

JavaScript is single-threaded but non-blocking. The Event Loop is the mechanism that allows JavaScript to perform non-blocking, asynchronous operations despite being a single-threaded language. It acts as a traffic coordinator, continuously monitoring the state of the execution environment to decide when to run specific pieces of code.

The event loop processes the call stack, then microtasks (Promises, queueMicrotask), then macrotasks (setTimeout, setInterval, I/O). Understanding this order is critical for predicting output in interview questions.

event-loop-order.tstypescript
console.log("1 โ€” sync");

setTimeout(() => console.log("2 โ€” macrotask"), 0);

Promise.resolve().then(() => console.log("3 โ€” microtask"));

console.log("4 โ€” sync");

// Output: 1, 4, 3, 2
// Sync first, then microtasks, then macrotasks

Promises & Async/Await

Promises represent eventual values. In JavaScript, a Promise is a special object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It serves as a placeholder for a result that is not yet available but will be in the future, allowing you to handle asynchronous code in a cleaner way than traditional nested callbacks (often called "callback hell").
async/await is syntactic sugar over Promises that makes async code read like sync code. Key: await pauses the async function, not the entire thread. The event loop keeps running.

async-await.tstypescript
async function fetchUser(id: string) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    console.error("Failed to fetch user:", err);
    return null;
  }
}

// Promise.all for parallel requests
const [user, posts] = await Promise.all([
  fetchUser("1"),
  fetch("/api/posts").then(r => r.json()),
]);

๐Ÿ”ฅ Debounce & Throttle โ€” Must Know

TechniqueWhat it doesUse case
DebounceWaits until the user STOPS triggering for N ms, then fires onceSearch input, resize handler, form validation
ThrottleFires at most once every N ms, regardless of how often triggeredScroll handler, mousemove, rate-limiting API calls
debounce.tstypescript
function debounce<T extends (...args: unknown[]) => void>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timer: ReturnType<typeof setTimeout>;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

Deep vs Shallow Copy

Shallow copy duplicates the top-level properties but nested objects still share references (Object.assign, spread operator). Deep copy recursively clones everything (structuredClone, JSON.parse(JSON.stringify())).

Prototypes

Every JS object has a hidden [[Prototype]] link. When you access a property, JS walks the prototype chain until it finds it or hits null. This is how inheritance works in JS. Classes are syntactic sugar over prototypal inheritance.

๐Ÿ’ก Pro Tip โ€” How to identify JS questions in interviews

If the interviewer shows you a code snippet and asks "what's the output?" โ€” it's testing closures, this, or event loop order. Trace through the execution step by step: sync code first, then microtasks, then macrotasks.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Closure: Function + its outer scope. Enables data privacy and callbacks.

โ–ธ

this: Depends on call site. Arrow functions use lexical this.

โ–ธ

Event loop: Call stack โ†’ microtasks (Promises) โ†’ macrotasks (setTimeout).

โ–ธ

async/await: Sugar over Promises. await pauses the function, not the thread.

โ–ธ

Debounce: Wait until user stops, then fire. For search inputs.

โ–ธ

Throttle: Fire at most once per interval. For scroll handlers.

โ–ธ

Deep copy: structuredClone() or JSON round-trip. Spread is shallow only.

Common Interview Questions

Q:What is a closure and why is it useful?

A: A closure is a function that retains access to variables from its enclosing scope even after that scope has returned. It's useful for data privacy (module pattern), callbacks, and creating factory functions. Every event handler in React is a closure.

Q:Explain the event loop. What's the output of this code? (setTimeout 0 vs Promise)

A: JS runs all synchronous code first (call stack), then drains the microtask queue (Promise callbacks, queueMicrotask), then picks one macrotask (setTimeout, setInterval). So Promise.then always runs before setTimeout(fn, 0).

Q:Implement debounce from scratch.

A: Return a wrapper function that clears the previous timer and sets a new one. The original function only fires after the user stops calling for `delay` ms. Key: use closure to hold the timer reference across calls.

04

React

React is the most tested framework in frontend interviews. You need to understand not just how to use it, but how it works under the hood โ€” rendering, reconciliation, and performance.

๐Ÿ”ฅ Hooks โ€” Must Know

useState for local state, useEffect for side effects, useRef for mutable refs that don't trigger re-renders, useMemo/useCallback for memoization. The most common interview question: explain the useEffect dependency array and cleanup function.

useEffect-gotchas.tsxtypescript
useEffect(() => {
  // Runs after every render (no deps array)
});

useEffect(() => {
  // Runs once on mount (empty deps)
  return () => {
    // Cleanup on unmount
  };
}, []);

useEffect(() => {
  // Runs when 'id' changes
  const controller = new AbortController();
  fetch(`/api/user/${id}`, { signal: controller.signal });
  return () => controller.abort(); // cancel on cleanup
}, [id]);

๐Ÿ”ฅ Rendering & Reconciliation

React re-renders a component when its state or props change. It diffs the new virtual DOM against the previous one (reconciliation) and applies minimal DOM updates. Keys help React identify which items changed in lists.

Performance Optimization

  • React.memo โ€” skip re-render if props haven't changed
  • useMemo โ€” cache expensive computations
  • useCallback โ€” stable function references for child components
  • Virtualize long lists (react-window, react-virtuoso)
  • Code-split with React.lazy + Suspense

Controlled vs Uncontrolled Components

AspectControlledUncontrolled
State lives inReact state (useState)The DOM (ref)
Value accessVia state variableVia ref.current.value
When to useMost forms โ€” validation, conditional logicSimple forms, file inputs, third-party libs
Re-rendersOn every keystrokeOnly when you read the ref

๐Ÿ’ก Pro Tip

When asked "how would you optimize a slow React app," start with: (1) identify unnecessary re-renders with React DevTools Profiler, (2) memoize with React.memo/useMemo, (3) virtualize long lists, (4) code-split heavy routes. This shows a systematic approach.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

useState: Local state. Triggers re-render on update.

โ–ธ

useEffect: Side effects after render. Cleanup on unmount or before re-run.

โ–ธ

useRef: Mutable ref that persists across renders without causing re-render.

โ–ธ

Reconciliation: React diffs virtual DOM trees and applies minimal real DOM updates.

โ–ธ

Keys: Help React identify list items. Use stable IDs, never array index.

โ–ธ

React.memo: HOC that skips re-render if props are shallowly equal.

Common Interview Questions

Q:What happens when you call setState in React?

A: React schedules a re-render (it's async/batched). On the next render, the component function runs again with the new state, React diffs the new virtual DOM against the old one, and applies the minimal set of real DOM mutations.

Q:Why shouldn't you use array index as a key?

A: If items are reordered, inserted, or deleted, the index-based keys don't match the actual items anymore. React reuses DOM nodes incorrectly, causing bugs like input values appearing in the wrong row. Use stable unique IDs instead.

Q:Explain the useEffect cleanup function.

A: The function returned from useEffect runs before the effect re-runs (when deps change) and on unmount. Use it to cancel subscriptions, abort fetch requests, clear timers, or remove event listeners to prevent memory leaks.

05

TypeScript

TypeScript is expected at most top companies now. Interviews test whether you can type real-world patterns โ€” not just primitives.

Types vs Interfaces

Featuretypeinterface
Union typesโœ… type A = string | numberโŒ Not possible
Extendsโœ… type B = A & { extra: string }โœ… interface B extends A { }
Declaration mergingโŒโœ… Same-name interfaces merge
When to useUnions, mapped types, utility typesObject shapes, class contracts, API responses

๐Ÿ”ฅ Generics โ€” Must Know

generics.tstypescript
// Generic function โ€” works with any type
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

// Generic with constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// Generic React component
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map(renderItem)}</ul>;
}

Utility Types

  • Partial<T> โ€” all properties optional
  • Required<T> โ€” all properties required
  • Pick<T, K> โ€” select specific properties
  • Omit<T, K> โ€” exclude specific properties
  • Record<K, V> โ€” object with keys K and values V
  • ReturnType<T> โ€” extract return type of a function

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

type vs interface: type for unions/mapped types. interface for object shapes and merging.

โ–ธ

Generics: Parameterize types. Use constraints with extends.

โ–ธ

Utility types: Partial, Required, Pick, Omit, Record, ReturnType.

โ–ธ

React typing: React.FC (avoid), Props interface, React.ReactNode for children.

06

Browser & Web Internals

๐Ÿ”ฅ Very High Value in Senior Interviews

Browser internals separate mid-level from senior candidates. Understanding the rendering pipeline, event loop, and storage APIs shows deep knowledge that interviewers love.

๐Ÿ”ฅ Event Loop & Call Stack

The call stack executes synchronous code. When it's empty, the event loop checks the microtask queue (Promises), drains it completely, then picks one macrotask (setTimeout). This cycle repeats. The browser can only paint between macrotasks.

In JavaScript, the Call Stack is a core mechanism that the engine uses to track and manage function execution. It acts as a "to-do list," keeping track of which function is currently running and where to return once that function finishes.

DOM vs Virtual DOM

AspectReal DOMVirtual DOM
WhatBrowser's actual document treeLightweight JS object representation
UpdatesDirect manipulation is expensiveDiff two trees, apply minimal patches
Who uses itVanilla JS, jQueryReact, Vue (internally)
PerformanceSlow for frequent updatesBatches updates, minimizes reflows

๐Ÿ”ฅ Rendering Pipeline

HTML โ†’ DOM, CSS โ†’ CSSOM โ†’ Render Tree โ†’ Layout โ†’ Paint โ†’ Composite. Blocking resources (CSS, sync JS) delay first paint. Understanding this pipeline is key to performance optimization.

What Happens When You Type a URL

DNS lookup โ†’ TCP handshake โ†’ TLS handshake (HTTPS) โ†’ HTTP request โ†’ Server response โ†’ HTML parsing โ†’ DOM construction โ†’ CSSOM โ†’ Render tree โ†’ Layout โ†’ Paint. This is a classic interview question that tests breadth of knowledge.

Storage APIs

StorageSizePersistenceUse case
localStorage~5MBUntil clearedUser preferences, theme, tokens
sessionStorage~5MBUntil tab closesForm drafts, temp state
Cookies~4KBConfigurable expiryAuth tokens (httpOnly), tracking
IndexedDBLarge (100MB+)Until clearedOffline data, caching large datasets

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Event loop: Stack โ†’ microtasks (all) โ†’ one macrotask โ†’ paint โ†’ repeat.

โ–ธ

Rendering: HTMLโ†’DOM, CSSโ†’CSSOM, mergeโ†’Render Tree, Layout, Paint, Composite.

โ–ธ

Virtual DOM: JS object tree. React diffs old vs new, patches real DOM minimally.

โ–ธ

URL flow: DNS โ†’ TCP โ†’ TLS โ†’ HTTP โ†’ Parse โ†’ DOM โ†’ CSSOM โ†’ Render โ†’ Paint.

โ–ธ

Storage: localStorage (persistent), sessionStorage (tab), cookies (server), IndexedDB (large).

07

Performance & Optimization

Performance questions test whether you can diagnose and fix slow apps. Know the tools (Lighthouse, DevTools) and the techniques.

๐Ÿ”ฅ Core Web Vitals โ€” Must Know

MetricWhat it measuresGood threshold
LCP (Largest Contentful Paint)When the main content is visibleโ‰ค 2.5s
INP (Interaction to Next Paint)Responsiveness to user inputโ‰ค 200ms
CLS (Cumulative Layout Shift)Visual stability (unexpected shifts)โ‰ค 0.1

Key Techniques โ€” Explained

๐Ÿ”ฅ Lazy Loading

Lazy loading defers loading resources until they're actually needed. Instead of downloading every image and component upfront (which blocks the initial page load), you load them on demand โ€” typically when they enter the viewport or when a user navigates to a route.

For images: add loading="lazy" to <img> tags. The browser handles the rest โ€” it only fetches the image when it's about to scroll into view. Next.js <Image> does this by default.

For components: use React.lazy() + <Suspense>. The component's code is split into a separate bundle chunk and only downloaded when the component is first rendered.

lazy-loading.tsxtypescript
import { lazy, Suspense } from "react";

// Component is NOT loaded until <HeavyChart /> is rendered
const HeavyChart = lazy(() => import("./HeavyChart"));

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      {/* Suspense shows fallback while the chunk downloads */}
      <Suspense fallback={<div>Loading chart...</div>}>
        <HeavyChart />
      </Suspense>
    </div>
  );
}

// Image lazy loading โ€” native browser support
<img src="/hero.jpg" alt="Hero" loading="lazy" />

When to use lazy loading

Use it for below-the-fold images, heavy third-party components (charts, editors, maps), and routes the user may never visit. Don't lazy-load above-the-fold content โ€” that hurts LCP.

๐Ÿ”ฅ Code Splitting

Code splitting breaks your JavaScript bundle into smaller chunks that load on demand. Without it, users download your entire app upfront โ€” even pages they'll never visit. With it, each route gets its own chunk, and shared code is extracted into common chunks.

Route-based splitting is the most common approach. Next.js does this automatically โ€” each page.tsx becomes its own chunk. In plain React, use React.lazy with React Router.

Component-based splitting is for heavy components within a page. Use dynamic import() to load a chart library, rich text editor, or PDF viewer only when needed.

code-splitting.tsxtypescript
// Route-based splitting with React Router + lazy
const Home = lazy(() => import("./pages/Home"));
const Settings = lazy(() => import("./pages/Settings"));

// Next.js does this automatically for every page.tsx

// Component-based splitting with next/dynamic
import dynamic from "next/dynamic";

const RichEditor = dynamic(() => import("./RichEditor"), {
  loading: () => <p>Loading editor...</p>,
  ssr: false, // skip server-side rendering for browser-only libs
});

Caching Strategies

LayerHow it worksWhat to cache
Browser cacheHTTP headers (Cache-Control, ETag) tell the browser to reuse responsesStatic assets (JS, CSS, images), API responses
CDN edge cacheContent is cached at servers close to the user geographicallyStatic files, SSG pages, public API responses
Service WorkerJS that intercepts network requests and serves cached responsesOffline support, app shell, API fallbacks
In-memory (React)useMemo, React Query cache, state that avoids refetchingComputed values, API data with stale-while-revalidate

Minimizing Re-renders

  • State colocation โ€” keep state as close to where it's used as possible. If only one component needs it, don't put it in a parent.
  • React.memo โ€” wrap components that receive the same props often. Skips re-render if props are shallowly equal.
  • useMemo / useCallback โ€” stabilize expensive computations and function references so child components don't re-render unnecessarily.
  • Avoid object/array literals in JSX โ€” style={{ color: "red" }} creates a new object every render, breaking memo.

Image Optimization

  • Use modern formats: WebP (30% smaller than JPEG) or AVIF (50% smaller)
  • Serve responsive images with srcset and sizes โ€” don't send a 2000px image to a 400px phone screen
  • Always set width and height on images to prevent layout shift (CLS)
  • Use Next.js <Image> โ€” it handles lazy loading, responsive sizing, and format conversion automatically

Tree Shaking

Tree shaking removes unused code from your final bundle at build time. It works with ES modules (import/export) because they're statically analyzable. CommonJS (require) can't be tree-shaken. This is why you should always use named imports: import { debounce } from "lodash-es" instead of import _ from "lodash".

๐Ÿ’ก Pro Tip

In interviews, always mention measurement before optimization. "I'd first profile with DevTools/Lighthouse to identify the bottleneck, then apply the appropriate fix." This shows you don't optimize blindly.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

LCP: Largest Contentful Paint โ‰ค 2.5s. Optimize images, fonts, server response.

โ–ธ

INP: Interaction to Next Paint โ‰ค 200ms. Reduce JS execution, defer non-critical work.

โ–ธ

CLS: Cumulative Layout Shift โ‰ค 0.1. Set dimensions on images/ads, avoid dynamic injection.

โ–ธ

Lazy loading: loading='lazy' for images, React.lazy + Suspense for components.

โ–ธ

Code splitting: Split by route. Dynamic import() for heavy modules.

08

Networking & APIs

Frontend devs need to understand HTTP fundamentals. You'll be asked about methods, status codes, CORS, and how to handle API errors gracefully.

HTTP Methods

MethodPurposeIdempotent?
GETRetrieve dataYes
POSTCreate a resourceNo
PUTReplace a resource entirelyYes
PATCHPartially update a resourceNo
DELETERemove a resourceYes

Status Codes to Know

CodeMeaningWhen you see it
200OKSuccessful GET/PUT
201CreatedSuccessful POST
204No ContentSuccessful DELETE
301Moved PermanentlyURL redirect (cached)
304Not ModifiedCached response is still valid
400Bad RequestInvalid input/params
401UnauthorizedMissing or invalid auth token
403ForbiddenValid auth but insufficient permissions
404Not FoundResource doesn't exist
429Too Many RequestsRate limited
500Internal Server ErrorServer bug

๐Ÿ”ฅ CORS โ€” Must Know

Cross-Origin Resource Sharing. Browsers block requests to different origins by default. The server must include Access-Control-Allow-Origin headers. Preflight requests (OPTIONS) happen for non-simple requests (custom headers, PUT/DELETE). This is a very common source of bugs and interview questions.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

GET: Read data. Idempotent. Cacheable.

โ–ธ

POST: Create data. Not idempotent. Body required.

โ–ธ

CORS: Browser security. Server must whitelist origins. Preflight for complex requests.

โ–ธ

401 vs 403: 401 = not authenticated. 403 = authenticated but not authorized.

โ–ธ

fetch vs axios: fetch is native (no deps). axios has interceptors, auto JSON, cancel tokens.

09

Next.js

Next.js is the default React framework at many companies. Know the rendering strategies and when to use each.

๐Ÿ”ฅ Rendering Strategies โ€” Must Know

This is the single most asked Next.js interview question. You need to explain not just what each strategy does, but when to pick one over another and the trade-offs involved.

CSR โ€” Client-Side Rendering

The server sends a minimal HTML shell with a <script> tag. JavaScript downloads, executes, and renders the page in the browser. The user sees a blank screen (or spinner) until JS finishes.

  • Pros: simple deployment (static hosting), fast subsequent navigation (SPA), good for auth-gated content
  • Cons: slow first paint (blank screen), poor SEO (crawlers see empty HTML), large JS bundle
  • Use when: dashboards, admin panels, apps behind login where SEO doesn't matter

SSR โ€” Server-Side Rendering

The server generates full HTML on every request and sends it to the browser. The user sees content immediately, then JS hydrates to make it interactive. Each request hits the server.

  • Pros: fast first paint, great SEO, always fresh data
  • Cons: higher server cost (every request runs server code), slower TTFB than static, can't cache easily
  • Use when: personalized pages (user profile, feed), content that changes per request, SEO-critical dynamic pages

SSG โ€” Static Site Generation

HTML is generated at build time and served as static files. No server computation at request time โ€” the CDN serves pre-built HTML instantly. Fastest possible TTFB.

  • Pros: fastest performance (CDN-served), cheapest (no server), excellent SEO
  • Cons: content is stale until next build, build time grows with page count, can't personalize
  • Use when: blogs, documentation, marketing pages, any content that rarely changes

ISR โ€” Incremental Static Regeneration

The best of SSG and SSR. Pages are statically generated at build time, but can be revalidated in the background after a configurable interval. The first visitor after the interval gets the stale page (fast), and the next visitor gets the freshly regenerated page.

  • Pros: static performance + fresh data, no full rebuild needed, scales to millions of pages
  • Cons: first visitor after revalidation sees stale data, more complex mental model
  • Use when: e-commerce product pages, news articles, content that updates periodically (not real-time)

SSR vs SSG

In modern Next.js (App Router), the default behavior is server-side rendering using React Server Components. However, Next.js automatically chooses between SSG and SSR based on data fetching, and developers can opt into client-side rendering using "use client".

next.js-rendering.tsxtypescript
// SSG โ€” generated at build time (default for static pages)
export default function BlogPost({ post }) {
  return <article>{post.content}</article>;
}

// SSR โ€” generated on every request
export const dynamic = "force-dynamic"; // App Router
// or use generateMetadata / fetch with { cache: "no-store" }

// ISR โ€” static + revalidate every 60 seconds
export const revalidate = 60; // App Router
// Next request after 60s triggers background regeneration

// CSR โ€” client-only component
"use client";
import { useEffect, useState } from "react";
export default function Dashboard() {
  const [data, setData] = useState(null);
  useEffect(() => { fetch("/api/stats").then(r => r.json()).then(setData); }, []);
  return data ? <Chart data={data} /> : <Spinner />;
}
StrategyWhen HTML is generatedBest forSEOData freshness
CSRIn the browser at runtimeDashboards, auth-gated pagesโŒ PoorAlways fresh (client fetch)
SSROn each request on the serverPersonalized, dynamic pagesโœ… GoodAlways fresh (server fetch)
SSGAt build timeBlogs, docs, marketingโœ… GreatStale until rebuild
ISRBuild time + background revalidationE-commerce, news, semi-dynamicโœ… GreatFresh within revalidation window

Routing & Middleware

App Router uses file-system routing with page.tsx, layout.tsx, loading.tsx, error.tsx. Middleware runs before every request โ€” use it for auth checks, redirects, and A/B testing. Server Components are the default; add "use client" only when you need browser APIs or state.

๐Ÿ’ก Pro Tip

When asked "SSR vs CSR," don't just list differences. Explain the trade-off: SSR gives better SEO and faster first paint but higher server cost. CSR gives faster navigation after initial load but worse SEO. Then mention ISR as the middle ground.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

CSR: Browser renders. Fast nav, poor SEO. Use for dashboards.

โ–ธ

SSR: Server renders per request. Good SEO, higher server cost.

โ–ธ

SSG: Build-time HTML. Best perf, stale until rebuild.

โ–ธ

ISR: SSG + revalidation. Best of both worlds for semi-dynamic content.

โ–ธ

Server Components: Default in App Router. No client JS. Add 'use client' for interactivity.

10

State Management

State management questions test whether you know when to use local state vs global state, and which tool fits which problem.

When to Use What

ApproachScopeBest for
useStateSingle componentForm inputs, toggles, local UI state
Lifting state upParent + childrenShared state between siblings
Context APISubtreeTheme, auth, locale โ€” infrequently changing data
Redux / ZustandGlobalComplex state with many consumers, frequent updates
React Query / SWRServer stateAPI data with caching, revalidation, optimistic updates

๐Ÿ”ฅ Key Interview Insight

The most common mistake is reaching for Redux/Context too early. Start with local state. Lift up only when siblings need it. Use Context for truly global, infrequently-changing data (theme, auth). Use a server-state library for API data. Redux is for complex client-side state that many components read and write.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Local first: useState for component-scoped state. Don't over-engineer.

โ–ธ

Lift up: Move state to the nearest common parent when siblings need it.

โ–ธ

Context: For theme/auth/locale. Avoid for frequently changing data (causes re-renders).

โ–ธ

Redux/Zustand: Global client state with many consumers. Zustand is simpler.

โ–ธ

React Query: Server state. Handles caching, refetching, loading/error states.

11

Testing

Testing questions are less common in coding rounds but come up in system design and behavioral rounds. Know the concepts and when to apply each type.

Unit vs Integration vs E2E

TypeWhat it testsToolsSpeed
UnitSingle function/component in isolationJest, VitestFast
IntegrationMultiple components working togetherReact Testing LibraryMedium
E2EFull user flows in a real browserPlaywright, CypressSlow

React Testing Library Philosophy

Test behavior, not implementation. Query by role, label, or text โ€” not by class name or test ID. If a user can't see it, don't test it. This approach makes tests resilient to refactors.

component-test.tsxtypescript
import { render, screen, fireEvent } from "@testing-library/react";
import Counter from "./Counter";

test("increments count on click", () => {
  render(<Counter />);
  const button = screen.getByRole("button", { name: /increment/i });
  fireEvent.click(button);
  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Unit tests: Test one thing in isolation. Fast. Use Jest/Vitest.

โ–ธ

Integration: Test components together. Use React Testing Library.

โ–ธ

E2E: Test full user flows. Use Playwright/Cypress. Slow but high confidence.

โ–ธ

RTL philosophy: Test behavior, not implementation. Query by role/text, not class.

โ–ธ

Testing pyramid: Many unit tests, fewer integration, fewest E2E.

12

Frontend System Design

๐Ÿ”ฅ High-value for senior roles

Frontend system design rounds are increasingly common at FAANG. You're expected to design component architectures, handle scalability, and make trade-off decisions. Many of these topics have dedicated deep-dive pages โ€” use them.

Component Design

  • Separate logic (smart/container) from presentation (dumb/presentational)
  • Keep components small and focused โ€” single responsibility
  • Use composition over inheritance
  • Design for reusability: props for configuration, children for content

Folder Structure

Feature-based structure groups files by feature (auth/, dashboard/, settings/) rather than by type (components/, hooks/, utils/). This scales better for large teams because each feature is self-contained.

Scalability Patterns

  • Virtualization for large lists (10K+ items)
  • Pagination / infinite scroll for large datasets
  • Debounced search for real-time filtering
  • Optimistic updates for perceived performance
  • Code splitting per route for bundle size

Handling Large Forms

  • Use a form library (React Hook Form, Formik) for complex forms
  • Validate on blur, not on every keystroke
  • Split large forms into steps (wizard pattern)
  • Use uncontrolled inputs with refs for performance-critical forms

Security

๐Ÿ’ก Pro Tip โ€” System Design Interview Framework

Use this structure: (1) Clarify requirements, (2) Define the component tree, (3) Identify state and data flow, (4) Handle edge cases (loading, error, empty), (5) Discuss performance and scalability, (6) Mention testing strategy. This framework works for any frontend system design question.

๐Ÿ“ Quick Revision

โšก

Quick Revision Cheat Sheet

โ–ธ

Component design: Smart/dumb split. Single responsibility. Composition over inheritance.

โ–ธ

Folder structure: Feature-based for scale. Group by domain, not file type.

โ–ธ

Large lists: Virtualize with react-window. Paginate or infinite scroll.

โ–ธ

Large forms: React Hook Form. Validate on blur. Wizard pattern for multi-step.

โ–ธ

Security: Sanitize inputs (XSS). CSRF tokens. httpOnly cookies for auth.

โ–ธ

Interview framework: Requirements โ†’ Components โ†’ State โ†’ Edge cases โ†’ Perf โ†’ Testing.

๐Ÿ”— All System Design Deep Dives