Module 12 · Stack · Full-Stack
Frontend
Frontend interviews probe whether you understand the platform beneath the framework — the event loop, the DOM, accessibility. Get those, and React is just an application of them.
By the end you'll be able to explain, with conviction:
- Why semantic HTML and the event loop matter more than any framework.
- The React mental model — components, virtual DOM, hooks.
- State management and the real frontend performance levers.
1HTML semantics & accessibility
Using the right element isn't pedantry — it's free accessibility, SEO, and resilience.
Semantic HTML means using elements for their meaning — <nav>, <button>, <article>, <header> — rather than a sea of <div>s. The payoff is large: screen readers announce a real <button> correctly and make it keyboard-operable for free, search engines understand structure, and the markup is self-documenting. A <div onclick> reinvents all of that, badly.
Accessibility (a11y) builds on that foundation: keyboard navigability, sufficient colour contrast (WCAG AA), alt text on images, labels on form inputs, and ARIA attributes only where native semantics fall short. The first rule of ARIA is "don't use ARIA if a native element does the job." Mentioning that you build accessibly by default — not as an afterthought — is a strong, increasingly expected signal.
Interview angle
"I reach for semantic elements first because they give accessibility and keyboard support for free — a real button is focusable and announced correctly, a div isn't. ARIA is a patch for when native semantics aren't enough, not a default."
2CSS layout — Flexbox vs Grid
Modern layout comes down to two systems, and knowing when to use each is the question. Flexbox is one-dimensional — it lays out items along a single axis (a row or a column) and excels at distributing space and aligning items: navbars, button groups, centering. Grid is two-dimensional — rows and columns together — and excels at overall page structure and complex layouts.
The clean rule: Grid for the macro layout (the page skeleton), Flexbox for the micro (aligning items within a component). They compose — a grid cell often contains a flex container. Knowing the box model (content, padding, border, margin) and box-sizing: border-box underneath rounds out the fundamentals.
3JavaScript core — closures & the event loop
Two concepts separate people who "use JS" from people who understand it. A closure is a function that remembers the variables from the scope where it was created, even after that scope has returned. It's the mechanism behind data privacy, function factories, and hooks like useState — the function "closes over" its surrounding state.
The event loop explains how single-threaded JavaScript handles concurrency without blocking. Synchronous code runs on the call stack; async callbacks wait in queues and are run only when the stack is empty.
The crucial detail interviewers love: microtasks (promise callbacks) run before macrotasks (setTimeout), so a resolved promise's .then fires before a setTimeout(0). Being able to explain that ordering — and that JS is single-threaded but non-blocking — is a genuine seniority marker.
Interview angle
"JavaScript is single-threaded but non-blocking: synchronous code runs on the call stack, and the event loop schedules async callbacks when it clears — microtasks like promises before macrotasks like setTimeout. Closures are functions remembering their birth scope, which is what powers things like useState."
4TypeScript — why it matters
TypeScript adds static types on top of JavaScript, caught at compile time and erased at runtime. The value is the same as generics in Module 01: move whole categories of errors from runtime (a 2 a.m. production page) to compile time (a red squiggle as you type). On a large codebase with many contributors, that's transformative — the types are also living documentation and power confident refactoring and editor autocomplete.
The honest tradeoff: a learning curve and some ceremony, plus the escape hatch any that, overused, throws away the benefit. The mature take is that on any non-trivial or team project, the upfront cost pays for itself many times over in caught bugs and safer change — which is why it's become the default for serious frontend work.
5React mental model
React's core idea is UI as a function of state: you describe what the UI should look like for a given state, and React handles updating the DOM when state changes. You stop manually poking the DOM and instead declare the end result — the same declarative shift you saw in IaC and Kubernetes (Module 09).
The virtual DOM is how it does this efficiently. On a state change, React builds a lightweight virtual representation of the new UI, diffs it against the previous one (reconciliation), and applies only the minimal real-DOM changes — because touching the real DOM is slow. Components are the reusable building blocks (encapsulation again), and data flows one way: parent to child via props, which makes the UI predictable and debuggable.
Interview angle
"React makes the UI a declarative function of state — I describe what it should be, React reconciles the difference. The virtual DOM lets it batch and minimise expensive real-DOM updates, and one-way data flow from props keeps the app predictable."
6Hooks & lifecycle
Hooks let function components use state and side effects. The two you must know cold: useState holds local state and re-renders the component when it changes; useEffect runs side effects (data fetching, subscriptions) after render, controlled by a dependency array that decides when it re-runs.
The dependency array is where most bugs live: an empty [] runs the effect once on mount; [x] re-runs when x changes; omitting it runs after every render. Forgetting a dependency causes stale data; over-including causes infinite loops. The "Rules of Hooks" (only call them at the top level, never in conditionals) exist because React tracks them by call order. useMemo/useCallback cache expensive values/functions between renders.
Common trap
The useEffect dependency array is the #1 React bug source. A missing dependency gives you a stale closure (old values); an unstable one (a new object/function each render) triggers an infinite re-render loop. Be ready to explain both.
7State management options
Not all state is equal, and matching the tool to the scope is the skill. Local component state (useState) for UI that one component owns. Lifting state up or Context for state a few related components share — Context avoids "prop drilling" through many layers. A global store (Redux, Zustand) for app-wide state touched from everywhere.
The senior instinct is restraint: keep state as local as possible, and don't reach for Redux reflexively — its boilerplate is only worth it for genuinely complex, widely-shared state. A huge category, server state (data from APIs), is now best handled by dedicated tools like React Query that manage caching, refetching, and staleness — distinct from client UI state.
Interview angle
"I keep state as local as I can and only escalate when sharing demands it — Context for a subtree, a global store for truly app-wide state. And I separate server state from UI state: things like React Query handle caching and refetching far better than stuffing API data into Redux."
8Performance — splitting & memoization
Frontend performance has two fronts. Load performance: ship less JavaScript. Code splitting and lazy loading break the bundle so users download only what the current view needs; tree shaking drops unused code. This is what moves Core Web Vitals (LCP, etc.).
Runtime performance: avoid unnecessary re-renders. Memoization (React.memo, useMemo, useCallback) skips recomputing or re-rendering when inputs haven't changed; for very long lists, virtualisation renders only the visible rows. The senior caveat: measure before optimising — premature memoization adds complexity and can even hurt. Profile, find the real bottleneck, then fix that.
9Browser storage
Three client-side stores, each with a purpose: localStorage — key-value strings, persists until cleared, ~5–10 MB, synchronous; good for non-sensitive preferences. sessionStorage — same API but cleared when the tab closes. Cookies — small, sent with every request to the server; the right place for auth tokens, ideally HttpOnly + Secure + SameSite (Module 05).
The security point ties the course together: never store sensitive tokens in localStorage — it's readable by any JavaScript, so an XSS bug hands an attacker the token. An HttpOnly cookie isn't reachable from JS, which is why it's safer for credentials. For larger structured client data, IndexedDB exists, but the three above cover most interview ground.
10Bundlers in one line
A bundler (Vite, webpack, esbuild) takes your many source modules, dependencies, and assets and produces optimised files the browser can load efficiently — resolving imports, transpiling modern/TypeScript syntax for older browsers, minifying, tree-shaking, and code-splitting. Vite is the modern default, fast in dev because it serves native ES modules and only bundles for production.
You won't be grilled on config; you're expected to know why a build step exists — browsers can't efficiently load hundreds of unoptimised modules, and you write modern code that must be transformed to run everywhere. It's the frontend's equivalent of the build tools in Module 09.
Recap — what you can now teach
- Semantic HTML gives accessibility and keyboard support for free; ARIA only patches the gaps.
- Grid for macro layout, Flexbox for micro; know the box model.
- JS is single-threaded, non-blocking: microtasks before macrotasks; closures remember scope.
- React = UI as a function of state + virtual-DOM diffing + one-way data flow.
useEffectdeps are the top bug source; keep state local, separate server state from UI state.- Perf = ship less JS (splitting) + avoid re-renders (memoization), but measure first; never put tokens in localStorage.
Self-check
Say each answer out loud before revealing it.
Why prefer a real <button> over a <div onclick>?
The native button is focusable, keyboard-operable, and announced correctly by screen readers for free; a div reinvents all of that and usually gets it wrong.
In the event loop, what runs first: a resolved promise's .then or a setTimeout(0)?
The promise — microtasks are drained before macrotasks like setTimeout each time the call stack empties.
What does the virtual DOM buy you?
It diffs the new UI against the old in memory and applies only the minimal real-DOM changes, avoiding slow, redundant DOM manipulation.
Why not store auth tokens in localStorage?
localStorage is readable by any JavaScript, so an XSS bug leaks the token. An HttpOnly cookie isn't accessible to JS, making it safer for credentials.
What's the most common useEffect mistake?
Getting the dependency array wrong — a missing dep gives stale values; an unstable dep (new object/function each render) causes an infinite re-render loop.