React Interview
50 Most Probable React Interview Questions (2025) — with thorough answers
Curated and ordered by likelihood based on common interview patterns for React engineers in 2024–2025. Answers are practical, detailed, and include pitfalls, examples, and “when to use” guidance.
1) Explain useState and useEffect. What are the most common pitfalls with useEffect?
Detailed answer:
- useState lets you hold local component state. State updates are asynchronous and batched during event handlers.
- useEffect runs after render. Control execution with a dependency array: none (every render), [] (mount/unmount), [deps] (on change). Cleanup returned function runs before re-running and on unmount.
- Top pitfalls:
- Missing dependencies (stale closures). Fix: include all used values or refactor into a custom hook.
- Infinite loops (setting state unconditionally inside the effect). Fix: guard with conditions or correct dependencies.
- Leaks (not cleaning subscriptions/timers). Always return cleanup.
- Fetch race conditions. Use AbortController or “mounted” guard.
Example (fetch with abort):
useEffect(() => {
const ac = new AbortController();
(async () => {
const res = await fetch(`/api/users?q=${q}`, { signal: ac.signal });
const data = await res.json();
setUsers(data);
})().catch(() => {});
return () => ac.abort();
}, [q]);
2) How does React rendering work? What triggers re-renders and how do you avoid unnecessary ones?
Detailed answer:
- A component re-renders when its state changes or when its parent re-renders and passes new props (new identities).
- React compares element trees (reconciliation) and updates the DOM minimally.
- To avoid unnecessary re-renders:
- Use React.memo for pure child components.
- Stabilize function and object props via useCallback/useMemo.
- Split large contexts; avoid putting high-churn data in a single provider.
- Derive UI from state instead of duplicating state.
- Virtualize long lists (react-window).
3) What are keys in lists and why are they critical?
Detailed answer:
- Keys give identity to list items between renders. React uses keys to match old vs new children efficiently.
- Use stable unique IDs; avoid array indexes when items can be reordered/inserted/removed. Wrong keys cause state mix-ups (e.g., inputs losing focus).
4) Controlled vs uncontrolled inputs. When to choose each?
Detailed answer:
- Controlled: value is driven by state and updated via onChange. Best for validation, conditional UI, and single source of truth.
- Uncontrolled: DOM holds value (ref). Simpler for basic forms, large forms where full control adds overhead, or file inputs.
// Controlled
const [v, setV] = useState('');
<input value={v} onChange={e => setV(e.target.value)} />;
// Uncontrolled
const r = useRef(null);
<input ref={r} defaultValue="hi" />;
5) What is Context? When to use it vs Redux Toolkit, Zustand, or React Query?
Detailed answer:
- Context removes prop drilling for relatively stable global values (theme, locale, auth user, feature flags).
- Avoid using one giant context for rapidly changing values (causes wide re-renders). Split contexts or use a state library.
- Redux Toolkit: predictable, centralized state with good devtools; best for complex client state and cross-cutting concerns.
- Zustand/Jotai/Recoil: minimal APIs and fine-grained subscriptions; good for simpler or performance-focused stores.
- React Query/SWR: manages server state (caching, refetching, retries). Don’t put server data in Redux by default.
6) How and when to use useMemo and useCallback? What can go wrong?
Detailed answer:
- useMemo memoizes expensive computations; useCallback memoizes function identities passed to children.
- Use them when profiling shows unnecessary re-renders or costly calculations.
- Pitfalls: overuse (adds complexity), wrong dependencies (stale results), and assuming they always improve performance. Measure first.
7) Data fetching in React: useEffect vs React Query/SWR. Which is better and why?
Detailed answer:
- Raw useEffect is fine for simple fetches but you must handle caching, stale data, retries, deduplication, background revalidation, and error states manually.
- React Query/SWR handle these concerns out of the box, improving UX and reliability. They also support optimistic updates and infinite queries.
8) React Router basics: routing, nested routes, and code splitting.
Detailed answer:
- Client routing manages URL <-> UI mapping without full page reloads.
- Use nested routes to mirror UI layout; lazy-load route components with React.lazy + Suspense to reduce initial bundle size.
const Users = lazy(() => import('./Users'));
<Suspense fallback={<Spinner />}>
<Routes>
<Route path="/users" element={<Users />} />
</Routes>
</Suspense>;
9) Performance optimization checklist for React apps.
Detailed answer:
- Memoize pure child components (React.memo).
- Stabilize props (useCallback/useMemo) only when needed.
- Virtualize long lists; paginate where possible.
- Code-split heavy routes/components.
- Debounce/throttle high-frequency events.
- Use production builds and analyze bundles (source-map-explorer, webpack-bundle-analyzer).
10) Error Boundaries: what they catch and what they don’t.
Detailed answer:
- Class components with getDerivedStateFromError/componentDidCatch catch render-time errors in their subtree and show a fallback UI.
- They do not catch: event handler errors (handle with try/catch), async errors outside render, or SSR errors.
11) useRef: practical use cases beyond DOM access.
Detailed answer:
- Hold mutable values that persist across renders without causing re-renders (e.g., timers, previous values, counters).
- Access DOM nodes for focus/measure/integrations.
- Avoid using refs as state: changes to ref.current don’t trigger renders.
12) useReducer: when it’s superior to multiple useState calls.
Detailed answer:
- Prefer useReducer when state transitions are complex or interdependent, or when you want to centralize logic and make updates explicit.
- Works well with Context to build a mini-store.
13) Suspense and React.lazy: what problems do they solve?
Detailed answer:
- React.lazy defers loading component code; Suspense shows a fallback while loading. This reduces initial bundle sizes and improves perceived performance.
- For data fetching Suspense requires a compatible data layer (frameworks or libraries that throw promises). Otherwise use SWR/React Query.
14) useLayoutEffect vs useEffect.
Detailed answer:
- useLayoutEffect runs synchronously after DOM mutations but before paint; use for measuring layout and avoiding flicker.
- Prefer useEffect when possible to keep UI responsive.
15) Prop drilling: costs and alternatives.
Detailed answer:
- Deeply passing props through intermediate components increases coupling and churn. Alternatives:
- Context (split contexts to limit re-render scope)
- Composition (render props/children)
- Localized stores (Zustand/Redux slices near features)
16) Common anti-patterns that cause bugs or poor performance.
Detailed answer:
- Mutating state directly; not creating new objects/arrays.
- Duplicating derived state (e.g., storing both value and value.length in state).
- Using indexes as keys in dynamic lists.
- Doing heavy work in render instead of memoized selectors.
17) Testing React components effectively (RTL + Jest).
Detailed answer:
- Test behavior from the user’s perspective (queries by role/label/text), not implementation details.
- Mock network with MSW; avoid brittle snapshots.
- Cover accessibility (e.g., focus moves, ARIA attributes) and edge cases.
18) TypeScript with React: typing props, events, and hooks.
Detailed answer:
- Prefer explicit Prop types and FC inference for children as needed.
- Type event handlers correctly:
React.ChangeEvent<HTMLInputElement>. - Type custom hooks’ return objects and params for clarity.
type ButtonProps = { onClick: () => void; disabled?: boolean };
const Button: React.FC<ButtonProps> = ({ onClick, disabled, children }) => (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
19) Accessibility (a11y) must-knows for React devs.
Detailed answer:
- Use semantic HTML first; ARIA only to fill gaps.
- Ensure keyboard navigation and visible focus states.
- Manage focus when opening modals/alerts; restore focus on close.
- Prefer labels tied to inputs; test with screen readers and axe.
20) SSR vs SSG vs CSR; hydration; common pitfalls.
Detailed answer:
- CSR: render entirely on client; simplest but slower first paint/SEO.
- SSR: render HTML on server per request; good SEO and time-to-first-byte.
- SSG: pre-render at build time; super fast for static content.
- Hydration attaches event listeners to server-rendered HTML; mismatches occur if client markup differs (avoid random values/time-based output during SSR).
21) Concurrency features: useTransition and useDeferredValue.
Detailed answer:
- useTransition marks updates as non-urgent, allowing React to keep UI responsive (e.g., typing) while deferring expensive re-renders.
- useDeferredValue defers recalculating derived values (like filtered lists) during fast-typing.
const [isPending, startTransition] = useTransition();
startTransition(() => setQuery(q));
22) React Server Components (RSC) vs Client Components (as used in Next.js).
Detailed answer:
- RSC render on the server, can access server resources directly, and send a serialized component tree to the client, reducing bundle size.
- Client Components run in the browser and manage interactivity.
- Rule of thumb: prefer RSC for data-heavy, non-interactive pieces; mark interactive parts as client. Mind boundaries and props constraints (no functions across RSC -> Client).
23) Form management at scale: React Hook Form vs Formik.
Detailed answer:
- React Hook Form leverages uncontrolled inputs and refs for performance, minimal re-renders, and tiny bundle size; great DX with resolvers (Yup/Zod).
- Formik is controlled by default and more render-heavy for large forms but familiar API.
- Prefer RHF for performance-sensitive, large forms.
24) Debounce and throttle in React.
Detailed answer:
- Debounce: delay invocation until user stops typing (search box).
- Throttle: invoke at most once per interval (scroll handler).
const debounced = useMemo(() => debounce(doSearch, 300), [doSearch]);
useEffect(() => () => debounced.cancel(), [debounced]);
25) Optimistic updates and rollback.
Detailed answer:
- Update UI immediately to feel responsive; if server fails, roll back.
- With React Query:
onMutate,onError(rollback),onSettled(refetch).
26) Handling modals, portals, and focus trapping.
Detailed answer:
- Use React Portals to render modals outside normal DOM hierarchy but keep React tree.
- Trap focus inside the modal, restore focus on close, and handle Escape key.
27) Forwarding refs and useImperativeHandle.
Detailed answer:
- forwardRef lets parent obtain child’s DOM node or imperative API.
- useImperativeHandle limits what methods are exposed.
const Input = forwardRef((props, ref) => {
const inner = useRef(null);
useImperativeHandle(ref, () => ({ focus: () => inner.current?.focus() }));
return <input ref={inner} {...props} />;
});
28) Styling strategies: CSS Modules, Tailwind, styled-components, CSS-in-JS.
Detailed answer:
- CSS Modules: scoping via generated class names; good default.
- Tailwind: utility-first, fast iteration, small runtime.
- styled-components/Emotion: themeable, co-locate styles with components; consider SSR performance and critical CSS.
29) Large list rendering and virtualization.
Detailed answer:
- Rendering thousands of DOM nodes is slow. Use react-window/react-virtualized to render only visible items.
- Ensure rows have stable heights or use dynamic measuring.
30) Security basics for React apps (XSS, CSRF).
Detailed answer:
- Avoid dangerouslySetInnerHTML unless sanitized.
- Prefer HttpOnly cookies for tokens; protect APIs from CSRF via same-site cookies/CSRF tokens.
- Validate/encode user-provided content.
31) Environment variables and configuration per environment.
Detailed answer:
- Use .env.* files. Remember client-side envs must be prefixed per tool (e.g., NEXTPUBLIC / VITE_).
- Don’t ship secrets to clients; keep them server-side.
32) Code organization for scale: feature folders and boundaries.
Detailed answer:
- Group by feature (components, hooks, store, tests, styles per feature).
- Keep shared primitives in a UI or core library.
- Maintain stable public APIs for cross-feature imports.
33) Hydration mismatches: causes and fixes.
Detailed answer:
- Causes: non-deterministic output (Date.now(), Math.random()) during SSR, using window in SSR, locale differences.
- Fixes: gate client-only logic with useEffect or dynamic import
ssr: false; use consistent locale and deterministic rendering.
34) Handling errors in async code and surfacing to users.
Detailed answer:
- Wrap async actions with try/catch; show toasts or inline errors.
- Centralize error handling (interceptors). Log to monitoring (Sentry) with context.
35) Bundle size control and tree-shaking.
Detailed answer:
- Prefer ES modules and named imports; avoid importing whole libraries.
- Analyze bundles; replace heavy libs with lighter alternatives.
- Lazy-load rarely used code paths.
36) SEO considerations in React/Next.js.
Detailed answer:
- Use SSR/SSG for crawlable content. Provide meta tags, Open Graph, structured data. Ensure canonical URLs and sitemaps. Avoid client-only rendering for critical content.
37) Internationalization (i18n) patterns.
Detailed answer:
- Use react-i18next/FormatJS. Lazy-load locales, handle plurals and interpolation. Keep keys stable and tested, and ensure RTL support.
38) WebSockets/realtime updates.
Detailed answer:
- Connect in a custom hook; clean up on unmount. Consider backoff retries and presence/room abstractions. Keep state updates batched/debounced to avoid thrashing.
39) Pagination and infinite scroll (cursor vs offset).
Detailed answer:
- Prefer cursor-based pagination for reliability under mutations. For infinite scroll, prefetch next page early and guard against duplicate requests.
40) Animations: CSS vs JS libraries.
Detailed answer:
- Prefer CSS for simple transitions; use Framer Motion for complex animations and orchestration. Ensure reduced motion accessibility preference is respected.
41) Migrating from class components to hooks.
Detailed answer:
- Map lifecycle methods to useEffect/useLayoutEffect. Replace this.state with useState/useReducer. Replace refs with useRef. Be mindful of dependency arrays and stale closures.
42) File uploads: progress, cancellation, and large files.
Detailed answer:
- Use FormData and XHR/fetch with progress (XHR easier for progress). Support abort with AbortController. For large files, use chunking and resumable protocols.
43) Portals and z-index issues in overlays.
Detailed answer:
- Render overlays via createPortal to escape stacking contexts. Ensure backdrop click handling and keyboard accessibility.
44) Using selectors and memoization for derived state.
Detailed answer:
- Don’t store derived values in state. Compute via useMemo or library selectors (reselect) to avoid recomputations and keep state minimal.
45) Forms: validation strategies (Yup/Zod), UX, and performance.
Detailed answer:
- Validate on blur or submit; show inline accessible messages tied to inputs. With RHF, use resolvers for schema validation and avoid re-renders by using uncontrolled inputs.
46) Common useEffect recipes (subscriptions, intervals, event listeners).
Detailed answer:
- Always clean up: remove listeners, clear timers. Keep dependencies precise. Extract reusable patterns into custom hooks (e.g., useInterval, useEventListener).
47) Integrating third-party widgets (maps, charts).
Detailed answer:
- Initialize in useEffect after mount, store instance in a ref, update imperatively on prop changes, and dispose on unmount. Avoid direct DOM manipulation outside the library’s API.
48) CI/CD and quality gates for React apps.
Detailed answer:
- Run type checks, lint, unit/integration tests in CI; measure Lighthouse budgets and bundle sizes. Block regressions with thresholds.
49) Debugging tricky re-render loops.
Detailed answer:
- Use React DevTools “why did this render” plugins or console tracing of props identities. Stabilize callbacks/objects, and split components to isolate churn.
50) Top 5 React interview coding prompts to prepare for.
Detailed answer:
- Debounced search box with loading state and race cancellation.
- Infinite scrolling list with virtualization.
- Form with validation and dependent fields (RHF or controlled inputs).
- A custom hook (e.g., useLocalStorage or useInterval) with tests.
- Global state slice (Redux Toolkit/Zustand) with optimistic update.
hello30