Frontend Engineer Interview Prep: Questions and a Mock Test
Frontend interviews have drifted a long way from trivia about which browser supports what. The modern loop tests whether you understand the platform underneath the framework: what blocks the main thread, why a layout shifts, what a component re-renders and why, and what the browser will and will not let one origin do to another. This page covers what the rounds examine, one set of numbers that changed recently enough to catch people out, and ends with a graded mock across six areas.
How the process is structured
| Round | Length | What it tests |
|---|---|---|
| 1.JavaScript fundamentals[1] | Not published | Closures and scoping, this binding, prototypes, promises and async ordering, and the event loop. Often delivered as short output-prediction snippets rather than as explanation questions. |
| 2.Component build[2] | Not published | Building a working interface live, typically an autocomplete, a data table or a modal. Assessed on state modelling, handling loading and error and empty states, keyboard support, and whether you notice the race condition when requests return out of order. |
| 3.Frontend system design[1] | Not published | Architecture for a non-trivial interface: component boundaries, state ownership, data fetching and caching, rendering strategy, and performance budgets. Core Web Vitals are the usual measurement vocabulary, assessed at "the 75th percentile of page loads". |
| 4.Accessibility and security review[2] | Not published | Reading markup and finding what is wrong with it. WCAG 2.2 is the current standard, organised around perceivable, operable, understandable and robust, at conformance levels A, AA and AAA. Browser security questions cover same-origin policy, XSS and token storage. |
Bracketed markers point to the dated sources at the end of this article. Loops change; check the retrieval dates before relying on a round count.
Core Web Vitals, with the current numbers
Performance questions in a frontend loop almost always route through Core Web Vitals, and the metric set changed recently enough that a lot of preparation material is wrong.
There are three. Largest Contentful Paint should occur within 2.5 seconds. Interaction to Next Paint should be 200 milliseconds or less. Cumulative Layout Shift should be 0.1 or less. All three are assessed at "the 75th percentile of page loads, segmented across mobile and desktop devices", which is worth saying out loud, because it means your median user's experience is not what is being measured.
The important currency detail: Interaction to Next Paint replaced First Input Delay. INP "was promoted in 2023 from experimental to pending status with the intent to eventually retire FID" and "became a stable Core Web Vital metric in 2024". Naming FID as a current Core Web Vital is a clean dating signal, and the difference is substantive rather than cosmetic. FID measured only the delay before the first interaction began processing, which flattered pages badly: an app could score well while every interaction after the first was slow. INP considers interactions across the whole page visit and includes the time to render the next frame, so it measures what the user actually feels.
The practical consequences follow. LCP is usually fixed by prioritising the hero resource, preloading it, avoiding lazy-loading it, and cutting render-blocking work. CLS is fixed by reserving space: explicit dimensions on media, space for anything injected late, and avoiding inserting content above what someone is already reading. INP is fixed by getting long tasks off the main thread, breaking up work, and not doing expensive rendering synchronously inside an event handler.
The event loop is the model everything rests on
Almost every JavaScript round contains a question that reduces to the event loop, because it explains both correctness and performance.
The model to be able to state: JavaScript runs on one thread with a call stack. When the stack empties, the runtime drains the microtask queue completely, then takes one task from the macrotask queue, then repeats. Promise callbacks are microtasks; setTimeout callbacks, and events, are tasks. That ordering is what makes a promise resolved with no delay run before a setTimeout with zero delay, and it is why an endlessly self-scheduling microtask starves the page while an endlessly self-scheduling timeout does not.
The performance consequence is the one interviewers care about. Rendering happens between tasks, so any synchronous work longer than a frame budget blocks painting and delays interaction response. That is the mechanism behind a poor INP score, and it is why the standard advice is to break long tasks up, yield to the main thread, and move genuinely heavy computation to a worker.
Expect the closure and scoping questions too, because they still separate people. A var declaration inside a loop shares one binding, which is the classic reason a set of callbacks all see the final value, while let creates a fresh binding per iteration. And expect this: arrow functions take this lexically from where they are defined, ordinary functions take it from how they are called, which is why a method extracted from an object and passed as a callback loses its receiver.
Rendering, reconciliation and re-renders
Framework rounds test whether you can explain why something rendered, not whether you can recite an API.
The general model: a change to state produces a new description of the UI, the framework compares it against the previous description, and applies the minimum set of real DOM operations. Keys exist so that comparison can match elements across renders. Using an array index as a key is the classic bug, because inserting at the front shifts every index, so the framework matches the wrong old element to each new one and component state ends up attached to the wrong row. A stable identifier from the data is the fix.
Re-render questions follow. A component re-renders when its own state changes, when its parent re-renders, or when a context it consumes changes. It does not re-render because a variable it closes over changed, which is a common source of confusion. Memoisation helps only when the comparison is cheaper than the render, and passing a freshly created object or inline function as a prop defeats it, since a new reference fails the shallow comparison every time.
Accessibility belongs in this section rather than as an afterthought, because in a strong loop it is examined alongside markup. The current standard is WCAG 2.2, published in October 2023, organised around four principles, perceivable, operable, understandable and robust, with success criteria at three conformance levels, A, AA and AAA. WCAG 3 exists only as an early draft. In practice the interview questions are concrete: use semantic elements rather than a div with a click handler, ensure keyboard operability and visible focus, label form controls, and reach for ARIA only when no native element expresses the semantics.
Data fetching, caching and the states people forget
Data fetching rounds are where interviewers find out whether you have shipped something real, because the failure modes are so specific.
The first is the incomplete state machine. Every fetch has at least four states, idle, loading, success and error, and a great many interfaces only implement two. Empty is a fifth that is distinct from loading and usually needs its own treatment, because a successful response with zero results is not an error and should not look like one.
The second is the race. Two requests fired for different queries can return out of order, so the slower earlier request overwrites the newer result. The answers are an abort controller cancelling the superseded request, or tracking the latest request identifier and discarding responses that are not current. Any candidate who has built a search-as-you-type interface will have met this.
The third is caching. A client cache needs a key that captures every input to the request, a staleness policy, and a story for invalidation after a mutation. Optimistic updates are the follow-up: applying the change locally before the server confirms makes the interface feel instant, and requires a rollback path when the request fails, which is the half candidates omit.
Beyond that, expect debouncing versus throttling as a distinction, retry with backoff for transient failures, and pagination, where cursor-based approaches avoid the duplicate and skipped rows that offset pagination produces when the underlying data changes between pages.
What the browser does and does not protect you from
Security questions in a frontend loop concentrate on the boundaries the browser enforces and the ones it does not.
The same-origin policy is the foundation: scripts from one origin cannot read responses from another, where origin means scheme, host and port together. Cross-Origin Resource Sharing is the server opting to relax that, which is why CORS errors are so often misdiagnosed as client bugs when the fix is always on the server.
Cross-site scripting remains the most commonly examined class, and the correct framing is contextual output encoding rather than input sanitisation, because the same string is safe in an HTML body and dangerous in a JavaScript context or a URL. Content Security Policy is defence in depth on top, and dangerously setting inner HTML from user content is the direct route to the vulnerability.
Token storage is a favourite because there is no clean answer. Local storage is readable by any script on the page, so an XSS becomes a token theft. A cookie with HttpOnly is not readable by script, which removes that path, but cookies are attached automatically and therefore need SameSite and CSRF protection. The strong answer states the tradeoff rather than pretending one option is simply correct.
Worth knowing that in OWASP's 2025 Top 10, broken access control remains first and injection has moved to fifth, and that neither of those is a browser-side fix. A frontend that hides a button is not enforcing anything, because the client is under the user's control and every check that matters happens on the server.
What they actually ask
1.Build an autocomplete search field. Talk me through what you handle.
What a strong answer coversInterviewers use this because it contains most frontend concerns at once. Expected: debouncing input so a request is not sent per keystroke, and being clear that debounce delays until input stops while throttle limits rate. Cancelling superseded requests with an abort controller, or discarding stale responses, so a slow early request cannot overwrite a fast later one. A complete state machine including empty results, which is not an error. Keyboard support, arrow keys, enter, escape, and the ARIA combobox semantics that make the listbox announce properly, since a div-based dropdown is invisible to a screen reader. Caching by query string, minimum query length, and what happens offline. The strongest candidates mention that results must not shift the page layout as they load, which is a Cumulative Layout Shift concern.
2.Largest Contentful Paint on the marketing page is 4.5 seconds. How do you approach it?
What a strong answer coversMeasure before changing anything, and specifically at the 75th percentile on real devices rather than on a developer laptop, since the threshold is defined that way. Then identify which element is the LCP element, because the fix depends on whether it is an image, a text block or a video poster. Common causes: the resource is discovered late because it is inserted by JavaScript, it is lazy-loaded when it is above the fold, render-blocking stylesheets or fonts delay it, or the server response itself is slow. Fixes follow: preload the resource, mark it high priority, remove lazy loading for it, inline critical CSS, use font-display so text is not invisible while a webfont loads, and address time to first byte with caching or a CDN. The 2.5 second target should be stated as the goal.
3.A colleague says accessibility can be added at the end of the project. What is your response?
What a strong answer coversThe practical argument is that retrofitting is far more expensive, because the defects are structural rather than cosmetic: a custom control built from divs cannot be made keyboard operable by adding attributes, it has to be rebuilt or replaced with a native element. Strong answers name concrete things that are nearly free at build time and painful later, semantic elements, labels associated with inputs, focus management in modals, colour contrast, and not conveying meaning by colour alone. They reference the standard rather than opinion, WCAG 2.2 at level AA being the common contractual target, organised around perceivable, operable, understandable and robust. Mentioning the legal and procurement reality without being alarmist is reasonable, as is noting that automated tools catch only a fraction of issues and keyboard-only testing catches more.
4.Where would you store an authentication token in a single-page application?
What a strong answer coversThe strongest answers refuse the premise that one option is simply correct and lay out the tradeoff. Local storage is convenient and is readable by any script on the page, so a single cross-site scripting flaw becomes full token theft, and tokens there are not protected from third-party scripts you did not audit. An HttpOnly cookie cannot be read by script, which removes that path, but it is sent automatically with requests, which introduces cross-site request forgery and therefore requires SameSite and usually a token or origin check. In-memory storage is the safest against exfiltration and does not survive a refresh, which pushes you toward a refresh token in an HttpOnly cookie. Strong candidates conclude that the real mitigation is preventing XSS in the first place, because no storage choice survives arbitrary script execution on your origin.
5.A list of 10,000 rows makes the page unusable. What do you do?
What a strong answer coversExpected first move is to establish where the time goes rather than guessing: is it initial render, is it re-render on every keystroke, is it layout and paint, or is it memory pressure. Then the structural answer, which is to stop rendering what is not visible, through virtualisation that mounts only the rows in the viewport plus a small overscan. Strong answers cover the complications virtualisation brings, variable row heights, keyboard navigation, and find-in-page no longer working, and mention pagination or progressive loading as simpler alternatives that may be better product decisions. They also address the re-render side: stable keys from data rather than array indices, memoised row components, and not creating new callback references per row, which defeats the memoisation.
6.How would you structure state in an application that has grown to fifty components?
What a strong answer coversThe organising principle is that different kinds of state need different homes. Server data is a cache of something you do not own and belongs in a data-fetching layer with staleness and invalidation, not in a global store copied by hand. URL state, including filters and the current page, belongs in the URL so it can be shared and restored. Local UI state should live in the component that owns it and be lifted only as far as the nearest common ancestor that needs it. Truly global client state is usually small, theme, session, feature flags. Strong answers warn that context re-renders every consumer when its value changes, so a single large context is a performance trap, and that the common failure is putting server data into a global store and then reimplementing caching badly.
Three sample questions, answered
These three show the level the mock is pitched at, with the answer and the reasoning in the open. The graded paper keeps its answer key server-side.
- Time to Interactive, at 3.8 seconds or less
- Interaction to Next Paint, at 200 milliseconds or less
- Total Blocking Time, at 200 milliseconds or less
- First Contentful Paint, at 1.8 seconds or less
Why: Interaction to Next Paint became a stable Core Web Vital in 2024, replacing First Input Delay. The good threshold is 200 milliseconds or less. INP is stricter because it considers interactions throughout the page visit and includes rendering the next frame, rather than only measuring the delay before the first interaction began processing.
- Indices are numbers, and keys must be strings
- It prevents the list from being sorted
- It causes the entire list to unmount on every render
- Inserting at the front shifts every index, so state and DOM nodes get matched to the wrong items
Why: Keys let the reconciler match elements across renders. An index is a position, not an identity, so any insertion or reordering changes which item each key refers to. The visible symptom is component state, such as text typed into an input, staying with the position rather than following the data. A stable id from the data fixes it.
- The promise callback, because microtasks drain before the next task is taken
- The setTimeout callback, because timers have higher priority
- Whichever was scheduled first, since both queues are FIFO together
- They run in the same tick, so the order is not defined
Why: When the call stack empties, the runtime drains the entire microtask queue before taking one task from the macrotask queue. Promise reactions are microtasks; timer callbacks are tasks. This is also why a microtask that continually schedules another microtask starves the page, while a self-scheduling setTimeout does not.
An 18-question knowledge check
This is a knowledge check, not a simulation. The real loop happens on a whiteboard, in an editor, and in conversation. What this paper does measure is the underlying knowledge those rounds draw on: each question is tagged with a topic, grading happens per topic, and a weak topic points you at the course that fixes it.
- 1.At what percentile are Core Web Vitals thresholds assessed?Performance and Core Web Vitals
- 2.Which change most directly reduces Cumulative Layout Shift?Performance and Core Web Vitals
- 3.Why is Interaction to Next Paint a stricter measure than First Input Delay was?Performance and Core Web Vitals
- 4.A loop using var to declare its counter creates three callbacks. What value do they all see?JavaScript and the event loop
- 5.An object method is passed directly as a click handler and this becomes undefined. Why?JavaScript and the event loop
- 6.Why does a long synchronous task hurt interaction responsiveness?JavaScript and the event loop
- 7.Which of these does NOT cause a React-style component to re-render?State, hooks and effects
- 8.Memoising a child component has no effect because it re-renders every time. What is the most likely cause?State, hooks and effects
- 9.Under WCAG 2.2, which is the strongest reason to use a button element rather than a div with a click handler?Rendering, semantics and accessibility
- 10.A user types quickly in a search box and sees results for an earlier query. What is happening?Data fetching and client caching
- 11.What does list virtualisation actually change, and what does it cost?Rendering, semantics and accessibility
- 12.What must an optimistic update always include?State, hooks and effects
- 13.Why does offset-based pagination produce duplicate or missing rows on an active dataset?Data fetching and client caching
- 14.A fetch succeeds and returns an empty array. How should the interface treat it?Data fetching and client caching
- 15.What exactly does the same-origin policy compare?Browser security
- 16.Setting innerHTML from a value the user supplied creates which vulnerability?Browser security
- 17.What does an HttpOnly cookie protect against, and what does it not?Browser security
- 18.In OWASP's Top 10 2025, which category ranks first, and what does that imply for frontend work?Browser security
Sources
Hiring loops change. Every claim above carries a retrieval date so you can judge how current it is.
- [1]web.dev, Web Vitals · retrieved 2026-08-13
- [2]W3C Web Accessibility Initiative, WCAG 2 Overview · retrieved 2026-08-13
- [3]OWASP, Top 10:2025 · retrieved 2026-08-13
Refresh your memory
Free learning paths covering the ground this loop tests, whatever your score. Each one ends with a shareable certificate.
- ProgrammingFull-Stack with Node.js, React, and Next.js
An eight-lesson path that builds a full-stack engineer from the runtime up. Start with the Node.js event loop, build a REST API with Express, learn React fundamentals and hooks, master modern data fetching, then move into Next.js App Router, Server Components, Server Actions, and a Prisma + Postgres deployment.
8 lessons - ProgrammingPerformance Profiling: Finding the Bottleneck That Matters
Most optimisation effort goes into code that was never the problem, because intuition about where time goes is reliably wrong. This path builds the discipline instead: the arithmetic that caps what any optimisation can buy, how profilers work and how to read a flame graph, the six causes behind almost all real slowness, and how to benchmark honestly and recognise the point where optimising stops paying.
4 lessons - ProgrammingTesting Strategy: What Is Worth Testing
Tests are a purchase, not a virtue: you spend writing time, run time and maintenance time to buy confidence about specific breakages. This path works out how to spend that budget. What each level of test can and cannot catch, how test doubles buy speed by trading away fidelity, how property-based testing finds the inputs you would never have imagined, and why flakiness destroys a suite faster than any missing test.
4 lessons - Computer ScienceHow Computer Networks Actually Work
A request leaves your browser and arrives somewhere across the world in tens of milliseconds. This path follows it the whole way down. You will learn how headers nest as a packet is built, why MTU mismatches cause the classic bug where small requests work and large ones hang, how routers choose a path by longest prefix match and how a BGP mistake can take a network off the internet, how TCP turns an unreliable network into an ordered stream and what congestion control is really negotiating, and why HTTP/3 abandoned TCP for QUIC.
4 lessons - ProgrammingAPI Design & Reliability
Six lessons on shipping an HTTP API that other engineers actually enjoy using. Start with REST principles and status codes, secure it with OAuth 2.0, then make it survive real traffic with rate limiting, idempotency, and load balancing.
6 lessons

