Front-end interviews cover far more than framework syntax. Expect questions on the box model and specificity, when to use Flexbox versus Grid, positioning and container queries, the critical rendering path and Core Web Vitals, and the difference between server-side rendering, static generation and client-side rendering. Accessibility and CORS come up regularly, and strong candidates are expected to reason about performance with measurements rather than assumptions. The questions below cover the whole stack a front-end engineer owns.
Behavioural Questions
1. Tell me about a front-end project you are proud of. What made it challenging?
Note: Choose the project with the most interesting constraint, not the one with the most features. Constraints are what produce good interview stories.
Give it in four parts:
- The problem and who it was for. Keep it to one or two sentences and include scale — users, page views, or how much of the business ran through it.
- The constraint that made it hard. Strong options: a strict performance budget on slow mobile networks, an accessibility requirement, supporting an old browser the customer base still used, or a design system you had to build alongside the feature.
- A decision you made and the alternative you rejected. This is where interviewers judge seniority — say what you traded away.
- The measured outcome. Load time, conversion, bounce rate, support tickets, or Lighthouse score before and after.
Be honest about what you would change. Naming a weakness in your own work reads as confidence, not doubt.
2. How do you work with designers, especially when a design is hard or expensive to build?
The answer interviewers want is that you push back with information rather than either refusing or silently absorbing the cost.
- Get involved before the handoff. Reviewing designs while they are still cheap to change prevents most conflicts. Ask about empty states, long text, error states, and loading behaviour — the things designs usually omit and developers end up inventing.
- Quantify the cost. "This animation adds 40KB and drops us below our performance budget on 3G" is a conversation. "That's not possible" is a fight.
- Offer an alternative that keeps the intent. Designers care about the effect, not the implementation. Usually there is a cheaper way to get the same feeling.
- Use a shared vocabulary. A design system with agreed tokens for spacing, colour, and type removes most of the pixel-level negotiation permanently.
Note: Mentioning that you raise accessibility issues at design time — contrast ratios, focus order, touch target size — is a strong differentiator, because it is much cheaper to fix there than in code.
3. Describe a time you had to improve the performance of a slow page. What did you do?
Tell it as measurement first, guessing never.
- What the complaint was, and what the data said. These often differ. Real user monitoring beats a lab test on your own laptop, because your laptop is not the user's phone.
- Which metric was actually bad. Naming the specific Core Web Vital shows you know they have different causes: LCP is usually images or a slow server response, CLS is missing dimensions or late-injected content, INP is long JavaScript tasks blocking the main thread.
- How you profiled. The Performance panel for main-thread work, the Network waterfall for request chains, the Coverage tab for unused CSS and JavaScript.
- What you changed. Typical high-value fixes: code splitting a route, lazy-loading below-the-fold images, adding width and height attributes, replacing a heavy dependency, preloading a font, or moving work off the main thread.
- The before and after numbers, and any business metric that moved with them.
Note: Mention the budget or CI check you added afterwards so the improvement did not silently erode.
4. How do you handle a situation where requirements change midway through building a feature?
Show that you treat it as normal rather than as a failure of process — because on most teams it is normal.
- Establish what actually changed. Often the underlying need has not changed at all, only the proposed solution. Asking what problem the new requirement solves sometimes reveals that what you have already built covers it.
- State the cost honestly and early. What is now wasted, what can be reused, and what the new date looks like. Absorbing scope quietly and missing the date is much worse than a difficult conversation on day three.
- Protect what is expensive to change. If the change touches a data model or an API contract other teams consume, that needs more scrutiny than a change to layout.
How to reduce the pain structurally: build in thin vertical slices behind a feature flag, so there is always something demonstrable and always a safe way to turn it off. Getting a rough version in front of the stakeholder early is the single most effective way to surface a change before it is expensive.
5. How do you make sure the interfaces you build are accessible, and have you ever had to argue for it?
Split it into what you do by default and what you check.
By default: semantic HTML before ARIA — a real <button> is keyboard-operable, focusable, and announced correctly with no extra work, while a <div onclick> needs four attributes and a key handler to catch up. Labels tied to inputs, headings in order, alt text that says what the image is for, and visible focus styles you never remove.
What you check:
- Tab through the whole feature without touching the mouse.
- Run axe or Lighthouse, understanding that automated tools catch perhaps a third of real problems.
- Test one flow with a screen reader — VoiceOver or NVDA.
- Check colour contrast at design time.
On arguing for it: the effective arguments are legal exposure, the size of the affected audience, and the fact that retrofitting costs several times more than building it in. The ineffective argument is that it is the right thing to do — true, but it rarely moves a roadmap on its own.
Technical Questions
1. Explain the CSS box model and the difference between content-box and border-box sizing.
Every element is drawn as four nested rectangles: the content box, then padding around it, then the border, then margin outside that. Margin is transparent space between elements and is not part of the element's own background.
box-sizing decides what width refers to:
content-boxis the default.width: 300pxsets the content area alone, so 20px of padding and a 2px border on each side make the element actually occupy 344px. This is why layouts overflow unexpectedly.border-boxmakeswidthinclude padding and border. Set 300px and the element occupies 300px, with the content area shrinking to accommodate the padding.
Almost every codebase applies this reset:
*, *::before, *::after { box-sizing: border-box; }Note: Be ready for margin collapsing as a follow-up. Adjacent vertical margins between block elements collapse to the larger of the two rather than adding up — and this does not happen in flex or grid containers, or across a border or padding.
2. What is the difference between Flexbox and CSS Grid, and how do you choose between them?
The clean distinction is dimensionality. Flexbox is one-dimensional — it lays items out along a single axis and the other axis follows the content. Grid is two-dimensional — you define rows and columns together and place items into that structure.
Reach for Flexbox when:
- The content should determine the sizing — a navigation bar, a toolbar, a row of buttons, a card footer.
- You want items to wrap naturally without caring which row they land on.
- You need alignment along one axis, including the classic centring case.
Reach for Grid when:
- The layout should determine the structure — a page skeleton, a dashboard, an image gallery with aligned rows and columns.
- You need items to line up in both directions, which flex-wrap cannot guarantee.
- You want to name areas and rearrange them at breakpoints with
grid-template-areas, which is far more readable than reordering markup.
Note: They are not rivals. The normal pattern is Grid for the page skeleton with Flexbox inside individual components. And auto-fit with minmax gives you a responsive gallery with no media queries at all: grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))
3. How does CSS specificity work, and how is a conflict between two rules resolved?
When several rules target the same element, the browser resolves it in this order:
- Origin and importance. An
!importantdeclaration beats a normal one; author styles beat user-agent defaults. - Specificity, counted as three numbers — inline styles, then IDs, then classes, attribute selectors and pseudo-classes, then elements and pseudo-elements. So
#nav .item(1,1,0) beats.header .nav .item(0,3,0), because any number of classes cannot outweigh a single ID. - Source order. If specificity ties, the rule that appears later wins.
Things that catch people out:
- The universal selector
*and combinators such as>and+add nothing. :not()itself adds nothing, but its argument counts.:where()always has zero specificity, which makes it excellent for defaults that should be easy to override.
Note: The right conclusion is that you should keep specificity flat rather than win specificity battles. Escalating to !important is a symptom, and it makes the next override worse.
4. What is the difference between position relative, absolute, fixed, sticky and static?
position controls what an element is positioned against and whether it stays in the normal document flow.
static— the default. In normal flow;top,left,rightandbottomare ignored entirely.relative— stays in the flow and still occupies its original space, but is visually shifted by the offsets. Its main use is to create a positioning context for absolutely positioned children.absolute— removed from the flow, so it no longer takes up space. Positioned against the nearest ancestor that is not static; if there is none, against the initial containing block.fixed— removed from the flow and positioned against the viewport, so it does not move when the page scrolls. Used for headers, modals, and cookie banners.sticky— a hybrid. It behaves as relative until it reaches a specified offset while scrolling, then behaves as fixed within its parent's bounds.
Note: Two gotchas worth knowing. Sticky silently does nothing unless you give it a threshold such as top: 0, and it is constrained by its parent — an ancestor with overflow hidden or auto breaks it. And any ancestor with a transform, filter, or will-change becomes the containing block for fixed children, which is a genuinely confusing bug to meet in the wild.
5. What is the critical rendering path, and how do you optimise a page's first paint?
The critical rendering path is everything the browser must do between receiving HTML and painting pixels:
- Parse HTML into the DOM.
- Parse CSS into the CSSOM.
- Combine them into the render tree.
- Layout — compute the geometry of every box.
- Paint and composite the layers to the screen.
The two blockers: CSS is render-blocking, because the browser will not paint until it knows the styles. A synchronous <script> in the head is parser-blocking, because it can rewrite the document.
How to make first paint faster:
- Inline the small amount of CSS needed for above-the-fold content, and load the rest asynchronously.
- Add
deferto scripts so they download in parallel and execute after parsing, orasyncfor independent third-party scripts. - Preload the LCP image and the primary font, and use
font-display: swapso text is never invisible. - Serve modern image formats at the right size, with width and height set so nothing shifts.
- Reduce JavaScript. It is almost always the largest and most expensive resource on the page.
6. Explain the difference between server-side rendering, static generation and client-side rendering.
All three produce the same page; they differ in when the HTML is built.
- Client-side rendering (CSR) — the server sends a near-empty HTML shell and JavaScript builds the page in the browser. Cheap to host and excellent for highly interactive applications behind a login, but the first paint waits for the bundle to download and run, and crawlers see little without executing JavaScript.
- Server-side rendering (SSR) — HTML is built on the server for each request, then hydrated in the browser. Fast first contentful paint and good for SEO, at the cost of server work on every request and a gap where the page looks ready but is not yet interactive.
- Static site generation (SSG) — HTML is built once at deploy time and served from a CDN. The fastest and cheapest option by a wide margin, but the content is fixed until the next build. Incremental regeneration softens this by rebuilding individual pages on a schedule or on demand.
How to choose: content that is the same for everyone and changes rarely — marketing pages, documentation, blogs — should be static. Content that is personalised or changes constantly needs SSR. A dashboard behind a login can be CSR, because SEO is irrelevant and the shell can be cached.
7. What is the Virtual DOM, and does it make applications faster?
The Virtual DOM is a lightweight JavaScript description of what the UI should look like. When state changes, the library builds a new tree, diffs it against the previous one, and applies only the differences to the real DOM — a process called reconciliation.
The honest answer to whether it is faster is: not compared to hand-written, perfectly targeted DOM updates. Touching the DOM directly with full knowledge of what changed will always beat diffing. What the Virtual DOM actually buys you is a programming model: you describe the UI as a function of state and never write imperative update code, and the library guarantees the result is fast enough by batching updates and avoiding the naive alternative of rebuilding everything.
Worth knowing: several modern frameworks have dropped it. Svelte compiles updates at build time, and Solid uses fine-grained reactivity to update exactly the nodes that depend on a changed signal. Both are faster than diffing, which is good evidence that the Virtual DOM is a means rather than an end.
Note: The keys prop follows directly from this. Keys let the diff match elements across renders; using an array index as a key breaks that matching when the list reorders, and state ends up attached to the wrong item.
8. How do you make a website responsive, and what is the difference between a media query and a container query?
The foundations come before any query:
<meta name="viewport" content="width=device-width, initial-scale=1">— without it, mobile browsers pretend to be 980px wide.- Fluid units — percentages,
fr,rem, andclamp()for type that scales between a floor and a ceiling. max-width: 100%on images and embedded media.- Layout primitives that reflow on their own, such as
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))andflex-wrap.
Media queries respond to the viewport. They are the right tool for page-level decisions — how many columns the page skeleton has, whether the navigation collapses.
Container queries respond to the size of an element's own container:
.card-wrap { container-type: inline-size; }
@container (min-width: 400px) {
.card { display: grid; grid-template-columns: 120px 1fr; }
}This is what media queries could never do: the same card component now lays out correctly whether it sits in a wide main column or a narrow sidebar, without knowing anything about the page. For component libraries and design systems, container queries are the correct tool and media queries are a workaround.
9. What is CORS, and how would you fix a CORS error?
Browsers enforce the same-origin policy: a page at one origin — scheme, host, and port together — cannot read a response from a different origin unless that server allows it. CORS is the mechanism a server uses to grant that permission through response headers.
Simple requests — a GET or POST with a small set of allowed headers — are sent immediately, and the browser checks Access-Control-Allow-Origin on the response before letting JavaScript read it. Anything else triggers a preflight: the browser first sends an OPTIONS request asking whether the real request is permitted.
How to fix it, in order:
- The fix belongs on the server. Nothing you write in the browser can grant your own permission — that is the entire point.
- Set
Access-Control-Allow-Originto the specific origin, andAccess-Control-Allow-MethodsandAccess-Control-Allow-Headersto cover what the client actually sends. - Make sure the server answers
OPTIONSwith a 2xx and no body. A preflight hitting an auth middleware that returns 401 is an extremely common cause. - If you need cookies, set
Access-Control-Allow-Credentials: true— and note that you may not use*for the origin in that case.
Note: A proxy in your dev server is a legitimate local workaround, but say plainly that it is not a production fix. And a CORS error never means the request was blocked from being sent — it means the response was blocked from being read, so the server has already done the work.
10. What is the difference between localStorage, sessionStorage and cookies?
Three client-side storage mechanisms with different lifetimes, sizes, and — most importantly — different exposure.
- localStorage — around 5-10MB, persists until explicitly cleared, shared across every tab on the origin, and never sent to the server. Synchronous string-only API.
- sessionStorage — the same API and roughly the same size, but scoped to a single tab and cleared when that tab closes. Two tabs on the same site have entirely separate sessionStorage.
- Cookies — only about 4KB, and sent with every HTTP request to the origin, which is both their purpose and their cost. They have an explicit expiry and, crucially, security attributes the other two lack.
The point interviewers are usually driving at: where to keep an authentication token. localStorage is readable by any JavaScript on the page, so a single XSS vulnerability hands over every user's session. A cookie marked httpOnly cannot be read by JavaScript at all; adding secure restricts it to HTTPS and sameSite defends against CSRF. That is why httpOnly cookies are the recommended place for session tokens and localStorage is not.
Note: For structured or larger data, IndexedDB is the right answer — asynchronous, far larger, and it stores real objects rather than strings.





