React JS Interview Questions for 10 Years Experience
40 questions with answers for candidates with 10+ years of experience — 10 medium, 30 hard.
What interviewers expect: 10 years experience
At ten years and above, roles are frontend architect, principal engineer or engineering manager. Interviews focus on organisation-wide technical strategy, platform decisions, hiring and growing teams, aligning frontend architecture with business goals and long-term maintainability. You should show a track record of major initiatives, clear written communication and the ability to evaluate new technologies objectively.
This set is weighted towards Performance Optimization (8), Next.js and Advanced React (7), React Hooks (6), State Management (5).
Technical strategy and roadmaps Platform and tooling decisions Team building and hiring Stakeholder communication
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 user types into an input inside a list row, then sorts the list, and the typed text appears in a different row. How would you fix this?
The rows are probably keyed by index or by a value that changes after sorting, so React keeps the DOM input in the same position while the data moves. Give each row a stable key from the data, such as the record id. Better still, make the input controlled and store its value in state keyed by id, so the text always follows the correct record.
02
A new mid-sized dashboard project is starting. How would you decide between Context, Redux Toolkit and Zustand?
First separate server state, which I would handle with TanStack Query or RTK Query. For remaining client state, local useState covers most needs. Context suits rarely changing values like theme or auth. If there is complex shared state with many actions, strict patterns, large teams and need for DevTools time travel, Redux Toolkit fits. For simpler shared state with less boilerplate, Zustand is a good choice.
03
A teammate writes setCount(count + 1) three times in a click handler and is surprised that the count increases by only one. Explain why.
Inside one render, count is a constant snapshot, say 0. Each call schedules setCount(0 + 1), so all three set the same value 1, and React batches them into one render. State does not change immediately after calling the setter. To increment three times, use the updater form setCount(c => c + 1), which queues functions that each receive the latest pending value.
04
When would you use a Web Worker in a React application?
Use a Web Worker for CPU-heavy work that would block the main thread, such as parsing a large CSV, image processing, complex calculations or searching big datasets in the browser. The worker runs in a separate thread and communicates through postMessage, so the UI stays responsive. Libraries like Comlink simplify the messaging. Workers cannot touch the DOM, so results are sent back and set in state.
05
Without using memo, what structural techniques reduce unnecessary re-renders?
Move state down into the smallest component that needs it, so changes do not re-render siblings. Lift content up by passing expensive parts as children: a component that changes state does not re-render JSX it receives as children, because that JSX was created by the parent. Split large components, and avoid putting fast-changing values in widely used context. These often fix performance more cleanly than memoisation.
useEffectEvent, stable since React 19.2, creates a function that always sees the latest props and state but is not reactive, so it does not need to be in the effect's dependency array. Example: a chat effect reconnects when roomId changes, and an onConnected event shows a notification using the current theme. Wrapping the notification in useEffectEvent stops theme changes from reconnecting the socket.
React 19 added functions in react-dom such as preload, preinit, prefetchDNS and preconnect. They tell the browser to load fonts, stylesheets, scripts or connect to API origins early, and React places the hints in the document head, including during server rendering. React 19 also supports rendering <title>, <meta> and <link> tags inside components, hoisting them to the head automatically.
08
How would you implement an optimistic update with TanStack Query and roll back on failure?
In useMutation, use onMutate to cancel ongoing queries, save the previous cache data and update the cache with the expected result. In onError, restore the saved snapshot and show an error toast. In onSettled, invalidate the query so the cache syncs with the server. This makes actions like toggling a favourite feel instant while keeping data correct if the server rejects the change.
React Hook Form registers inputs as uncontrolled by default and reads values through refs, so typing does not re-render the whole form. This makes large forms fast. It provides validation, error messages, field arrays for dynamic rows, and integrates with Zod or Yup through resolvers. It has a small bundle and works with UI libraries through a Controller component for custom controlled inputs.
10
How do Suspense and streaming server rendering improve performance?
With streaming SSR, the server sends HTML in chunks: the page shell is sent immediately and slower sections wrapped in Suspense are streamed later as their data resolves, replacing the fallback. Users see content sooner and hydration of parts can happen selectively, prioritising what the user interacts with. Frameworks like Next.js App Router use this with server components and loading.js boundaries.
11
How do you type props for a component in TypeScript?
Situation: our app ran on an unmaintained Create React App setup with slow builds. Task: move to Vite without stopping feature work. Action: I wrote a migration plan, fixed incompatible imports and env variables in a branch, ran both builds in CI for a week, and trained the team on differences. Result: we switched over a weekend, local start time went from over a minute to a few seconds, and nothing broke for users.
14
After a release, the Lighthouse performance score of the home page dropped from 90 to 60. How do you find and fix the cause?
Compare the two builds instead of guessing. Run Lighthouse and WebPageTest on both versions, check which metric dropped (LCP, TBT or CLS), and diff bundle analyser reports to spot new heavy dependencies. Review the release changes: a new third-party script, an unoptimised hero image or a component moved from server to client are common causes. Fix it, then add a performance budget check in CI to prevent repeats.
It is code that runs before a request completes, at the edge of the app, and can rewrite, redirect, set headers or cookies based on the request. In Next.js 16 the middleware.ts file convention was renamed to proxy.ts, with the same idea. Typical uses are redirecting unauthenticated users, locale detection, A/B testing and bot filtering. Keep it light, and still check authorisation in data access code.
16
How do you expose a custom method like focus() from a child to a parent in React 19?
In React 19, function components receive ref as a normal prop, so forwardRef is no longer needed. Use useImperativeHandle to expose only selected methods.
function SearchInput({ ref }) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return <input ref={inputRef} />;
}
// parent: const r = useRef(); <SearchInput ref={r} />; r.current.focus();
17
How would you update deeply nested state cleanly? Show with Immer.
Manual spreading of nested objects is error prone. Immer lets you write mutating code on a draft and produces an immutable copy. Redux Toolkit uses it internally.
import { useImmer } from 'use-immer';
const [form, updateForm] = useImmer({ address: { city: '', pin: '' } });
updateForm(draft => {
draft.address.city = 'Noida';
});
Also consider flattening state to avoid deep nesting.
18
What is state colocation and why does it matter for performance and maintainability?
State colocation means keeping state as close as possible to the components that use it. Global or high-level state causes large parts of the tree to re-render on every change and makes ownership unclear. If only a dropdown needs isOpen, keep it in the dropdown. Lift state only when siblings must share it. Good colocation often removes the need for memoisation entirely.
useSyncExternalStore subscribes a component to an external, non-React data source, like a browser API or a custom store, in a way that is safe with concurrent rendering. You give it a subscribe function and a getSnapshot function, and optionally getServerSnapshot. It prevents tearing, where parts of the UI show different values of the same store. Libraries like Redux and Zustand use it internally.
20
A 60-field insurance form becomes laggy while typing because every keystroke re-renders the whole form. How would you fix it?
The state for all fields probably lives in one top-level useState, so each change re-renders every input. Options: switch to React Hook Form so inputs are uncontrolled and only error messages re-render, split the form into memoised sections that own their own state, and debounce expensive validation or derived calculations. Profile first with React DevTools to confirm which components take the most time.
21
How do you handle a tight deadline when you know the code is not perfect?
Situation: a festive sale banner feature had to go live in two days. Task: ship safely without gold-plating. Action: I listed must-haves with the product owner, wrote tests for the critical checkout path, kept a simple implementation and logged clear tech-debt tickets for refactoring. Result: we launched on time with no incidents, and I cleaned up the code in the next sprint as planned.
Both are concurrent features that keep the UI responsive. useTransition gives you startTransition to mark a state update as non-urgent, plus an isPending flag, so typing stays smooth while a heavy list re-renders. useDeferredValue takes a value and returns a lagging copy that React updates in the background. Use useTransition when you control the setter, and useDeferredValue when you only receive the value as a prop.
23
What is the main performance pitfall of React Context and how do you avoid it?
Every component that reads a context re-renders when the provider value changes. If you pass a new object like value={{ user, setUser }} on each render, all consumers re-render even if nothing changed. Memoise the value with useMemo, split one big context into smaller ones (for example state and dispatch separately), and keep fast-changing data in a dedicated store like Zustand that supports selectors.
24
What are micro-frontends and when are they worth it?
Micro-frontends split a large web app into independently built and deployed parts owned by different teams, for example search, cart and account, combined at runtime using Module Federation, iframes or route-level composition. They help very large organisations scale teams and deployments. The costs are duplicated dependencies, harder shared state, inconsistent UX and complex tooling, so they are rarely worth it for small or medium teams.
25
After navigating between pages for a while, the app's memory keeps growing and it slows down. How do you investigate a memory leak?
Take heap snapshots in Chrome DevTools Memory panel before and after repeated navigation and compare retained objects, especially detached DOM nodes. Common React causes are effects without cleanup: intervals, event listeners on window, WebSocket subscriptions and third-party chart instances not destroyed. Also check global caches growing without limits. Add cleanups in useEffect returns and verify the heap stabilises.
26
A test in CI fails randomly about one time in ten. How do you handle a flaky test?
First reproduce it by running the test repeatedly and in random order. Common causes are shared state between tests, missing awaits, real timers or dates, network calls not mocked and tests depending on execution order. Fix the root cause: reset mocks and stores in afterEach, use findBy or waitFor instead of fixed sleeps, freeze time with fake timers. Quarantine it with a ticket only temporarily, never ignore it.
27
In a Next.js app you see a hydration mismatch error. How do you debug it?
It means the HTML rendered on the server differs from the first client render. Common causes: using Date.now(), Math.random() or locale formatting in render, reading window or localStorage during render, browser extensions changing the DOM, and invalid HTML nesting like a div inside a p. Read the diff in the error, render client-only values inside useEffect, use useId for ids and fix invalid markup.
28
How would you build and maintain a shared component library for several React apps?
Start with design tokens for colours, spacing and typography, and build accessible primitives, often on top of headless libraries like Radix UI or React Aria. Document components in Storybook with usage examples, add visual regression and accessibility tests, and publish versioned packages from a monorepo with semantic versioning and changelogs. Keep APIs small and composable, and collect feedback from consuming teams before breaking changes.
useOptimistic shows a temporary optimistic state while an async action is running. For example, when a user likes a post, you immediately show the like count increased, call the server, and React automatically reverts to the real state when the action finishes or fails. It makes interfaces feel instant for actions that usually succeed, like sending a chat message or adding to a wishlist.
30
Explain the testing pyramid and the testing trophy. Which applies to frontend?
The pyramid says write many unit tests, fewer integration tests and very few end-to-end tests, because lower tests are fast and cheap. The testing trophy, popular for frontend, adds static analysis at the base and puts the most effort in integration tests, since they give the best confidence per cost. For React, I rely on TypeScript and ESLint, many component tests, some unit tests for logic and a few E2E tests.
31
An e-commerce site has one lakh product pages whose prices change a few times a day. Which rendering strategy would you choose in Next.js?
Building all pages statically at deploy time would be slow, and full SSR on every request adds server load. I would pre-render the most popular products at build time, generate the rest on first request and cache them, and revalidate on demand with revalidateTag when prices change in the admin. Stock and price shown in the cart would be fetched fresh. This gives static speed with correct data.
RTK Query is the data fetching and caching tool included in Redux Toolkit. You define an API with createApi and endpoints, and it auto-generates hooks like useGetProductsQuery and useAddProductMutation. It handles loading states, caching, deduplicating requests, refetching and cache invalidation through tags. It removes most hand-written thunks and reducers for server data in Redux-based apps.
33
A search box fetches results on every keystroke and sometimes shows results for an older query. How do you fix this race condition?
Responses can arrive out of order, so a slow old request overwrites newer results. Cancel or ignore stale requests in the effect cleanup.
useEffect(() => {
const ctrl = new AbortController();
fetch(`/api/search?q=${q}`, { signal: ctrl.signal })
.then(r => r.json())
.then(setResults)
.catch(() => {});
return () => ctrl.abort();
}, [q]);
Also debounce the input, or use TanStack Query which handles this for you.
Server Components run only on the server or at build time. They can be async, read databases or files directly and send rendered output to the client without shipping their JavaScript, which reduces bundle size. They cannot use state, effects or browser APIs. Client Components, marked with 'use client', handle interactivity. Server Components can render Client Components and pass them serialisable props.
35
How do you track errors that happen in production React apps?
Use an error monitoring tool such as Sentry, which captures exceptions with stack traces, browser and user context. Upload source maps during the build so minified stack traces map back to your source, but do not serve them publicly. Wrap major sections in error boundaries that report errors and show friendly fallbacks. Add release tags so you can see which deployment introduced a bug.
Fiber is the reconciliation engine introduced in React 16. It represents each component as a unit of work (a fiber node) so rendering can be split into chunks, paused, prioritised and resumed. This makes concurrent features possible: urgent updates like typing can interrupt less urgent renders. Rendering happens in an interruptible render phase, then changes are applied in a synchronous commit phase.
37
In development your API call inside useEffect fires twice, but only once in production. Why does this happen, and what does StrictMode do?
StrictMode is a development-only wrapper that helps find bugs. It renders components twice to catch impure rendering, and mounts, unmounts and remounts components to check that effects clean up properly. This is why a fetch in useEffect may appear to fire twice locally. It does not happen in production. The fix is to write proper cleanup, not to remove StrictMode.
38
On a profile edit page, when you switch from one user to another the form still shows the old user's typed values. How can you reset it cleanly?
React preserves state when the same component stays at the same position in the tree. Give the form a key tied to the user, like <ProfileForm key={user.id} user={user} />. When the key changes, React unmounts the old form and mounts a fresh one with new initial state. This is cleaner than writing an effect that manually resets every field when the user prop changes.
39
What are memoised selectors and why use createSelector?
A selector reads or derives data from the store. If a selector returns a new array each time, such as state.todos.filter(t => t.done), useSelector sees a new reference and re-renders on every store update. createSelector from Reselect, included in Redux Toolkit, caches the result and recomputes only when its input selectors return changed values, avoiding wasted renders and repeated expensive calculations.
40
A teammate wraps every function in useCallback and every value in useMemo for performance. When should you not memoise, and what feedback would you give?
Memoisation has costs: extra memory, comparisons on every render and more complex code. Skip it for cheap calculations, components that rarely re-render, and props that change every render anyway. Memoising everything by default also hides design problems like state placed too high. Use the profiler to find real bottlenecks first. With the React Compiler, manual memoisation is mostly needed only as an escape hatch.
Frequently asked questions
What is asked in React JS interviews for 10+ years of experience?
At ten years and above, roles are frontend architect, principal engineer or engineering manager. Interviews focus on organisation-wide technical strategy, platform decisions, hiring and growing teams, aligning frontend architecture with business goals and long-term maintainability. You should show a track record of major initiatives, clear written communication and the ability to evaluate new technologies objectively.
How many questions should I practise before a React JS 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 10+ years of experience?
Focus first on Technical strategy and roadmaps, Platform and tooling decisions, Team building and hiring, Stakeholder communication.