Skip to content

Frontend Development Interview Questions for 5 Years Experience

40 questions with answers for candidates with 5 years of experience — 2 easy, 28 medium, 10 hard.

What interviewers expect: 5 years experience

At five years, the focus shifts to architecture and leadership: rendering strategies, monorepos, design systems, performance at scale, observability and testing strategy. You may design a complete frontend for a large product and discuss migration plans. Behavioural rounds explore leading decisions, handling disagreements and improving team velocity.

This set is weighted towards Frontend Architecture (8), TypeScript (7), Build Tools and Tooling (7), CSS Architecture and Accessibility (6).

Architecture decisions Design systems Performance at scale Technical leadership
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

A third-party library you need has no TypeScript types. What do you do?

Save to ▾
Show answer
First I check if types exist on DefinitelyTyped by installing @types/library-name. If not, I write a minimal declaration file, for example declare module 'old-chart-lib' with only the functions I use, in a types folder included by tsconfig. This is better than a blanket any import. If the library is important to the community, contributing those types upstream helps everyone.
02

Move a heavy calculation into a Web Worker.

Save to ▾
Show answer
The worker runs on its own thread and talks through messages: // worker.js self.onmessage = (e) => { const total = e.data.reduce((sum, row) => sum + row.amount, 0); self.postMessage(total); }; // main.js const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }); worker.postMessage(bigArrayOfRows); worker.onmessage = (e) => showTotal(e.data); The UI stays responsive. Data is copied between threads, so for huge binary data use transferable objects like ArrayBuffer.
03

npm audit reports a critical vulnerability in a dependency. What do you do?

Save to ▾
Show answer
I would check whether the vulnerable package is actually used in the production bundle or only in build tooling, and whether the vulnerable code path is reachable. Then I would update to a patched version, using npm update or overrides for transitive dependencies. If no fix exists, I would look for an alternative package or mitigate. Automated tools like Dependabot or Renovate keep dependencies current.
04

Run linting and formatting automatically before each commit.

Save to ▾
Show answer
Use Husky with lint-staged: npm i -D husky lint-staged npx husky init echo 'npx lint-staged' > .husky/pre-commit // package.json "lint-staged": { "*.{ts,tsx,js}": ["eslint --fix", "prettier --write"], "*.{css,md,json}": "prettier --write" } Only staged files are checked, so commits stay fast. CI should still run the full lint because hooks can be skipped locally.
05

How do you estimate the time needed for a frontend feature?

Save to ▾
Show answer
I break the feature into small parts: components, states such as loading, empty and error, API integration, responsive behaviour, accessibility and tests. I estimate each part, add time for reviews and unknowns, and flag dependencies like pending designs or APIs. For example, a filter panel I estimated at five days was delivered in five because I had listed edge cases upfront instead of discovering them late.
06

Animate a modal smoothly when it opens without causing layout work.

Save to ▾
Show answer
Animate only transform and opacity, which run on the compositor: .modal { opacity: 0; transform: translateY(16px) scale(0.98); transition: opacity 200ms ease, transform 200ms ease; } .modal.is-open { opacity: 1; transform: none; } @media (prefers-reduced-motion: reduce) { .modal { transition: opacity 100ms linear; transform: none; } } Animating top, height or margin instead would trigger layout on every frame and cause jank on low-end phones.
07

The build works on your machine but fails in CI. How do you troubleshoot?

Save to ▾
Show answer
I would compare environments first: Node version, package manager version, and whether CI uses npm ci with the committed lockfile while I used npm install. Other common causes are case-sensitive file paths on Linux, missing environment variables, and files ignored by Git. Pinning Node with an .nvmrc or engines field and reproducing the CI command locally in a clean clone or container usually reveals the difference.
08

Create a typed fetch helper with generics.

Save to ▾
Show answer
Let callers specify the expected response type: async function getJson<T>(url: string): Promise<T> { const res = await fetch(url); if (!res.ok) throw new Error(`Request failed: ${res.status}`); return res.json() as Promise<T>; } interface Course { id: number; title: string; } const courses = await getJson<Course[]>('/api/courses'); courses[0].title.toUpperCase(); The cast trusts the server, so for untrusted data validate with a schema library like Zod before returning.
09

Test a DOM form with Testing Library and user-event.

Save to ▾
Show answer
Simulate real typing and clicks: import { screen } from '@testing-library/dom'; import userEvent from '@testing-library/user-event'; import { mountSignupForm } from './signup'; it('shows an error for an invalid mobile number', async () => { document.body.innerHTML = '<div id=app></div>'; mountSignupForm(document.getElementById('app')); const user = userEvent.setup(); await user.type(screen.getByLabelText('Mobile'), '12345'); await user.click(screen.getByRole('button', { name: 'Sign up' })); expect(screen.getByRole('alert')).toHaveTextContent('Enter a valid mobile number'); }); toHaveTextContent comes from @testing-library/jest-dom.
10

Implement a throttle function.

Save to ▾
Show answer
Throttle allows a function to run at most once in a given interval: function throttle(fn, wait) { let last = 0; return function (...args) { const now = Date.now(); if (now - last >= wait) { last = now; fn.apply(this, args); } }; } window.addEventListener('scroll', throttle(updateProgressBar, 100)); Unlike debounce, which waits for calls to stop, throttle fires regularly during continuous events such as scrolling or dragging.
11

How do you secure a modern frontend application beyond what the framework provides?

Save to ▾
Show answer
Frameworks escape output by default, but risks remain: dangerouslySetInnerHTML or v-html with unsanitised data, javascript: URLs in links, and vulnerable npm packages. I would sanitise any HTML with DOMPurify, enforce a strict Content Security Policy with nonces, use Subresource Integrity for CDN scripts, keep tokens in HttpOnly cookies, run dependency audits in CI and never place secrets in frontend environment variables.
12

What are declaration files (.d.ts) and when do you write them?

Save to ▾
Show answer
Declaration files describe the types of JavaScript code without containing implementation. Libraries ship them so TypeScript users get type checking, and many come from DefinitelyTyped as @types packages. You write your own when using an untyped library, declaring global variables injected by scripts, or typing non-code imports like declare module '*.svg'. Generating them with declaration: true is standard when publishing a TypeScript library.
13

The bundle analyser shows two copies of the same library. How do you fix it?

Save to ▾
Show answer
Duplicates happen when different packages depend on incompatible versions, so the package manager installs both. I would run npm ls library-name or pnpm why to see who requires which version, then update dependents to a common version, use overrides or resolutions to force one compatible version, or configure resolve.dedupe in Vite. Duplicate framework copies are especially harmful because they can break shared state and context.
14

Tell me about a time you convinced your team to adopt a new tool like TypeScript.

Save to ▾
Show answer
Situation: our JavaScript app kept breaking after API changes. Task: I believed TypeScript would help but the team worried about slowing down. Action: I converted one small module as a pilot, showed three bugs it caught, and proposed gradual adoption with no deadline pressure. Result: the team agreed, we migrated over two quarters, and production errors related to wrong data shapes dropped significantly.
15

Explain any, unknown and never.

Save to ▾
Show answer
any turns off type checking completely, so errors slip through silently. unknown also accepts any value, but you must narrow it with typeof, instanceof or a type guard before using it, which makes it the safe choice for API responses and catch errors. never represents values that never occur, such as a function that always throws, or the leftover case in an exhaustive switch over a union.
16

Design the frontend for a search box with autocomplete suggestions.

Save to ▾
Show answer
I would debounce input by about 200 milliseconds, cancel outdated requests with AbortController and cache recent queries in a Map. Suggestions render in an accessible combobox pattern with aria-expanded, aria-activedescendant and arrow key navigation. I would highlight matched text safely, show loading and empty states, and limit results to around eight. Measuring latency and prefetching popular terms keep it feeling instant on slow networks.
17

What is the same-origin policy and how does postMessage work around it?

Save to ▾
Show answer
An origin is the combination of scheme, host and port. The same-origin policy stops scripts from reading data or DOM of another origin, such as an embedded iframe from a payment provider. window.postMessage lets two windows or iframes exchange messages safely across origins. The receiver must check event.origin against an allow-list, and the sender should specify the exact target origin instead of '*'.
18

In a single-page app, the back button takes users out of the site instead of to the previous filter. Why?

Save to ▾
Show answer
The app changes the view without adding history entries, so the browser has nothing to go back to within the site. Each meaningful navigation, like changing a category or opening a product, should call pushState or the router's navigate function, while minor changes such as typing in search can use replaceState. The app must also listen to popstate and restore the view and scroll position from the URL.
19

The frontend test suite takes 20 minutes in CI. How do you make it faster?

Save to ▾
Show answer
I would measure which tests are slowest. Options include running tests in parallel and sharding across CI machines, moving logic checks from end-to-end tests into faster integration tests, reusing logged-in state instead of logging in every time, and running only tests affected by changed files on pull requests with the full suite nightly. Replacing real network calls with MSW and caching dependencies also helps.
20

Users report errors you cannot reproduce. How do you set up frontend observability?

Save to ▾
Show answer
I would add an error monitoring tool like Sentry with uploaded source maps, release tags and user context such as browser and route, without personal data. Real user monitoring tracks Web Vitals and slow API calls. Breadcrumbs of clicks and network requests before an error help reproduce issues, and session replay with privacy masking shows what users saw. Alerts on error spikes after a release enable quick rollbacks.
21

Build an API client that refreshes an expired access token once and retries.

Save to ▾
Show answer
Share one refresh promise so parallel requests do not refresh repeatedly: let refreshing = null; async function api(path, options = {}) { let res = await fetch(path, { ...options, credentials: 'include' }); if (res.status !== 401) return res; refreshing ??= fetch('/auth/refresh', { method: 'POST', credentials: 'include' }) .finally(() => (refreshing = null)); const refreshed = await refreshing; if (!refreshed.ok) { location.assign('/login'); throw new Error('Session expired'); } return fetch(path, { ...options, credentials: 'include' }); } Cookies carry the tokens, so JavaScript never reads them.
22

Where should a frontend store authentication tokens?

Save to ▾
Show answer
Tokens in localStorage are readable by any script, so a single XSS exposes them. The safer pattern is an HttpOnly, Secure, SameSite cookie set by the server, invisible to JavaScript, paired with CSRF protection. When an access token must be held in JavaScript, keep it short-lived in memory and refresh it using an HttpOnly refresh cookie. A backend-for-frontend layer can keep tokens entirely off the browser.
23

How do HTTP caching headers work for frontend assets?

Save to ▾
Show answer
Cache-Control controls caching. Files with content hashes in their names, like app.3f9a2c.js, can use max-age=31536000, immutable because a new build produces a new filename. HTML should use no-cache, which forces revalidation so users get new asset references after a deploy. ETag and Last-Modified allow cheap revalidation with 304 Not Modified responses. Wrong headers cause either stale apps or needless re-downloads.
24

Field data shows poor INP on the product page's Add to Cart button. How do you investigate?

Save to ▾
Show answer
I would reproduce with CPU throttling in the Performance panel and record the click to see what runs. Typical findings are a heavy handler, analytics calls running synchronously, or a large re-render of the whole page. Fixes include updating the button state immediately, deferring non-urgent work like tracking with scheduler.yield or requestIdleCallback, and narrowing which components re-render. Attribution data from web-vitals helps find the exact element and script.
25

Which rendering strategy would you choose for a large e-commerce site?

Save to ▾
Show answer
I would mix strategies by page type. Category and product pages need SEO and fast LCP, so I would use static generation with incremental revalidation or server rendering with CDN caching. Cart, checkout and account pages are personalised, so server rendering without shared caching or client rendering works. Prices and stock can be fetched fresh on the client. Frameworks like Next.js, Nuxt and Astro support this per-route choice.
26

How do environment variables work in frontend builds, and what is the risk?

Save to ▾
Show answer
Build tools replace variables at build time, for example Vite exposes only variables prefixed with VITE_ through import.meta.env. The values become plain text inside the JavaScript bundle that anyone can read. So frontend environment variables must never hold secrets like private API keys or database passwords; those belong on a server or backend proxy. Use them only for public configuration such as API base URLs.
27

What does the :has() selector allow you to do?

Save to ▾
Show answer
:has() is often called the parent selector because it styles an element based on what it contains or what follows it. For example, .card:has(img) can change padding for cards with images, and form:has(input:invalid) can highlight a form with errors. It removes many cases where JavaScript was used only to toggle classes. It is supported in all modern browsers.
28

Model API request state with a discriminated union.

Save to ▾
Show answer
A shared status field lets TypeScript narrow each case: type ApiState<T> = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; message: string }; function render(state: ApiState<string[]>) { switch (state.status) { case 'success': return state.data.join(', '); case 'error': return 'Error: ' + state.message; default: return 'Loading...'; } } Impossible states, like having both data and an error, cannot be represented.
29

Tell me about a time you received critical feedback in a code review.

Save to ▾
Show answer
Situation: a senior engineer rejected my pull request for a new dashboard because the CSS was hard to maintain. Task: I had to rework it without missing the sprint. Action: I asked for a short call to understand the concerns, refactored styles into reusable components with tokens, and added notes explaining my choices. Result: the PR was approved, and the components were later reused in two other screens.
30

What is a JavaScript bundler and why do frontend projects need one?

Save to ▾
Show answer
A bundler takes your source files, follows imports to build a dependency graph and outputs optimised files for the browser. Along the way it transpiles TypeScript and JSX, processes CSS, handles images, minifies code, removes unused exports and adds content hashes for caching. Native ES modules work in browsers, but loading hundreds of unoptimised files is slow, so production builds still use bundlers like Vite, webpack or Rspack.
31

How can you communicate between multiple tabs of the same site?

Save to ▾
Show answer
BroadcastChannel lets same-origin tabs, windows and workers publish and subscribe to messages on a named channel, which suits syncing logout, theme or cart updates. The storage event fires in other tabs when localStorage changes, which is a simple fallback. SharedWorker can hold shared state for all tabs. Each option works only within the same origin, so cross-domain sync needs a server or postMessage.
32

Your app must now support an Arabic right-to-left version. What changes are needed in CSS?

Save to ▾
Show answer
I would set dir='rtl' on the html element for that locale and replace physical properties like margin-left, padding-right and left with logical ones such as margin-inline-start and inset-inline-start. Flexbox and Grid follow direction automatically. Directional icons like arrows need flipping with a transform in RTL, while logos and media do not. I would test layouts with real Arabic text, which has different line heights.
33

What are the pros and cons of snapshot testing?

Save to ▾
Show answer
Snapshot tests save a component's rendered output and fail when it changes, which quickly detects unintended changes with little effort. However, large snapshots are hard to review, so developers often update them blindly, and they break on harmless markup changes. They work best for small, stable outputs like serialised config or error messages. For UI behaviour, explicit assertions on visible text and roles are more meaningful.
34

Explain the utility types Partial, Pick, Omit and Record.

Save to ▾
Show answer
Partial<T> makes all properties optional, useful for update payloads. Pick<T, 'id' | 'name'> creates a type with only selected keys, and Omit<T, 'password'> removes keys, handy for public user objects. Record<K, V> builds an object type with keys K and values V, such as Record<'en' | 'hi', string> for translations. Others include Required, Readonly, ReturnType and Awaited.
35

Write a card that changes layout based on its container width.

Save to ▾
Show answer
Use a container query so the card adapts wherever it is placed: .card-wrapper { container-type: inline-size; } .card { display: grid; gap: 1rem; } @container (min-width: 420px) { .card { grid-template-columns: 160px 1fr; align-items: center; } } In a narrow sidebar the image stacks above text; in a wide main column it sits beside the text, with no viewport media queries.
36

Lighthouse shows a score of 95, but Search Console reports failing Core Web Vitals. Why?

Save to ▾
Show answer
Lighthouse is a single lab run on a simulated device, while Search Console uses field data from real Chrome users at the 75th percentile over 28 days. Real users may have slower phones, interact with the page, which affects INP and CLS later in the session, or see different content like ads and personalised blocks. I would add real user monitoring with attribution to find which pages and elements fail.
37

Compare CSS Modules, CSS-in-JS and utility-first CSS.

Save to ▾
Show answer
CSS Modules scope class names per file at build time, giving plain CSS with no runtime cost. CSS-in-JS libraries write styles in JavaScript with dynamic props, but runtime versions add bundle size and slow server rendering, so zero-runtime options are now preferred. Utility-first frameworks like Tailwind compose small classes in markup, producing small CSS and consistent spacing. The choice depends on team preference and rendering strategy.
38

Your app needs to support Hindi and English. How do you design internationalisation?

Save to ▾
Show answer
I would move all UI text into translation files keyed by message ids, use an i18n library that supports plurals and interpolation, and format numbers, dates and currency with Intl APIs for each locale, like en-IN and hi-IN. Layouts must handle longer text and Devanagari fonts with proper line height. The locale goes in the URL, such as /hi/, with hreflang tags for SEO and lang on html.
39

When a user logs out in one tab, other open tabs stay logged in. How do you fix it?

Save to ▾
Show answer
I would broadcast a logout message using BroadcastChannel, or the storage event as a fallback, so every tab clears its in-memory state and redirects to the login page. The server must also invalidate the session or refresh token, so stale tabs fail their next API call with a 401, which the app handles by logging out. Both client sync and server invalidation are needed.
40

Production builds take eight minutes on an old webpack setup. How would you speed them up?

Save to ▾
Show answer
I would profile the build to find slow loaders and plugins. Quick wins include replacing Babel and Terser with SWC or esbuild, enabling persistent filesystem caching, and removing unused plugins. For a bigger gain, I would evaluate migrating to Vite or Rspack, which keeps webpack-compatible config. In CI, caching dependencies and build outputs and running only affected packages in a monorepo cut time further.

Frequently asked questions

What is asked in Frontend Development interviews for 5 years of experience?

At five years, the focus shifts to architecture and leadership: rendering strategies, monorepos, design systems, performance at scale, observability and testing strategy. You may design a complete frontend for a large product and discuss migration plans. Behavioural rounds explore leading decisions, handling disagreements and improving team velocity.

How many questions should I practise before a Frontend Development interview?

Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.

Which topics matter most for 5 years of experience?

Focus first on Architecture decisions, Design systems, Performance at scale, Technical leadership.