Skip to content

React JS Interview Questions for 5 Years Experience

40 questions with answers for candidates with 5 years of experience — 29 medium, 11 hard.

What interviewers expect: 5 years experience

At five years, interviews focus on architecture and leadership: designing component libraries, choosing state and data strategies, Server Components, caching, security and accessibility. Frontend system design rounds ask you to design a large feature end to end. You should explain trade-offs clearly, show how you mentored others and give examples of measurable improvements you drove.

This set is weighted towards State Management (10), Performance Optimization (7), Testing and Debugging (5), React Fundamentals (5).

Frontend system design Server Components and caching Design systems Mentoring and ownership
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

Your team has hundreds of snapshot tests that developers update blindly whenever they fail. What are the drawbacks of snapshot tests and what would you suggest?

Save to ▾
Show answer
A snapshot test saves the rendered output to a file and fails when future output differs. It is quick to write and catches unexpected changes. Drawbacks: large snapshots are hard to review, developers often blindly update them, and they fail on harmless markup changes while saying nothing about behaviour. Use small, focused inline snapshots for stable output, and prefer behaviour assertions for most component tests.
02

What is atomic state management, as in Jotai or Recoil-style libraries?

Save to ▾
Show answer
Atomic state splits state into small independent units called atoms. Components subscribe to only the atoms they use, and derived atoms compute values from others, similar to a spreadsheet. This gives fine-grained re-renders without selectors or a big central store. Jotai is the popular option today; Recoil is no longer maintained. It suits apps with many independent pieces of interactive state.
03

A developer stores fullName in state and updates it in a useEffect whenever firstName or lastName changes. What would you suggest?

Save to ▾
Show answer
This is redundant derived state. It causes an extra render and risks the values going out of sync. Compute it during render instead: const fullName = firstName + ' ' + lastName. If the calculation is expensive, wrap it in useMemo. As a rule, if a value can be calculated from existing props or state, do not store it in state and do not sync it with an effect.
04

How would you implement an optimistic update with TanStack Query and roll back on failure?

Save to ▾
Show answer
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.
05

Should you keep related values in one state object or several useState calls?

Save to ▾
Show answer
Group values that always change together, like x and y coordinates, into one object. Keep unrelated values in separate useState calls, which makes updates simpler and avoids accidentally overwriting fields. Avoid contradictory or duplicated state, such as isLoading and isError both being true. When many values change together through clear events, useReducer is often a cleaner choice than a big object.
06

Your team uses the array index as key in a todo list and users report that checkboxes get ticked on the wrong item after deleting a row. What is happening?

Save to ▾
Show answer
When an item is deleted, every later item shifts to a new index, so React matches old state and DOM nodes to the wrong items. Uncontrolled inputs and component state stay attached to the index, not the data. The fix is to use a stable unique id from the data as the key. Index keys are only safe for static lists that never reorder, filter or delete.
07

How do you analyse and reduce JavaScript bundle size?

Save to ▾
Show answer
Generate a visual report with rollup-plugin-visualizer in Vite or @next/bundle-analyzer in Next.js. Look for large dependencies, duplicate packages and code that loads on every page. Fixes include importing only what you need, replacing heavy libraries such as moment with date-fns or dayjs, lazy loading routes and rarely used components, and checking that libraries support tree shaking. Track size in CI to prevent regressions.
08

What is automatic batching in React 18 and later?

Save to ▾
Show answer
Batching means React groups several state updates into one re-render. Before React 18 this only happened inside React event handlers. With createRoot, updates inside promises, setTimeout and native event handlers are also batched automatically. This reduces unnecessary renders. If you truly need a synchronous DOM update in between, you can use flushSync from react-dom, but that is rarely needed.
09

What resource preloading APIs did React 19 add?

Save to ▾
Show answer
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.
10

What is a Higher-Order Component and is it still used?

Save to ▾
Show answer
A Higher-Order Component is a function that takes a component and returns a new component with extra behaviour, for example withAuth(Profile) that redirects unauthenticated users. HOCs were common for sharing logic like routing or Redux connect. Today custom hooks are preferred because they avoid wrapper nesting, prop name collisions and confusing component trees. You will still see HOCs in older codebases.
11

In development your API call inside useEffect fires twice, but only once in production. Why does this happen, and what does StrictMode do?

Save to ▾
Show answer
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.
12

What is the difference between the App Router and the Pages Router in Next.js?

Save to ▾
Show answer
The Pages Router uses the pages directory with getServerSideProps and getStaticProps for data. The App Router, the recommended default, uses the app directory with nested layouts, React Server Components by default, streaming with loading.js, error.js boundaries and Server Functions. Data is fetched directly in async server components. Many older projects still use Pages Router, and both can coexist during migration.
13

What is Zustand and how do you use it?

Save to ▾
Show answer
Zustand is a small state library with a hook-based API and no Provider. import { create } from 'zustand'; const useCart = create(set => ({ items: [], add: item => set(s => ({ items: [...s.items, item] })), clear: () => set({ items: [] }) })); const count = useCart(s => s.items.length); Components subscribe with selectors, so they re-render only when the selected slice changes.
14

Tell me about a time you had to learn a new tool or library quickly.

Save to ▾
Show answer
Situation: our project needed to switch from Jest to Vitest with a week's notice. Task: migrate around 300 tests. Action: I read the official migration guide, migrated one folder first to discover patterns, wrote a codemod for jest.fn to vi.fn replacements and documented gotchas for the team. Result: we finished in four days, test runs became much faster, and the guide helped others with later work.
15

What are the risks of spreading all props like <Input {...props} />?

Save to ▾
Show answer
Spreading is convenient for wrapper components, but it can pass unknown attributes to DOM elements, causing console warnings, and it hides what the component actually accepts. It can also override props you set explicitly, depending on order. Prefer destructuring the props you use and spreading only the rest onto the underlying element, for example const { label, ...inputProps } = props.
16

Show how to fetch data with TanStack Query.

Save to ▾
Show answer
TanStack Query caches by query key and handles loading, errors, retries and background refetching. const queryClient = new QueryClient(); // wrap app: <QueryClientProvider client={queryClient}> function Orders({ userId }) { const { data, isPending, error } = useQuery({ queryKey: ['orders', userId], queryFn: () => fetch(`/api/users/${userId}/orders`).then(r => r.json()), staleTime: 60_000 }); if (isPending) return <Spinner />; if (error) return <p>Error</p>; return <OrderList orders={data} />; }
17

What is useId and why not use Math.random for ids?

Save to ▾
Show answer
useId generates a unique, stable id for a component instance that matches between server and client rendering. It is used for accessibility attributes, like linking a label's htmlFor to an input's id. Math.random or a global counter would produce different values on the server and the browser, causing hydration mismatches, and would change on every render. useId should not be used as list keys.
18

Five components on one page each fetch the same user profile, causing duplicate API calls. How would you fix this?

Save to ▾
Show answer
Lift the data fetching into a shared cache instead of each component fetching on its own. With TanStack Query or RTK Query, all components using the same query key share one request and one cached result, and get updates together. Alternatively, fetch once in a parent or route loader and pass data down. This also removes inconsistent loading spinners across the page.
19

Your tests pass but print 'An update to Component inside a test was not wrapped in act(...)'. What does it mean and how do you fix it?

Save to ▾
Show answer
It means state changed after the test finished checking, usually because an async operation like a fetch or timer resolved later. The test may not be checking the final UI. Fix it by awaiting the result: use findBy queries or waitFor for the expected content, await user-event calls, and use fake timers with act when testing timeouts. Do not just wrap random code in act to hide the warning.
20

How do you upload a file with a preview in React?

Save to ▾
Show answer
Use a file input, create a preview URL and send FormData. function AvatarUpload() { const [file, setFile] = useState(null); const preview = useMemo(() => file && URL.createObjectURL(file), [file]); useEffect(() => () => preview && URL.revokeObjectURL(preview), [preview]); async function upload() { const fd = new FormData(); fd.append('avatar', file); await fetch('/api/avatar', { method: 'POST', body: fd }); } return (<> <input type="file" accept="image/*" onChange={e => setFile(e.target.files[0])} /> {preview && <img src={preview} alt="preview" />} <button onClick={upload}>Upload</button> </>); }
21

Show a Server Function used to handle a form in Next.js.

Save to ▾
Show answer
A function marked 'use server' runs on the server and can be passed to a form action. // app/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; export async function addTodo(formData: FormData) { const title = String(formData.get('title')); await db.todo.create({ data: { title } }); revalidatePath('/todos'); } // app/todos/page.tsx <form action={addTodo}> <input name="title" required /> <button>Add</button> </form> Always validate input and check authorisation inside the function.
22

How do you expose a custom method like focus() from a child to a parent in React 19?

Save to ▾
Show answer
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();
23

Without using memo, what structural techniques reduce unnecessary re-renders?

Save to ▾
Show answer
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.
24

How do you mock a module in Vitest?

Save to ▾
Show answer
Use vi.mock with a factory, then control return values per test. import { vi } from 'vitest'; import { getProfile } from './api'; vi.mock('./api', () => ({ getProfile: vi.fn() })); test('shows name', async () => { getProfile.mockResolvedValue({ name: 'Ravi' }); render(<Profile />); expect(await screen.findByText('Ravi')).toBeInTheDocument(); }); In Jest, the same pattern uses jest.mock and jest.fn.
25

What is the React Compiler?

Save to ▾
Show answer
The React Compiler, released as stable version 1.0 in late 2025, is a build-time tool (Babel plugin, also integrated in Vite and Next.js setups) that automatically memoises components and values. It analyses your code and inserts caching similar to useMemo, useCallback and React.memo, so developers write plain code. It requires following the rules of React, such as pure rendering, and can be adopted gradually per directory.
26

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?

Save to ▾
Show answer
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.
27

You are building a 4-step onboarding wizard. How do you keep data when users move back and forth between steps?

Save to ▾
Show answer
Store the wizard data in a parent component, a reducer, or a small store, not inside each step, because step components unmount when hidden. Each step receives its slice and an update callback. Validate each step before moving forward, and optionally save drafts to sessionStorage or the server so a refresh does not lose progress. The current step can also live in the URL for back-button support.
28

Tell me about a time you pushed back on adding a new library to the project.

Save to ▾
Show answer
Situation: a teammate proposed adding Redux to a small admin app. Task: decide whether it was worth the added complexity. Action: I listed our actual shared state, which was just the user and a few filters, and showed a prototype using TanStack Query for API data and one small context. Result: we shipped faster with less code, and agreed to revisit if requirements grew.
29

When would you use a Web Worker in a React application?

Save to ▾
Show answer
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.
30

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?

Save to ▾
Show answer
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.
31

After adding an AuthContext at the root, profiling shows the whole app re-rendering every few seconds. What would you check?

Save to ▾
Show answer
Check what the provider value is and what updates it. Likely the value is a new object each render, or it includes fast-changing data such as a token refresh timer or a clock. Memoise the value with useMemo, move frequently changing data out of the context, and split it into separate contexts, so components reading only the user do not re-render when unrelated fields change.
32

How did you bring better testing practices to a team that had very few tests?

Save to ▾
Show answer
Situation: our React app had almost no tests and regressions reached production every release. Task: improve quality without slowing delivery. Action: I set up Vitest, React Testing Library and MSW, wrote example tests for two key screens, ran a short session for the team and added a rule that bug fixes include a test. Result: within three months, critical flows were covered and regressions dropped noticeably.
33

You wrapped a row component in React.memo, but the profiler shows it still re-renders every time. The parent passes onClick={() => remove(id)}. Why?

Save to ▾
Show answer
An inline function like onClick={() => remove(id)} creates a new function object on every render. React.memo compares props by reference, sees a different function and re-renders the child every time, so memo has no effect. Wrap the handler with useCallback and correct dependencies, or let the React Compiler handle it. If the child is not memoised, inline functions are fine and cheap.
34

What is Redux middleware?

Save to ▾
Show answer
Middleware sits between dispatching an action and the reducer, letting you intercept actions for side effects. Signature: store => next => action => {...}. Common uses are async logic (redux-thunk, included by default in Redux Toolkit), logging, analytics, error reporting and the RTK Query cache middleware. Redux Toolkit also provides listenerMiddleware for reacting to actions, a lighter alternative to redux-saga for most cases.
35

What is the render props pattern?

Save to ▾
Show answer
Render props means passing a function as a prop that the component calls to decide what to render, for example <MouseTracker render={pos => <Cursor pos={pos} />} />. It lets a component share its state or behaviour while the caller controls the UI. Like HOCs, it has mostly been replaced by custom hooks, but libraries for forms and virtualised lists still expose render-prop or children-as-function APIs.
36

How do you create a protected route that redirects users who are not logged in?

Save to ▾
Show answer
Create a wrapper that checks auth and either renders the child routes or redirects. import { Navigate, Outlet, useLocation } from 'react-router'; function RequireAuth() { const { user } = useAuth(); const location = useLocation(); if (!user) return <Navigate to="/login" replace state={{ from: location }} />; return <Outlet />; } <Route element={<RequireAuth />}> <Route path="/account" element={<Account />} /> </Route> The server must still enforce authorisation.
37

Describe a time you disagreed with a teammate about how a component should be designed.

Save to ▾
Show answer
Situation: a teammate wanted one large Table component with over twenty boolean props. Task: we needed a design that both of us and future developers could maintain. Action: I built a quick prototype using composition with Table.Header and Table.Row, and we compared both on two real screens. Result: the team chose composition, prop count dropped sharply, and we agreed to prototype before arguing in future.
38

Write a useDebounce custom hook.

Save to ▾
Show answer
Debouncing delays a value until the user stops typing, which reduces API calls. function useDebounce(value, delay = 400) { const [debounced, setDebounced] = useState(value); useEffect(() => { const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; } const q = useDebounce(searchText); // fetch when q changes
39

Create a Redux Toolkit slice for a counter.

Save to ▾
Show answer
createSlice generates action creators and the reducer. Immer lets you write mutating syntax safely. import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: state => { state.value += 1; }, addBy: (state, action) => { state.value += action.payload; } } }); export const { increment, addBy } = counterSlice.actions; export default counterSlice.reducer;
40

What are memoised selectors and why use createSelector?

Save to ▾
Show answer
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.

Frequently asked questions

What is asked in React JS interviews for 5 years of experience?

At five years, interviews focus on architecture and leadership: designing component libraries, choosing state and data strategies, Server Components, caching, security and accessibility. Frontend system design rounds ask you to design a large feature end to end. You should explain trade-offs clearly, show how you mentored others and give examples of measurable improvements you drove.

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 5 years of experience?

Focus first on Frontend system design, Server Components and caching, Design systems, Mentoring and ownership.