6. React Interview Questions
Grouped Basic → Intermediate → Advanced. Each answer is written the way you would actually say it in an interview: short answer first, then the detail if they push.
Basic
1. What is React?
A JavaScript library for building user interfaces from reusable components. It is declarative, component based, uses a Virtual DOM, and has one-way data flow.
Follow-up: Library or framework? — Library. It only handles the view. Routing, state and data fetching come from other packages. Next.js is the framework built on top of it.
2. What are the advantages of React?
- Reusable components — build once, use everywhere
- Virtual DOM — efficient, batched DOM updates
- Declarative — describe the UI for a given state, React figures out the transitions
- One-way data flow — easier to trace where data came from
- Huge ecosystem and job market
- SEO-friendly through SSR (Next.js)
- React Native reuses the same skills for mobile
3. What are the limitations of React?
- Only the view layer — you assemble the rest of the stack yourself
- Fast-moving ecosystem; patterns churn
- JSX is a learning curve for newcomers
- Poor SEO without SSR
- Documentation for third-party libraries varies wildly
- Large bundle size compared to Svelte or vanilla JS
4. What is JSX?
A syntax extension that lets you write HTML-like markup in JavaScript. Babel compiles it to React.createElement() calls.
const el = <h1 className="title">Hi</h1>;
// becomes
const el = React.createElement("h1", { className: "title" }, "Hi");Follow-up: Is JSX required? — No. It is sugar. You can write createElement by hand.
5. What is the Virtual DOM and how does React use it?
A lightweight JavaScript object tree mirroring the real DOM. On a state change React builds a new tree, diffs it against the old one, computes the minimal set of real DOM operations, and applies them in one batch.
Why it helps: real DOM writes trigger layout and paint and are expensive. Diffing plain objects is cheap.
Honest framing: the Virtual DOM is not inherently faster than perfect hand-written DOM code. It gives you near-optimal updates from a declarative description, for free.
6. What is useState?
The hook that adds local state to a function component.
const [count, setCount] = useState(0);Returns the current value and a setter. Calling the setter schedules a re-render.
Follow-up: Why the updater form? — because state updates are batched. setCount(count + 1) twice adds 1. setCount(p => p + 1) twice adds 2.
7. What are keys in React?
A special prop that gives list items a stable identity so React can match old and new children during diffing.
{users.map((u) => <li key={u.id}>{u.name}</li>)}Rules: unique among siblings, stable across renders. Never Math.random().
Follow-up: Why is index a bad key? — deleting or reordering shifts every index, so React reuses the wrong DOM node and internal state (like input values) lands on the wrong row.
8. What are props?
Read-only inputs passed from parent to child. They configure a component.
<Profile name="Dipak" role="Dev" />Props are immutable inside the child. To change data owned by a parent, the parent passes down a callback.
9. State vs props?
| Props | State | |
|---|---|---|
| Owner | Parent | The component |
| Mutable | No | Yes |
| Purpose | Configuration | Changing data over time |
One-liner: props are the arguments to a function, state is a variable inside it that survives between calls.
10. Functional vs class components?
Functional: a plain function, uses hooks, no this, less boilerplate — the modern default. Class: extends React.Component, uses this.state and lifecycle methods.
Since React 16.8 hooks made them equivalent, with one exception: error boundaries still require a class.
11. Controlled vs uncontrolled components?
Controlled — React state is the source of truth (value + onChange). Easy validation, re-renders per keystroke.
Uncontrolled — the DOM holds the value, read via a ref (defaultValue). No re-renders.
File inputs must be uncontrolled. react-hook-form is uncontrolled by design for performance.
12. What is prop drilling?
Passing props through components that don't use them just to reach a deep child.
Fixes, in order of preference: component composition (children), Context API, then a state library.
13. What are error boundaries?
Class components that catch JavaScript errors in their child tree, log them, and render a fallback UI instead of unmounting the whole app.
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) { logToService(error, info); }They do NOT catch: event handler errors, async code, SSR errors, or errors in the boundary itself.
14. What are React Hooks?
Functions starting with use that let function components use state, lifecycle and context. Added in React 16.8.
They solved: stateful logic reuse (previously HOC/render-prop wrapper hell), components split by lifecycle instead of by concern, and confusing this binding.
15. What are the rules of hooks and why?
- Only call hooks at the top level — not in conditions, loops or nested functions
- Only call hooks from React function components or custom hooks
Why: React stores hook state in an ordered list per component and matches state to hook by call index, not by name. Skip a hook once and every hook after it reads the wrong slot.
16. What does useEffect do?
Runs side effects after render, and optionally cleans them up.
| Deps | Runs |
|---|---|
| omitted | after every render |
[] | once after mount |
[a] | on mount and whenever a changes |
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);The return function is the cleanup — it runs on unmount and before every re-run.
17. What is a custom hook?
A function starting with use that calls other hooks, used to share stateful logic between components.
function useToggle(initial = false) {
const [on, setOn] = useState(initial);
const toggle = useCallback(() => setOn((p) => !p), []);
return [on, toggle];
}Key point: custom hooks share logic, not state. Two components calling useToggle get two independent states.
18. What are the types of side effects?
- Without cleanup — logging, analytics, updating
document.title - With cleanup — subscriptions, timers, event listeners, WebSockets
- Layout effects — DOM measurement that must happen before paint (
useLayoutEffect)
Forgetting cleanup causes memory leaks and duplicate listeners.
19. Explain conditional rendering.
{isLoggedIn ? <Dashboard /> : <Login />}
{hasError && <ErrorMessage />}Trap: {items.length && <List />} renders a literal 0 when the array is empty. Use items.length > 0 &&.
20. What is React.Fragment?
Lets you return multiple elements without an extra DOM node.
<>
<td>A</td>
<td>B</td>
</>Needed because a wrapper <div> breaks flex/grid layouts and produces invalid HTML inside tables. Use <React.Fragment key={...}> when you need a key.
21. What is Strict Mode?
A development-only wrapper that double-invokes renders and effects to surface impure code and missing cleanups, and warns about deprecated APIs.
"My effect runs twice" is Strict Mode in development. It does not happen in production.
22. How do you style a React component?
Inline styles, plain CSS, CSS Modules, Tailwind, CSS-in-JS (styled-components, Emotion), or zero-runtime CSS-in-JS (vanilla-extract).
Note: runtime CSS-in-JS does not work in Server Components, which is why the ecosystem shifted toward Tailwind and CSS Modules.
23. What are the component lifecycle phases?
Mounting → Updating → Unmounting, plus error handling.
| Class | Hook |
|---|---|
componentDidMount | useEffect(fn, []) |
componentDidUpdate | useEffect(fn, [deps]) |
componentWillUnmount | cleanup returned from useEffect |
shouldComponentUpdate | React.memo |
Better framing: useEffect is not a lifecycle mapping — it is a synchronisation tool.
Intermediate
24. What is the Context API and when do you use it?
A way to pass data through the tree without prop drilling.
const ThemeContext = createContext("light");
<ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
const theme = useContext(ThemeContext);Best for values that are needed widely and change rarely: theme, locale, current user.
Why not for everything: Context has no selector. Every consumer re-renders when the provider value changes, even if it reads only one field.
25. React.memo vs useMemo vs useCallback?
| Caches | Usage | |
|---|---|---|
React.memo | The rendered output of a component | memo(Component) |
useMemo | A computed value | useMemo(() => calc(), [deps]) |
useCallback | A function reference | useCallback(fn, [deps]) |
useCallback(fn, deps) is exactly useMemo(() => fn, deps).
All three are optimisations, not correctness tools. The React Compiler now does this automatically.
26. What is useReducer and when over useState?
const [state, dispatch] = useReducer(reducer, initialState);Use it when several state values change together, when the next state depends on the previous one, or when the update logic is complex enough to be worth unit testing on its own — the reducer is a pure function testable without React.
27. useEffect vs useLayoutEffect?
useEffect runs after paint, asynchronously — the default. useLayoutEffect runs after DOM mutation but before paint, synchronously — it blocks painting.
Use useLayoutEffect only when the user would otherwise see wrong layout for one frame (measuring a tooltip, scroll position restore). It warns during SSR because there is no DOM.
28. What is useRef used for?
Two things:
- Accessing a DOM node —
<input ref={inputRef} />theninputRef.current.focus() - Storing a mutable value that does not trigger a re-render — timer IDs, previous values, library instances
Changing ref.current never re-renders. That is the whole point.
29. What is forwardRef?
Refs are not props, so they don't pass through a component. forwardRef lets a parent attach a ref to a child's inner DOM node.
const Input = forwardRef((props, ref) => <input ref={ref} {...props} />);React 19: ref is now a normal prop for function components, so forwardRef is no longer needed and is deprecated.
30. What is useImperativeHandle?
Lets a component expose a limited API through its ref instead of the raw DOM node.
useImperativeHandle(ref, () => ({
focus: () => innerRef.current.focus(),
}));Use it sparingly — it is an escape hatch from declarative data flow.
31. What are Higher Order Components?
A function that takes a component and returns an enhanced component. A pattern, not an API.
const withAuth = (C) => (props) => (user ? <C {...props} /> : <Login />);Real examples: React.memo, Redux connect(). Downsides: wrapper hell, prop collisions, unclear prop origin. Mostly replaced by custom hooks for logic reuse.
32. What are render props?
Passing a function as a prop that returns JSX, so the parent supplies the logic and the child decides the rendering.
<Mouse render={({ x, y }) => <p>{x},{y}</p>} />The three code-reuse patterns in React: HOC, render props, custom hooks.
33. What are React Portals?
createPortal(children, domNode) renders a child into a DOM node outside the parent's DOM hierarchy while keeping it in the React tree.
Used for modals, tooltips and dropdowns that would otherwise be clipped by overflow: hidden or trapped in a z-index stacking context.
Key detail: events still bubble through the React tree, not the DOM tree.
34. What are React.lazy and Suspense?
const Chart = lazy(() => import("./Chart"));
<Suspense fallback={<Spinner />}><Chart /></Suspense>lazy creates a component whose code is fetched in a separate bundle chunk on first render. Suspense declares the fallback shown while anything below it is loading.
Best split points: routes, modals, charts, editors.
35. How do you prevent unnecessary re-renders?
- Colocate state — move it down so fewer components sit under it
childrenprop — an element created by the grandparent doesn't change referenceReact.memoon genuinely expensive componentsuseMemo/useCallbackfor stable prop references- Split contexts so consumers don't share unrelated updates
- Stable keys
- Profile first — React DevTools Profiler tells you what actually re-renders
36. Name React performance optimisation techniques.
Rendering: memo, useMemo, useCallback, state colocation, correct keys, virtualisation. Bundle: code splitting via React.lazy, dynamic imports, tree shaking, bundle analysis. Data: debounce inputs, paginate, cache with React Query. Assets: lazy images, WebP/AVIF, CDN, compression. Rendering strategy: SSR/SSG for first paint.
Always open with "I'd profile first."
37. How do you fetch data in React?
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((r) => r.json())
.then(setData)
.catch((e) => { if (e.name !== "AbortError") setError(e); });
return () => controller.abort();
}, [url]);The abort prevents a race condition where a slow older request overwrites a fast newer one.
In production most teams use React Query / SWR instead — you get caching, dedupe, retries and background refetch for free.
38. Client state vs server state?
Client state — owned by your app, synchronous: modal open, form draft, theme. Server state — owned by a backend, asynchronous, shared, and can go stale without you touching it.
Redux was designed for client state. React Query exists because server state needs caching, revalidation and dedupe — a different problem entirely.
39. Explain Redux and its core principles.
- Single source of truth — one store
- State is read-only — change it only by dispatching actions
- Changes are made by pure reducers
Flow: dispatch(action) → middleware → reducer → new state → subscribed components re-render.
Modern Redux means Redux Toolkit: createSlice, configureStore, createAsyncThunk.
40. Redux Toolkit lets me write state.items.push() — isn't that a mutation?
It looks like one but is not. RTK uses Immer, which records mutations against a draft proxy and produces a new immutable state object. Outside createSlice/createReducer, you must still write immutable updates by hand.
41. Can hooks replace Redux?
Partly. useReducer + Context gives you the reducer pattern and global access.
What it does not give you: selector-based subscriptions (Context re-renders all consumers), middleware, time-travel DevTools, and a store usable outside React.
For small and medium apps, hooks + Context is genuinely enough. Answer with the trade-off, not yes or no.
42. How do you pass data between components?
| Direction | Method |
|---|---|
| Parent → Child | Props |
| Child → Parent | Callback prop |
| Sibling → Sibling | Lift state to common parent |
| Deeply nested | Context |
| Anywhere | Redux / Zustand |
| Across routes | URL params, search params, router state |
43. How do you pass data between siblings using React Router?
navigate("/detail", { state: { userId: 7 } });
const { userId } = useLocation().state ?? {};Prefer URL params over router state for anything that should survive a refresh or be shareable as a link.
44. How do you redirect after login?
const navigate = useNavigate();
await api.login(creds);
navigate("/dashboard", { replace: true });replace: true removes the login page from history so the back button doesn't return there.
Better version — send them where they originally wanted to go:
const from = location.state?.from?.pathname || "/dashboard";
navigate(from, { replace: true });45. What is React Router and how do you protect a route?
The standard client-side routing library. It intercepts navigation, updates the URL with the History API, and swaps components without a page reload.
function ProtectedRoute({ children }) {
const { user, isLoading } = useAuth();
const location = useLocation();
if (isLoading) return <Spinner />;
if (!user) return <Navigate to="/login" state={{ from: location }} replace />;
return children;
}Security note: route guards are UX only. Every protected resource must also be authorised on the server.
46. How do you build a switching component?
A lookup object, not an if/else chain:
const PAGES = { home: Home, about: About, contact: Contact };
function Page({ name }) {
const Component = PAGES[name] ?? NotFound;
return <Component />;
}47. How do you re-render on browser resize?
A custom hook with an event listener and cleanup:
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);Follow-up they want: throttle it with requestAnimationFrame, and use a CSS media query instead if it is purely visual.
48. What is debouncing and where do you use it in React?
Delay running a function until activity stops.
const debouncedQuery = useDebounce(query, 400);
useEffect(() => { if (debouncedQuery) search(debouncedQuery); }, [debouncedQuery]);Debounce for search inputs and autosave. Throttle (run at most once per interval) for scroll, resize and mousemove.
49. Do hooks work with static typing?
Yes, and well. useState infers from the initial value; you can be explicit with a generic.
const [user, setUser] = useState<User | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const [state, dispatch] = useReducer<Reducer<State, Action>>(reducer, init);Custom hooks get full inference. Return a tuple with as const so TypeScript keeps the positions typed rather than widening to a union array.
50. Do hooks cover everything classes do?
Almost. The gaps are getSnapshotBeforeUpdate, getDerivedStateFromError and componentDidCatch — the error boundary trio. Everything else has a hook equivalent.
51. How does hook performance compare to classes?
Slightly better. No class instances to construct, no method binding, smaller minified output, and the compiler can optimise plain functions more easily. The difference is small — the real wins are architectural.
Advanced
52. Explain reconciliation and the diffing algorithm.
Reconciliation is comparing the new Virtual DOM tree to the previous one to find the minimum set of DOM operations.
A general tree diff is O(n³). React gets O(n) with two heuristics:
- Different element types produce different trees — React destroys the old subtree entirely and rebuilds.
<div><Counter/></div>→<span><Counter/></span>unmountsCounterand loses its state. - Keys identify stable children — with keys React moves existing nodes instead of recreating them.
Same type = keep the node, patch only the changed attributes, recurse into children.
53. What is React Fiber?
The reconciler rewritten in React 16. It splits rendering into small units of work that can be paused, resumed, reprioritised or abandoned.
| Stack reconciler | Fiber | |
|---|---|---|
| Rendering | Synchronous recursion | Incremental |
| Interruptible | No | Yes |
| Priorities | None | Urgent vs transition |
Fiber is what makes useTransition, Suspense and concurrent rendering possible.
54. What is concurrent rendering?
React can work on multiple versions of the UI at once and keep the app responsive during a heavy render.
const [isPending, startTransition] = useTransition();
setQuery(value); // urgent — input updates now
startTransition(() => setResults(filter(value))); // interruptibleuseDeferredValue is the same idea for a value you receive rather than a setter you own.
55. What is automatic batching?
React groups multiple state updates into one re-render.
Before React 18, batching only happened inside React event handlers. Since React 18 it applies everywhere — promises, setTimeout, native event handlers.
setTimeout(() => {
setA(1);
setB(2); // React 17: 2 renders. React 18+: 1 render
}, 0);Opt out with flushSync(() => setA(1)).
56. What is useSyncExternalStore for?
Subscribing to a store outside React in a way that is safe under concurrent rendering — it guarantees a consistent snapshot so you never see a torn UI.
const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);Redux, Zustand and Jotai use it internally. You rarely call it directly.
57. What is useId and why not use it as a key?
Generates a unique, SSR-stable ID for accessibility attributes:
const id = useId();
<label htmlFor={id} /><input id={id} />Not a list key: it is stable per component instance, not per data item, so it can't tell React which item moved.
58. What are React Server Components?
Components that render only on the server. Stable in React 19 (December 2024).
- Zero JavaScript shipped to the client for that component
- Can
awaitdirectly — query a database, read a file, call an API with secrets - Cannot use state, effects, or browser APIs
- Cannot take event handlers as props
// Server Component — no "use client"
async function Users() {
const users = await db.user.findMany();
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}Server Components vs SSR: SSR renders your client components to HTML on the server and then ships their JavaScript for hydration. Server Components never ship that JavaScript at all.
59. What are Actions in React 19?
Functions that wrap async work and expose pending, error and result state through hooks — replacing the manual useState + onSubmit + loading + error pattern.
const [state, formAction, isPending] = useActionState(async (prev, formData) => {
const res = await save(formData.get("name"));
return res.ok ? { ok: true } : { error: "Failed" };
}, {});
<form action={formAction}>…</form>Related hooks: useFormStatus (pending state of the nearest parent form, no prop drilling) and useOptimistic (show the expected result instantly, reconcile later).
60. What is the use() API?
Reads a promise or a context during render.
const comments = use(commentsPromise); // suspends until resolved
const theme = use(ThemeContext);Unlike every other hook, use() can be called conditionally and inside loops, because it does not rely on call-order slots.
61. What is the React Compiler?
A build-time compiler, stable at v1.0 since October 2025, that automatically memoises components and values — doing what useMemo, useCallback and React.memo did by hand.
Requirement: your code must follow the Rules of React (no mutation during render, no side effects in the render body). eslint-plugin-react-compiler flags violations.
62. What is hydration and what causes a mismatch?
Hydration is the client attaching event listeners to server-rendered HTML instead of rebuilding the DOM.
A mismatch happens when the server HTML differs from the first client render. Usual causes: Date.now(), Math.random(), window/localStorage, locale-dependent formatting, and browser extensions injecting markup.
Fix: render the client-only value after mount, or use suppressHydrationWarning for genuinely unavoidable cases like timestamps.
63. SSR vs CSR vs SSG vs ISR?
| Rendered | Best for | |
|---|---|---|
| CSR | In the browser | Dashboards behind a login |
| SSR | Per request on the server | Personalised, always-fresh pages |
| SSG | At build time | Blogs, marketing, docs |
| ISR | At build, then revalidated in the background | Large content sites that change occasionally |
64. Why must state updates be immutable?
React compares the previous and next value with Object.is(). Mutating an object keeps the same reference, so the comparison says "unchanged" and React skips the re-render.
user.name = "Rahul"; setUser(user); // no re-render
setUser({ ...user, name: "Rahul" }); // re-rendersThe same shallow comparison drives React.memo, useMemo deps and useEffect deps.
65. What is a stale closure and how do you fix it?
A callback captured a variable from an old render and keeps reading that frozen value.
useEffect(() => {
const id = setInterval(() => setCount(count + 1), 1000); // count frozen at 0
return () => clearInterval(id);
}, []);Fixes: the updater form setCount(p => p + 1), add the value to the dependency array, or hold it in a ref if it must not re-trigger the effect.
66. When should you NOT use useEffect?
The React docs' own guidance — "You Might Not Need an Effect":
- Transforming data for rendering → compute it during render, or
useMemoif expensive - Handling a user event → put the logic in the event handler
- Resetting state when a prop changes → change the component's
keyinstead - Deriving state from props → derive it, don't mirror it into state
Effects are for synchronising with external systems: the DOM, network, timers, subscriptions.
67. How do you reset a component's state when a prop changes?
Change its key:
<ProfileForm key={userId} userId={userId} />A different key means a different element identity, so React unmounts the old instance and mounts a fresh one with clean state. Cleaner than a useEffect that manually resets every field.
68. How do you test a React component?
React Testing Library + Vitest or Jest. Test behaviour, not implementation.
test("shows error on empty submit", async () => {
render(<LoginForm />);
await userEvent.click(screen.getByRole("button", { name: /log in/i }));
expect(await screen.findByText(/email is required/i)).toBeInTheDocument();
});Query by role and accessible name, not by class or test ID, so the test also verifies accessibility. Mock the network with MSW rather than stubbing fetch.
69. How do you handle forms at scale?
For anything past a few fields, use react-hook-form with a Zod schema:
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema),
});It is uncontrolled internally, so typing does not re-render the whole form, and the Zod schema can be shared with your backend validation.
70. What accessibility issues come up in React?
- Missing
htmlFor/idpairs on labels <div onClick>instead of<button>— no keyboard access, no role- Modals without focus trapping or an Escape handler
- No
aria-liveregion, so screen readers miss async updates - Missing
alttext - Removing focus outlines without a visible replacement
Use eslint-plugin-jsx-a11y and test with keyboard only.
71. How would you debug a slow React app?
- React DevTools Profiler — record an interaction, find the expensive commits
- Highlight updates — see what re-renders as you click
- Chrome Performance tab — is it React, or layout thrash, or a long task?
- Bundle analyser — is it actually a network problem?
- Only then optimise: colocate state, memoise the hot path, virtualise long lists, split the bundle
- Measure again to confirm the fix
Never optimise before step 1. That answer alone lands well.
72. What would you check in a React code review?
- Missing or index-based keys
useEffectdependency arrays — missing deps, or effects that shouldn't exist- Missing cleanup on subscriptions and timers
- Direct state mutation
- Business logic inside components instead of hooks or utilities
- Values in Context that change every render (no
useMemo) dangerouslySetInnerHTMLwithout sanitisation (XSS)- Secrets in client-side code or
NEXT_PUBLIC_variables - Unhandled loading and error states
- Accessibility: semantic elements, labels, keyboard paths