Skip to content

Medium React JS Interview Questions

96 medium questions with answers. Applied questions that check whether you can use the concepts on real work.

Topics in the medium set

Routing and Forms: 14 State Management: 13 Next.js and Advanced React: 13 React Hooks: 12 React Fundamentals: 11 Props and State: 11 Performance Optimization: 11 Testing and Debugging: 11
Solved — mediumStart 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 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.
02

What changed with createRoot in React 18 and later compared to ReactDOM.render?

Save to ▾
Show answer
createRoot from react-dom/client replaces the legacy ReactDOM.render, which was removed in React 19. createRoot enables the concurrent renderer, automatic batching of all updates, and features like useTransition and Suspense for data. You call const root = createRoot(document.getElementById('root')) and then root.render(<App />). For server-rendered HTML you use hydrateRoot instead.
03

What are synthetic events in React?

Save to ▾
Show answer
React wraps native browser events in a SyntheticEvent object that has the same interface (preventDefault, stopPropagation, target) across browsers. Since React 17, listeners are attached to the root container instead of document, which makes it safer to mix multiple React versions. Event pooling was removed, so you can read event properties asynchronously. Handlers are passed as functions, like onClick={handleClick}.
04

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.
05

Why does React prefer composition over inheritance?

Save to ▾
Show answer
React reuses UI by combining components, not by extending classes. You pass components or JSX as props or children, so a generic Card or Modal can wrap any content. Specialisation is done by configuring a general component with props, like <Button variant='danger'>. The React team found no use cases where inheritance hierarchies were better, and composition keeps components flexible and loosely coupled.
06

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.
07

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.
08

How do you create an error boundary in React?

Save to ▾
Show answer
An error boundary catches errors thrown while rendering its children and shows a fallback UI. It must be a class component (or use the react-error-boundary package). class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch(error, info) { logError(error, info.componentStack); } render() { return this.state.hasError ? <p>Something went wrong</p> : this.props.children; } } It does not catch errors in event handlers or async code.
09

What are portals and when would you use them?

Save to ▾
Show answer
A portal renders children into a DOM node outside the parent component's DOM hierarchy while keeping them in the same React tree, so context and event bubbling still work. import { createPortal } from 'react-dom'; function Modal({ children }) { return createPortal( <div className="modal">{children}</div>, document.getElementById('modal-root') ); } They are used for modals, tooltips and dropdowns that must escape overflow hidden or z-index issues.
10

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?

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

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.
12

Why should you use the functional form of setState? Show an example.

Save to ▾
Show answer
State updates are scheduled, and each render sees a snapshot of state. If you call setCount(count + 1) several times in one handler, all calls read the same old value. The updater function receives the latest pending value. function addThree() { setCount(c => c + 1); setCount(c => c + 1); setCount(c => c + 1); } // count increases by 3, not 1
13

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.
14

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?

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

Your team's old codebase uses PropTypes and is upgrading to React 19. What would you recommend for checking prop types?

Save to ▾
Show answer
PropTypes checks props at runtime in development and was removed from the React package in React 19, so it now needs the separate prop-types library and is ignored silently. TypeScript checks types at compile time, gives editor autocompletion, and catches errors before code runs. Most new React projects in India and abroad use TypeScript, and many job descriptions now list it as required.
16

How do you type props for a component in TypeScript?

Save to ▾
Show answer
Define a type or interface and annotate the destructured props. type ProductCardProps = { title: string; price: number; tags?: string[]; onAdd: (id: string) => void; children?: React.ReactNode; }; function ProductCard({ title, price, tags = [], onAdd, children }: ProductCardProps) { return <div onClick={() => onAdd(title)}>{title} - Rs {price}{children}</div>; }
17

A teammate writes setCount(count + 1) three times in a click handler and is surprised that the count increases by only one. Explain why.

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

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.
19

A cart page keeps the selected item object in state and also the items array. After editing the item's quantity, the details panel shows the old quantity. Why?

Save to ▾
Show answer
The selected item is a copy duplicated from the array, so updating the array does not update the copy. This breaks the single source of truth. Store only selectedId in state and derive the item during render: const selected = items.find(i => i.id === selectedId). Now there is one place where each item lives, and every view stays in sync automatically.
20

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.
21

A junior notices that every child component re-renders when the parent's state changes, even when their props did not change. How do you explain this?

Save to ▾
Show answer
By default, when a component renders, React re-renders all of its children, because it cannot know whether they depend on something else. Re-rendering is usually cheap since only real changes reach the DOM. If a child is expensive, wrap it in React.memo and keep its props stable with useCallback or useMemo, or let the React Compiler memoise automatically. Moving state down also helps.
22

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.
23

What is lazy initialisation in useState and when should you use it?

Save to ▾
Show answer
If the initial value is expensive to compute, pass a function instead of the value. React calls it only on the first render. // runs on every render (wasteful) const [data, setData] = useState(JSON.parse(localStorage.getItem('cart'))); // runs once const [data, setData] = useState(() => JSON.parse(localStorage.getItem('cart') ?? '[]') );
24

What is an effect cleanup function and why is it important?

Save to ▾
Show answer
The function you return from useEffect is the cleanup. React runs it before the effect runs again and when the component unmounts. Use it to clear timers, remove event listeners, close WebSocket connections, unsubscribe from stores or abort fetch requests. Without cleanup you get memory leaks, duplicate listeners and state updates on components that are no longer on the screen.
25

What is the difference between useMemo and useCallback?

Save to ▾
Show answer
useMemo caches the result of a calculation between renders, like a filtered list: useMemo(() => filter(items, q), [items, q]). useCallback caches a function definition itself: useCallback(fn, deps), which equals useMemo(() => fn, deps). Both are performance tools, useful when passing values to React.memo children or effect dependencies. With the React Compiler enabled, much of this manual memoisation becomes unnecessary.
26

When should you choose useReducer over useState? Show a small example.

Save to ▾
Show answer
Use useReducer when state has several related fields or many update types, so logic lives in one pure, testable function. function reducer(state, action) { switch (action.type) { case 'added': return [...state, action.item]; case 'removed': return state.filter(i => i.id !== action.id); default: return state; } } const [cart, dispatch] = useReducer(reducer, []); dispatch({ type: 'added', item });
27

Write a custom hook to fetch data with loading and error states.

Save to ▾
Show answer
A custom hook is a function starting with use that combines other hooks. function useFetch(url) { const [state, setState] = useState({ data: null, loading: true, error: null }); useEffect(() => { const ctrl = new AbortController(); setState(s => ({ ...s, loading: true })); fetch(url, { signal: ctrl.signal }) .then(r => { if (!r.ok) throw new Error(r.status); return r.json(); }) .then(data => setState({ data, loading: false, error: null })) .catch(err => { if (err.name !== 'AbortError') setState({ data: null, loading: false, error: err }); }); return () => ctrl.abort(); }, [url]); return state; }
28

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.
29

What do useActionState and useFormStatus do in React 19?

Save to ▾
Show answer
useActionState wraps an action function and returns [state, formAction, isPending]; you pass formAction to a form's action prop and the returned state holds the result, like a validation error. useFormStatus, from react-dom, lets a child component such as a submit button read the pending status of the parent form without prop drilling. Together they remove a lot of manual loading and error state code.
30

Your component fetches data in useEffect and the network tab shows thousands of requests in a loop. What could cause it?

Save to ▾
Show answer
Common causes are a missing dependency array, so the effect runs after every render and its setState triggers another render, or a dependency that is a new object or array each render, like options={{ page: 1 }}. Since Object.is compares by reference, the effect always sees a change. Fix by adding correct dependencies, depending on primitive values, or memoising the object with useMemo.
31

The linter shows a react-hooks/exhaustive-deps warning and a teammate wants to disable the rule. What do you advise?

Save to ▾
Show answer
Disabling it usually hides a stale closure bug. The rule tells you the effect reads a value that can change. Instead, include the dependency, move a function inside the effect, use the updater form of setState so state is not needed, or move non-reactive logic into useEffectEvent. If an object dependency changes every render, create it inside the effect or memoise it. Suppress only with a clear comment.
32

A developer sends an analytics event inside a useEffect that watches a 'submitted' state flag. Is this a good pattern?

Save to ▾
Show answer
No. Logic caused by a specific user action belongs in the event handler, not in an effect. Setting a flag and reacting in an effect adds an extra render, is harder to follow and can fire again on remount in StrictMode. Call sendAnalytics directly in handleSubmit. Effects are for synchronising with external systems because the component is displayed, not for responding to events.
33

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
34

A junior developer is confused about when to use useEffect. How would you explain it and help them?

Save to ▾
Show answer
Situation: a new joiner was putting almost all logic into effects, causing bugs. Task: help them build the right mental model. Action: I explained that effects sync with outside systems, while calculations go in render and user actions go in handlers. We refactored one of their components together and I shared the react.dev article on avoiding effects. Result: their next PR had no unnecessary effects.
35

What is the main performance pitfall of React Context and how do you avoid it?

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

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;
37

How do you handle async API calls with createAsyncThunk?

Save to ▾
Show answer
createAsyncThunk creates pending, fulfilled and rejected actions around an async function, and you handle them in extraReducers. export const fetchOrders = createAsyncThunk('orders/fetch', async () => { const res = await fetch('/api/orders'); return res.json(); }); extraReducers: builder => { builder .addCase(fetchOrders.pending, s => { s.status = 'loading'; }) .addCase(fetchOrders.fulfilled, (s, a) => { s.status = 'idle'; s.list = a.payload; }) .addCase(fetchOrders.rejected, (s, a) => { s.status = 'failed'; s.error = a.error.message; }); }
38

What is RTK Query?

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

What is the difference between server state and client state?

Save to ▾
Show answer
Client state is owned by the browser, like whether a modal is open, form input or theme. Server state lives on a backend and is only cached on the client, like products or orders; it can become stale, be changed by other users and needs refetching. Tools like TanStack Query or RTK Query manage server state, while useState, Context or Zustand handle client state.
40

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} />; }
41

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.
42

A new mid-sized dashboard project is starting. How would you decide between Context, Redux Toolkit and Zustand?

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

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.
44

How do you persist part of your state, like the cart, across page reloads?

Save to ▾
Show answer
Save it to localStorage and read it on startup. With Zustand, the persist middleware does this. import { persist } from 'zustand/middleware'; const useCart = create(persist( set => ({ items: [], add: i => set(s => ({ items: [...s.items, i] })) }), { name: 'cart-storage' } )); With Redux, use redux-persist or a store.subscribe that writes a throttled snapshot. Never store secrets like tokens in localStorage.
45

Where should you store the logged-in user's auth token in a React app?

Save to ▾
Show answer
The safest common choice is an httpOnly, Secure, SameSite cookie set by the server, because JavaScript cannot read it, which protects it from XSS theft. Keep only non-sensitive user info, such as name and role, in React state or context. If you must use tokens in JavaScript, keep the access token in memory, use short expiry and refresh tokens via cookies. Avoid localStorage for tokens.
46

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.
47

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.
48

What are nested routes and what is the role of Outlet?

Save to ▾
Show answer
Nested routes let a parent route render a shared layout, like a dashboard sidebar, while child routes render inside it. The parent component places <Outlet /> where the matching child should appear. For example, /dashboard renders DashboardLayout, and /dashboard/orders renders Orders inside its Outlet. This keeps layouts consistent, avoids repeating headers and lets each section load its own data.
49

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.
50

What are loaders and actions in React Router?

Save to ▾
Show answer
In data or framework mode, a route can define a loader that fetches data before the route renders, and an action that handles form submissions or mutations for that route. Components read data with useLoaderData. After an action runs, the router automatically revalidates loaders so the UI shows fresh data. This moves data fetching out of effects and avoids request waterfalls between parent and child components.
51

How do you lazy load route components to reduce the initial bundle?

Save to ▾
Show answer
Use React.lazy with dynamic import and wrap routes in Suspense, so each page becomes a separate chunk downloaded only when visited. In React Router data mode you can also use the route's lazy property to load the component and loader together. This greatly reduces the first load size for big apps like admin panels. Show a lightweight fallback, such as a skeleton, while chunks load.
52

Your React app works locally, but after deployment refreshing /dashboard gives a 404 from the server. Why, and how do you fix it?

Save to ▾
Show answer
The server looks for a real file at /dashboard, which does not exist because routing happens in the browser. Configure the server to return index.html for all unknown paths: a rewrite rule in Nginx (try_files $uri /index.html), a _redirects or rewrites setting on static hosts, or a fallback in Express. Alternatively, use a framework with server routing. HashRouter also avoids it but gives ugly URLs.
53

Why is React Hook Form popular?

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

Show a login form using React Hook Form with Zod validation.

Save to ▾
Show answer
Define a schema and pass it through zodResolver. const schema = z.object({ email: z.string().email('Invalid email'), password: z.string().min(8, 'Min 8 characters') }); function Login() { const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ resolver: zodResolver(schema) }); return ( <form onSubmit={handleSubmit(data => login(data))}> <input {...register('email')} /> {errors.email && <p>{errors.email.message}</p>} <input type="password" {...register('password')} /> <button disabled={isSubmitting}>Login</button> </form> ); }
55

How do form Actions work in React 19?

Save to ▾
Show answer
In React 19 you can pass a function to a form's action prop: <form action={saveAddress}>. React calls it with the FormData when the form submits, runs it inside a transition, tracks pending state and resets uncontrolled fields after success. Combined with useActionState for results and useFormStatus for button state, this removes manual onSubmit, preventDefault and loading flags. In Next.js the action can be a Server Function.
56

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.
57

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> </>); }
58

An accessibility audit says your signup form fails screen reader checks. What would you fix to make the form accessible?

Save to ▾
Show answer
Link every input to a visible label using htmlFor and id (useId helps), use correct input types, and mark required fields. Show errors as text, connect them with aria-describedby and set aria-invalid on failing fields. Move focus to the first error on submit, keep everything keyboard operable and do not rely on colour alone. Use native button and form elements instead of clickable divs.
59

Tell me about a time a requirement changed late in a sprint. How did you handle it?

Save to ▾
Show answer
Situation: two days before release, the product owner asked to add a GST number field with validation to our checkout form. Task: deliver it without risking the release. Action: I estimated the impact, proposed shipping the field with basic format validation first and the full verification API in the next sprint, and added tests. Result: we released on time and the follow-up shipped a week later.
60

How would you explain to a non-technical manager why our marketing pages need server rendering?

Save to ▾
Show answer
I would keep it simple: a pure client-side React page sends an almost empty HTML file, so some crawlers and social previews see little content and users on slow phones see a blank screen longer. Server rendering sends ready HTML, improving SEO and first load. I would back this with a Lighthouse comparison and search console data, and suggest a framework like Next.js only for pages that need it.
61

Describe a time you found and fixed a bug reported by a real user.

Save to ▾
Show answer
Situation: customers reported that the address form sometimes submitted an empty PIN code. Task: find the root cause quickly. Action: I reproduced it with browser autofill, which changed the DOM value without firing our onChange in a custom input. I switched to a native controlled input, added a schema check before submit and wrote a regression test. Result: the issue stopped and support tickets dropped.
62

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.
63

How do you find what is making a React page slow?

Save to ▾
Show answer
Use the React DevTools Profiler: record an interaction, then check the flame chart for components with long render times and the reason each one re-rendered (enable 'record why each component rendered'). Pair it with the Chrome Performance panel for long tasks, layout and network. Enable highlight updates to spot unnecessary renders. Measure first, fix the biggest cost, then measure again in a production build.
64

What is list virtualisation?

Save to ▾
Show answer
Virtualisation, or windowing, renders only the rows visible in the viewport plus a small buffer, instead of thousands of DOM nodes. As the user scrolls, rows are recycled with new data. Libraries such as TanStack Virtual and react-window implement it. It dramatically cuts render time and memory for long lists, chat histories and data grids, but needs care with variable row heights and browser find-in-page.
65

An orders table renders 10,000 rows and the page freezes on load and scroll. What would you do?

Save to ▾
Show answer
First question whether users need all rows at once: server-side pagination or infinite scroll with cursor-based loading may solve it. If a long scrollable list is required, virtualise it with TanStack Virtual so only around 30 rows exist in the DOM. Also memoise row components, avoid heavy formatting in render by precomputing, and move sorting or filtering off the main thread if it is expensive.
66

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.
67

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.
68

What are Core Web Vitals and how do React apps affect them?

Save to ▾
Show answer
Core Web Vitals are Google's user experience metrics: LCP (Largest Contentful Paint, loading, target under 2.5s), INP (Interaction to Next Paint, responsiveness, under 200ms, which replaced FID in 2024) and CLS (Cumulative Layout Shift, visual stability, under 0.1). Large React bundles hurt LCP and INP, heavy renders on clicks hurt INP, and images or content without reserved space hurt CLS. SSR, code splitting and transitions help.
69

What is tree shaking?

Save to ▾
Show answer
Tree shaking is dead code elimination done by bundlers like Rollup (used by Vite), esbuild and webpack. They analyse ES module import and export statements and drop exports that are never used. It works only with static ESM syntax, not CommonJS require, and can be blocked by modules with side effects. Importing import { debounce } from 'lodash-es' instead of the whole lodash library keeps the bundle small.
70

A developer writes key={Math.random()} to silence the missing key warning. What problem does this create?

Save to ▾
Show answer
A new random key on every render tells React each item is brand new, so it unmounts and remounts every row each time. This destroys component state such as typed input and focus, re-runs effects and is very slow for large lists. Keys must be stable and unique, ideally an id from the data. If items have no id, generate ids once when the data is created.
71

Tell me about a time you improved the performance of an application.

Save to ▾
Show answer
Situation: our admin dashboard took about eight seconds to become usable on office laptops. Task: bring it under three seconds. Action: I profiled it, found a 2 MB bundle and a chart library loaded on every page, lazy loaded routes and charts, replaced moment with dayjs and virtualised a large table. Result: load time dropped to around 2.5 seconds and support complaints about slowness stopped.
72

How do you handle a tight deadline when you know the code is not perfect?

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

Which queries should you prefer in React Testing Library and why?

Save to ▾
Show answer
Prefer queries accessible to everyone: getByRole (with the name option) first, then getByLabelText for form fields, getByPlaceholderText, getByText and getByDisplayValue. Use getByAltText and getByTitle for images and titles. getByTestId is a last resort when nothing else works. Role-based queries also check accessibility, because if a button cannot be found by role and name, screen reader users will struggle too.
74

Why is user-event preferred over fireEvent?

Save to ▾
Show answer
fireEvent dispatches a single DOM event, such as one change event. user-event simulates full user interactions: typing fires keydown, keypress, input and keyup for every character, clicking includes pointer and focus events, and it respects disabled elements and visibility. This finds bugs that fireEvent misses. In current versions, call userEvent.setup() and await each interaction because they are asynchronous.
75

How do you test a component that fetches data from an API?

Save to ▾
Show answer
Mock the network, not fetch itself, using Mock Service Worker (MSW), then wait for the UI. const server = setupServer( http.get('/api/users', () => HttpResponse.json([{ id: 1, name: 'Asha' }])) ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); test('shows users', async () => { render(<Users />); expect(await screen.findByText('Asha')).toBeInTheDocument(); });
76

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.
77

How do you test a custom hook?

Save to ▾
Show answer
Use renderHook from React Testing Library and wrap state changes in act. import { renderHook, act } from '@testing-library/react'; import { useCounter } from './useCounter'; test('increments', () => { const { result } = renderHook(() => useCounter(5)); act(() => result.current.increment()); expect(result.current.count).toBe(6); }); If the hook needs context, pass a wrapper option with the provider.
78

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.
79

When would you add end-to-end tests with Playwright or Cypress?

Save to ▾
Show answer
Add E2E tests for critical business flows that cross many components and the backend, such as signup, login, search, add to cart and payment. They run in real browsers and catch integration issues unit tests miss. They are slower and more brittle, so keep them few and focused, use stable selectors like roles or data-testid, seed test data and run them in CI on every pull request.
80

All unit tests passed, but users found that the delete button did nothing. The tests were checking that a handleDelete function was called. What went wrong?

Save to ▾
Show answer
The tests checked implementation details instead of user-visible behaviour. A mocked handler being called says nothing about whether the item actually disappears or the API is hit. Rewrite the test to click the button by role, mock the API with MSW and assert the item is removed from the screen. Behaviour-focused tests survive refactoring and catch real bugs like a broken wiring.
81

How do you include accessibility checks in your React tests?

Save to ▾
Show answer
Use jest-axe or vitest-axe to run axe-core on rendered components. import { axe } from 'vitest-axe'; test('form has no a11y violations', async () => { const { container } = render(<SignupForm />); expect(await axe(container)).toHaveNoViolations(); }); Also query by role and label in tests, run Lighthouse or axe DevTools in the browser, and test keyboard navigation manually, since automated tools catch only part of the issues.
82

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.
83

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.
84

Explain CSR, SSR, SSG and ISR.

Save to ▾
Show answer
CSR (client-side rendering) sends a minimal HTML shell and renders everything in the browser. SSR (server-side rendering) generates HTML on each request, good for personalised or fast-changing pages. SSG (static site generation) builds HTML at build time, fastest for pages that rarely change like blogs. ISR (incremental static regeneration) serves static pages but regenerates them in the background after a time or on demand, mixing speed with freshness.
85

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.
86

What does the 'use client' directive do?

Save to ▾
Show answer
'use client' at the top of a file marks a boundary: that module and everything it imports become part of the client bundle and can use state, effects, event handlers and browser APIs. It does not mean the component is only rendered on the client; it is still pre-rendered to HTML on the server and then hydrated. Keep 'use client' as low in the tree as possible to send less JavaScript.
87

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.
88

How do you fetch data in a Next.js App Router page?

Save to ▾
Show answer
Make the page an async Server Component and await data directly. // app/products/[id]/page.tsx export default async function ProductPage({ params }) { const { id } = await params; const product = await getProduct(id); if (!product) notFound(); return ( <main> <h1>{product.name}</h1> <AddToCartButton id={id} /> </main> ); } AddToCartButton is a Client Component. In recent Next.js versions params is a promise.
89

What is hydration?

Save to ▾
Show answer
Hydration is the process where React, running in the browser, attaches event handlers and state to HTML that was already rendered on the server, making it interactive. React expects the first client render to match the server HTML exactly; if not, it reports a hydration mismatch. Until hydration finishes, the page is visible but buttons may not respond, which is why reducing client JavaScript matters.
90

How do you set SEO metadata in a Next.js App Router page?

Save to ▾
Show answer
Export a metadata object or a generateMetadata function from a page or layout. export async function generateMetadata({ params }) { const { slug } = await params; const post = await getPost(slug); return { title: post.title, description: post.summary, alternates: { canonical: `/blog/${slug}` }, openGraph: { title: post.title, images: [post.cover] } }; } Next.js renders the tags in the head during server rendering.
91

Your Next.js build fails with 'ReferenceError: window is not defined'. Why, and how do you fix it?

Save to ▾
Show answer
Code that touches browser globals runs during server rendering, where window and document do not exist. This often comes from a library that accesses window at import time or from reading localStorage in render. Fix it by moving browser access into useEffect or event handlers inside a Client Component, checking typeof window !== 'undefined', or loading the library with next/dynamic and ssr: false from a client component.
92

What are the common styling options in React, and how do Server Components affect the choice?

Save to ▾
Show answer
Options include plain CSS or CSS Modules (scoped class names), Tailwind CSS utility classes, Sass, and CSS-in-JS libraries like styled-components or Emotion. Runtime CSS-in-JS needs client-side JavaScript and context, so it fits poorly with Server Components. That is why Tailwind and CSS Modules, or zero-runtime libraries, are the most common choice in modern Next.js projects. Component libraries like shadcn/ui build on Tailwind.
93

A blog feature must render HTML content that comes from a CMS. Why is dangerouslySetInnerHTML risky and how do you use it safely?

Save to ▾
Show answer
React escapes values in JSX by default, which protects against XSS. dangerouslySetInnerHTML bypasses this and inserts raw HTML, so if the content comes from users or an untrusted CMS, attackers can inject scripts. If you must render HTML, sanitise it first with a library like DOMPurify, restrict allowed tags, and add a Content Security Policy. Also never put user input into href attributes without validating the protocol.
94

During a security review you find a payment provider's secret key in the browser's JavaScript bundle. What do you do?

Save to ▾
Show answer
Treat it as leaked: rotate the key immediately in the provider dashboard and check logs for misuse. Then fix the cause, usually the secret was in a public env variable such as NEXT_PUBLIC_ or VITE_, or imported into a client component. Move the call to a server route or Server Function that uses a server-only variable, add the server-only package to guard imports, and add secret scanning in CI.
95

How do you add multiple languages to a React app?

Save to ▾
Show answer
Use an i18n library such as react-i18next or, in Next.js, next-intl. Store translations in JSON files per locale, reference keys like t('cart.title') instead of hard-coded text, and handle plurals, dates, numbers and currency with Intl APIs. Put the locale in the URL, like /hi/products, for SEO and shareable links. For Indian audiences, check fonts for Devanagari and other scripts and test longer translated text.
96

Tell me about working with a backend team when an API did not match what the frontend needed.

Save to ▾
Show answer
Situation: the orders API returned deeply nested data and no pagination, making our list screen slow. Task: get a better contract without blocking either team. Action: I shared a proposed response shape with the backend developer, explained the performance impact with numbers and built against a mocked API using MSW meanwhile. Result: they added pagination and flatter fields, and we integrated in two days with no rework.

Frequently asked questions

Who should practise medium React JS questions?

Anyone with some hands-on practice: most real interview time is spent on medium questions like these.

How should I practise these?

Answer each question aloud before opening the answer, mark it as solved only when your answer matches the key idea, and save misses to Last-Minute Revision.

Other difficulty levels