Article written by Rishabh Dev Choudhary, under the guidance of Neeraj Jhawar, a Senior Software Development Manager and Engineering Leader. Reviewed by Manish Chawla, a problem-solver, ML enthusiast, and an Engineering Leader with 20+ years of experience.
React powers the frontends of Facebook, Instagram, Airbnb, and Netflix — and it remains the most widely used JavaScript UI library heading into 2026. For frontend engineers and full-stack developers, preparing for React interview questions is one of the highest-return investments before a technical interview.
This guide covers the full spectrum of React js interview questions and answers — from foundational concepts like JSX, props, and the virtual DOM, through React hooks and state management, to advanced topics including Fiber, portals, and code splitting. Whether you are a fresher or an experienced developer preparing for a senior role, each section is structured to match how interviewers actually test these topics.
The sections below move from beginner-friendly fundamentals to senior-level concepts, so you can read end-to-end or jump directly to the area you need most.
These foundational React interview questions cover the concepts every interviewer expects you to know — from JSX and components to props, state, and event handling.
React is an open-source JavaScript library built and maintained by Meta (Facebook) for building user interfaces. It is not a full framework — React handles only the view layer of an application, leaving routing, data fetching, and server communication to companion libraries. This distinction matters: React is to MVC what the ‘V’ alone is, not the whole pattern.
React’s three defining characteristics are its component-based architecture (UIs are built from small, reusable pieces), its declarative programming model (you describe what the UI should look like for a given state, and React handles the DOM updates), and its virtual DOM (a lightweight JavaScript representation of the real DOM that minimizes expensive browser updates).
| Feature | What It Means |
|---|---|
| Component-based | UI is built from isolated, reusable building blocks — each manages its own logic and rendering |
| Virtual DOM | React maintains a lightweight JS copy of the DOM; only changed nodes get updated in the browser |
| JSX | JavaScript syntax extension that lets you write HTML-like markup directly in JS — transpiled by Babel |
| One-way data flow | Data flows parent → child via props; children cannot directly modify parent state |
Because React is built entirely on JavaScript, interviewers assume fluency in JavaScript interview questions — closures, the event loop, prototypes, and this keyword — before probing React-specific patterns.
React’s dominance in frontend development comes from a set of practical advantages that compound as an application grows in complexity.
// Reusable Button Component function Button({ label, onClick, variant = 'primary' }) { return ( <button className={`btn btn-${variant}`} onClick={onClick} > {label} </button> ); } // Usage <Button label='Save' onClick={handleSave} variant='primary' /> <Button label='Cancel' onClick={handleCancel} variant='secondary' /> <Button label='Delete' onClick={handleDelete} variant='danger' />
React is the most widely used frontend library, but it is not without trade-offs — and interviewers respect candidates who give a balanced answer.
React’s feature set is intentionally minimal — it focuses on doing a few things extremely well rather than being a batteries-included framework.
JSX (JavaScript XML) is a syntax extension for JavaScript that lets you write HTML-like markup directly inside JavaScript files. It looks like HTML but compiles down to plain JavaScript function calls — specifically, React.createElement() invocations.
Browsers only understand plain JavaScript — they have no parser for JSX syntax. A transpiler called Babel processes JSX files during the build step and converts every JSX element into an equivalent React.createElement() call before the browser ever sees the code.
// JSX const element = ( <div className='card'> <h1>Hello, {name}</h1> <p>Welcome to React</p> </div> ); // Transpiled JavaScript const element = React.createElement( 'div', { className: 'card' }, React.createElement('h1', null, 'Hello, ', name), React.createElement('p', null, 'Welcome to React') );
Transpilation flow: JSX → Babel → React.createElement() → Virtual DOM → Real DOM
The Virtual DOM is a lightweight, in-memory JavaScript representation of the actual browser DOM. React keeps this copy in sync with what should be on screen, and uses it to calculate the most efficient way to update the real DOM when state changes — rather than re-rendering everything from scratch.
The process is called reconciliation and works in three steps: (1) when state or props change, React builds a new virtual DOM tree; (2) it diffs the new tree against the previous one using its reconciliation algorithm; (3) it applies only the specific changes (patches) to the real DOM.
| Feature | Real DOM | Virtual DOM |
|---|---|---|
| Update speed | Slow — full reflow/repaint on changes | Fast — in-memory diff before real DOM write |
| Manipulation cost | High — browser must recalculate layout | Low — plain JavaScript object comparison |
| Memory | Managed by browser | Managed by React in JavaScript heap |
| Direct access | Yes — via document.querySelector etc. | No — indirect through React’s reconciler |
Note on Shadow DOM: the Virtual DOM is a React-specific concept. The Shadow DOM is a completely separate web platform standard used by Web Components for style/DOM encapsulation. They are often confused but serve different purposes.
Components are the fundamental building blocks of a React application — each component is a self-contained, reusable piece of UI with its own logic and rendering output. An entire application is a tree of components composing into each other.
| Feature | Functional Component | Class Component |
|---|---|---|
| Syntax | JavaScript function that returns JSX | ES6 class extending React.Component |
| State handling | useState hook | this.state and this.setState() |
| Lifecycle | useEffect hook covers all lifecycle needs | Explicit lifecycle methods (componentDidMount etc.) |
| Hooks support | Full support | Not supported |
| Use case today | Modern default — all new code should use this | Legacy codebases and error boundaries only |
// Functional component function Greeting({ name }) { return <h1>Hello, {name}!</h1>; } // Class component class Greeting extends React.Component { render() { return <h1>Hello, {this.props.name}!</h1>; } }
Props (short for properties) are read-only data passed from a parent component down to a child component. They are React’s mechanism for one-way data flow — the parent controls what data the child receives, and the child cannot modify them.
// Parent passing props function App() { return <UserCard name='Alice' role='Engineer' avatarUrl='/alice.png' />; } // Child receiving props function UserCard({ name, role, avatarUrl }) { return ( <div className='card'> <img src={avatarUrl} alt={name} /> <h2>{name}</h2> <p>{role}</p> </div> ); }
Props are immutable — a child component must never modify its own props. If a child needs to communicate back to the parent, it does so by calling a callback function passed down as a prop (e.g., an onChange or onSubmit handler).
State is mutable data that is owned and managed by a component. Unlike props (which come from outside), state is internal to the component. When state changes, React re-renders the component to reflect the updated data in the UI.
// useState example import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
Every call to the setter function (setCount) schedules a re-render. React batches multiple state updates in event handlers for performance. State should hold only the minimum data needed to describe the UI — derived values should be computed, not stored.
| Feature | Props | State |
|---|---|---|
| Source | Passed from parent component | Created and owned by the component itself |
| Mutability | Immutable — child cannot change them | Mutable — updated via setState or setter function |
| Scope | Controlled by parent | Private to the component |
| Who controls it | Parent | The component itself |
| Triggers re-render? | Yes — when parent passes new props | Yes — when updated via the setter function |
In functional components, state is updated using the setter function returned by useState. In class components, state is updated using this.setState(). In both cases, state must never be modified directly — always use the provided API.
// Functional component — useState setter const [count, setCount] = useState(0); setCount(10); setCount(prev => prev + 1); // Class component — setState this.setState({ count: 10 }); this.setState(prev => ({ count: prev.count + 1 }));
React events are SyntheticEvents — cross-browser wrappers around the native browser event system. They normalize event properties across browsers so you write consistent event handling code regardless of which browser runs it.
// Form handling function Form() { function handleSubmit(event) { event.preventDefault(); console.log(event.target.value); } return ( <form onSubmit={handleSubmit}> <input onChange={e => console.log(e.target.value)} /> <button type='submit'> Submit </button> </form> ); }
React uses camelCase naming for events (onClick, onChange, onSubmit) rather than lowercase HTML attributes (onclick). In React 17+, event pooling was removed — SyntheticEvent objects are no longer reused, so you can safely access event properties asynchronously.
A controlled component is one where React owns the form input’s value — the input’s value is driven by state and every change goes through an onChange handler. An uncontrolled component stores its own value in the DOM and is accessed via a ref.
| Feature | Controlled Component | Uncontrolled Component |
|---|---|---|
| Data source | React state (useState) | DOM node (accessed via ref) |
| Form handling | onChange updates state on every keystroke | Read value from DOM on submit only |
| Validation | Easy — validate on every change | Harder — only at read time |
| React philosophy | Aligned — React is single source of truth | Bypasses React’s data flow |
| Use case | Most form inputs, validated fields | File inputs, legacy library integration |
// Controlled component const [email, setEmail] = useState(''); <input value={email} onChange={e => setEmail(e.target.value)} /> // Uncontrolled component const inputRef = useRef(null); <input ref={inputRef} defaultValue='initial' /> // Read value inputRef.current.value;
React applications are Single Page Applications (SPAs) — the browser loads one HTML file and JavaScript handles all navigation. Without a router, clicking a ‘link’ would require a full page reload from the server, losing all in-memory state. A client-side router intercepts navigation and swaps components without a page reload.
// React Router setup import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'; function App() { return ( <BrowserRouter> <nav> <Link to='/'>Home</Link> <Link to='/about'>About</Link> </nav> <Routes> <Route path='/' element={<Home />} /> <Route path='/about' element={<About />} /> <Route path='/users/:id' element={<UserProfile />} /> </Routes> </BrowserRouter> ); }
React Router v6 uses a Routes/Route nested structure — the router matches the current URL to a Route path and renders the corresponding element with no page reload or lost state. Patterns like nested routes, protected routes, and lazy-loaded route components are covered in React interview questions for experienced developers, where routing architecture is a frequently tested senior topic.
In class components, the render() method is the only required method. It examines this.props and this.state and returns what should be displayed — a React element, array, fragment, string, number, or null. In functional components, the function body itself serves as the render — whatever you return is what gets rendered.
// Class component class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } } // Functional component function Welcome({ name }) { return <h1>Hello, {name}</h1>; }
render() must be a pure function — it should not modify component state, write to external storage, or call APIs. It will be called on every state or prop change, so side effects belong in useEffect (functional) or lifecycle methods (class).
React Hooks let you use state and lifecycle features in functional components — here are the questions interviewers ask most.
Hooks are special functions that let you ‘hook into’ React state and lifecycle features from functional components — without writing a class. They were introduced in React 16.8 to solve three recurring pain points: complex class boilerplate, difficulty reusing stateful logic between components, and the confusion caused by HOC and render prop wrapper trees.
// BEFORE Hooks — class component class Timer extends React.Component { constructor(props) { super(props); this.state = { seconds: 0 }; } componentDidMount() { this.interval = setInterval( () => this.setState(s => ({ seconds: s.seconds + 1 })), 1000 ); } componentWillUnmount() { clearInterval(this.interval); } render() { return <p>Seconds: {this.state.seconds}</p>; } } // AFTER Hooks — functional component function Timer() { const [seconds, setSeconds] = useState(0); useEffect(() => { const id = setInterval( () => setSeconds(s => s + 1), 1000 ); return () => clearInterval(id); }, []); return <p>Seconds: {seconds}</p>; }
Core hooks: useState, useEffect, useContext, useReducer, useRef, useMemo, useCallback, useLayoutEffect, useId.
useState is the most fundamental React hook. It declares a state variable inside a functional component and returns a pair: the current value and a setter function that updates it and triggers a re-render.
// Signup form with lazy initialization import { useState } from 'react'; function SignupForm() { const [name, setName] = useState(''); const [email, setEmail] = useState(''); // Lazy initialization const [config, setConfig] = useState(() => JSON.parse( localStorage.getItem('config') ?? '{}' ) ); return ( <form> <input value={name} onChange={e => setName(e.target.value)} placeholder='Name' /> <input value={email} onChange={e => setEmail(e.target.value)} placeholder='Email' /> </form> ); }
💡 Important: Always use the functional update form when new state depends on previous state: setCount(prev => prev + 1) not setCount(count + 1). The second form uses a stale closure — in async contexts or batch updates, count may not reflect the latest value.
useEffect runs side effects after the component renders — data fetching, subscriptions, DOM manipulation, timers. The dependency array is the key to controlling when the effect re-runs.
| Dependency Array | When Effect Runs | Equivalent Lifecycle |
|---|---|---|
| No array (omitted) | After every single render | componentDidUpdate (runs every update) |
| Empty array: [] | Once after the initial mount only | componentDidMount |
| [dep1, dep2] | After mount + whenever dep1 or dep2 changes | componentDidUpdate with prev-prop comparison |
useEffect(() => { const controller = new AbortController(); fetch(`/api/users/${userId}`, { signal: controller.signal }) .then(r => r.json()) .then(data => setUser(data)); // Cleanup: cancel the fetch if userId changes before it completes return () => controller.abort(); }, [userId]); // re-runs when userId changes
⚠️ Pro Tip: Missing a dependency in the array causes stale closures — the effect captures the value at the time it was registered, not the latest value. This is the #1 useEffect bug interviewers test. The ESLint react-hooks/exhaustive-deps rule catches these automatically.
| Feature | useMemo | useCallback |
|---|---|---|
| What it memoizes | The return VALUE of a function | The FUNCTION REFERENCE itself |
| Returns | Computed value (any type) | Memoized function |
| Use case | Expensive calculations, filtered/sorted lists | Stable function refs passed as props to memoized children |
| Re-computes when | Dependency array values change | Dependency array values change |
// useMemo: cache the result of an expensive calculation const sortedList = useMemo( () => [...items].sort((a, b) => a.price - b.price), [items] // only re-sorts when items changes ); // useCallback: cache a function reference so child does not re-render needlessly const handleDelete = useCallback( (id) => dispatch({ type: 'DELETE', payload: id }), [dispatch] ); <ExpensiveChild onDelete={handleDelete} />; // stable ref = no unnecessary re-render
💡 Important Note: Only use useMemo and useCallback when you have a measured performance problem. Every memoization adds code complexity and has its own overhead. Premature optimization with these hooks often hurts more than it helps.
useRef creates a mutable ref object with a .current property that persists for the full lifetime of the component. Unlike useState, updating a ref does not trigger a re-render — making it the right tool for values you need to store but do not need to display.
import { useRef, useEffect } from 'react'; function SearchInput() { const inputRef = useRef(null); // Use case 1: DOM access — focus the input on mount useEffect(() => { inputRef.current.focus(); }, []); return <input ref={inputRef} placeholder='Search...' />; } // Use case 2: store previous value without causing re-render function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }, [value]); return ref.current; } // Use case 3: store a timer ID across renders const timerId = useRef(null); timerId.current = setTimeout(callback, 1000); clearTimeout(timerId.current);
Prop drilling is the problem of passing data through multiple intermediate components that do not need it, just so a deeply nested child can access it. useContext solves this by making a value available to any component in the tree without manual prop passing at each level.
// 1. Create the context const ThemeContext = React.createContext('light'); // 2. Provide a value at the top of the tree function App() { const [theme, setTheme] = useState('dark'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Layout /> {/* does not need theme */} </ThemeContext.Provider> ); } // 3. Consume anywhere in the tree — no prop drilling function Button() { const { theme } = useContext(ThemeContext); return <button className={`btn-${theme}`}>Click</button>; }
💡 Important: Context is not a replacement for Redux in complex apps. Every time the context value changes, ALL consumers re-render — even those that only use part of the value. For performance-critical global state with many consumers, a dedicated state manager is still the right choice.
A custom hook is a JavaScript function whose name starts with ‘use’ that calls other hooks internally. It lets you extract and share stateful logic between components without changing the component hierarchy — no HOCs, no render props, no wrapper hell.
// Custom hook: useFetch — reusable data fetching logic function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; setLoading(true); fetch(url) .then(r => r.json()) .then(d => { if (!cancelled) { setData(d); setLoading(false); }}) .catch(e => { if (!cancelled) { setError(e); setLoading(false); }}); return () => { cancelled = true; }; }, [url]); return { data, loading, error }; } // Consumed in any component — clean and readable function UserProfile({ userId }) { const { data, loading, error } = useFetch(`/api/users/${userId}`); if (loading) return <Spinner />; if (error) return <ErrorMessage error={error} />; return <ProfileCard user={data} />; }
The ‘use’ prefix is mandatory — it allows React to apply the rules of hooks to your custom hook and enables ESLint’s hooks linting rules to work correctly.
React enforces two rules for hooks. Violating either causes bugs that are difficult to trace — React relies on the order of hook calls being consistent across every render.
Rule 1 — Only call hooks at the top level: Never call hooks inside loops, conditions, or nested functions. The order of hook calls must be identical on every render so React can correctly associate each hook with its state.
Rule 2 — Only call hooks from React functions: Call hooks from functional components or from custom hooks. Never from regular JavaScript functions, class components, or event handlers.
// WRONG — hook inside a condition breaks call order function Component({ isLoggedIn }) { if (isLoggedIn) { const [user, setUser] = useState(null); // ERROR: conditional hook } } // CORRECT — hook always called; condition inside it function Component({ isLoggedIn }) { const [user, setUser] = useState(null); // always called if (!isLoggedIn) return null; // early return after hooks }
⚠️ Pro Tip: Calling a hook inside an if statement is the most common hooks mistake — and a direct interview question. React tracks hook state by call order, not by name. If the order changes between renders, React’s internal state map becomes misaligned.
Beyond basic state, interviewers test how you manage data flow across components — from Redux to HOCs to refs.
Redux is a predictable state container for JavaScript applications. It centralizes application state in a single immutable store, making state changes traceable, debuggable, and testable. The entire state tree lives in one place, and it can only be changed by dispatching actions that are processed by pure reducer functions.
Core concepts:
// Reducer function cartReducer(state = [], action) { switch (action.type) { case 'ADD_ITEM': return [...state, action.payload]; case 'REMOVE_ITEM': return state.filter(item => item.id !== action.payload.id); default: return state; } } // Dispatch an action store.dispatch({ type: 'ADD_ITEM', payload: { id: 1, name: 'Apple' } });
💡 Important Note: Know when NOT to use Redux: small apps with only local component state, apps where React Context covers all sharing needs, or apps already using Zustand or Jotai (lighter modern alternatives). Recommending Redux for a 3-page app is a red flag in interviews.
The following are the key features of Redux:
A pure component only re-renders when its props or state actually change. For class components, React.PureComponent implements shouldComponentUpdate with a shallow equality check. For functional components, React.memo() provides the same optimization.
// Functional — React.memo wraps the component const UserRow = React.memo(function UserRow({ name, score }) { return <tr><td>{name}</td><td>{score}</td></tr>; }); // UserRow only re-renders when name or score actually change // Class — extend React.PureComponent class UserRow extends React.PureComponent { render() { return <tr><td>{this.props.name}</td><td>{this.props.score}</td></tr>; } }
Important: the comparison is shallow — if you pass a new object or array literal as a prop on every render (e.g., style={{ color: ‘red’ }}), the reference changes each time and the memo/PureComponent check fails, causing a re-render anyway. Memoize objects and arrays passed as props with useMemo.
A Higher-Order Component (HOC) is a function that takes a component as an argument and returns a new, enhanced component with additional props or behaviour injected. It is a pattern for reusing component logic without modifying the original component.
// HOC: adds a loading spinner to any component function withLoading(WrappedComponent) { return function WithLoadingComponent({ isLoading, ...props }) { if (isLoading) return <Spinner />; return <WrappedComponent {...props} />; }; } const UserListWithLoading = withLoading(UserList); <UserListWithLoading isLoading={fetching} users={data} />
Synthetic events are React’s cross-browser wrapper around the native browser event system. They implement the same interface as native DOM events (stopPropagation, preventDefault, target, currentTarget) but work identically across all browsers, abstracting away browser-specific quirks.
function SearchBar() { function handleInput(event) { event.preventDefault(); console.log(event.target.value); // same in Chrome, Firefox, Safari console.log(event.type); // 'change' event.stopPropagation(); // prevent bubble } return <input onChange={handleInput} placeholder='Search' />; }
Pre-React 17, synthetic events were pooled — the event object was reused across events, so accessing it asynchronously returned null. React 17 removed pooling, making it safe to access event properties in setTimeout or Promises without calling event.persist() first.
Refs provide a way to access and interact with a DOM node or a React component instance directly — bypassing React’s declarative data flow for situations where imperative access is unavoidable.
// useRef (functional components) — recommended const inputRef = useRef(null); <input ref={inputRef} />; inputRef.current.focus(); // imperative DOM access // createRef (class components or one-off refs outside hooks) class TextInput extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); } focusInput() { this.inputRef.current.focus(); } render() { return <input ref={this.inputRef} />; } }
Common use cases for refs: programmatic focus management (accessibility), text selection, triggering animations, integrating third-party non-React DOM libraries (D3, maps), and measuring DOM element dimensions.
Single source of truth (SSOT) means that state lives in exactly one place and everything else derives from it. In React’s component model, the parent component owns the state and passes it down to children via props — children display it, but do not own copies of it. When the state in the parent changes, all children re-render with the updated value automatically.
// Parent owns the truth function Parent() { const [selectedId, setSelectedId] = useState(null); return ( <> <ItemList items={items} selectedId={selectedId} onSelect={setSelectedId} /> <ItemDetail itemId={selectedId} /> </> ); } // Both ItemList and ItemDetail always agree on which item is selected // because they both read from the same state in Parent
Redux extends this principle to the application level — the Redux store is the single source of truth for all shared application state.
These advanced ReactJS interview questions test deeper understanding of architecture, performance, and tooling — the topics that consistently separate intermediate from senior candidates in technical rounds.
Error boundaries are class components that catch JavaScript errors thrown anywhere in their child component tree during rendering, in lifecycle methods, or in constructors — and display a fallback UI instead of crashing the whole app. They implement getDerivedStateFromError (to update state and show fallback) and componentDidCatch (to log the error).
class ErrorBoundary extends React.Component { state = { hasError: false, error: null }; static getDerivedStateFromError(error) { return { hasError: true, error }; // triggers fallback UI } componentDidCatch(error, info) { logErrorToService(error, info.componentStack); // send to Sentry etc. } render() { if (this.state.hasError) return <h2>Something went wrong.</h2>; return this.props.children; } } // Usage: wrap any subtree <ErrorBoundary> <Dashboard /> </ErrorBoundary>
| Feature | React | Angular |
|---|---|---|
| Type | UI library — handles view layer only | Full MVC framework — includes routing, HTTP, forms, DI |
| Language | JavaScript (TypeScript optional) | TypeScript (built-in, enforced) |
| DOM approach | Virtual DOM with reconciliation | Real DOM with change detection zones |
| Data binding | One-way (props, callbacks) | Two-way (ngModel) and one-way |
| Learning curve | Lower — just JavaScript + JSX | Steeper — TypeScript, decorators, modules, RxJS |
| Ecosystem | Choose your own (Router, state, etc.) | Batteries included — all bundled in |
| Mobile | React Native | Ionic / NativeScript |
When to choose React: projects requiring a flexible, composable UI layer, teams comfortable with JavaScript ecosystem choices, or applications where you want to control every architectural decision. When to choose Angular: large enterprise projects benefiting from enforced conventions, teams that prefer TypeScript-first development, or projects that need a complete solution out of the box. For a detailed breakdown of both options from a hiring perspective, the Angular vs React face-off covers the full architectural and ecosystem comparison interviewers draw on.
React.StrictMode is a development-only tool that wraps your app (or a subtree) and activates additional checks and warnings. It has no effect on production builds — it does not render any visible UI.
// Wrap your app root to enable strict mode root.render( <React.StrictMode> <App /> </React.StrictMode> );
The double-render behaviour catches the most bugs — if your component produces different output on the second call, it has a side effect in a place that should be pure.
React Fiber is the complete rewrite of React’s core reconciliation algorithm, shipped in React 16. The problem it solved: the original reconciler was synchronous and stack-based — once it started rendering an update, it could not be interrupted. On large component trees, this blocked the main thread and made apps feel unresponsive.
Fiber breaks rendering work into small units called ‘fibers’ that can be paused, resumed, prioritized, or discarded. Each fiber represents one unit of work in the component tree. The scheduler can yield to the browser between fiber units, keeping the UI responsive even during large re-renders.
Portals let you render a component’s output into a different DOM node than its parent in the React tree. The component logically belongs to its parent (events bubble through the React tree normally) but visually renders in a different part of the DOM — escaping parent CSS constraints like overflow: hidden or z-index stacking contexts.
import { createPortal } from 'react-dom'; function Modal({ isOpen, onClose, children }) { if (!isOpen) return null; // Renders children into document.body, not into the parent div return createPortal( <div className='modal-overlay'> <div className='modal-content'> <button onClick={onClose}>×</button> {children} </div> </div>, document.body // target DOM node ); }
💡 Pro Tip: Events from portals still bubble through the React component tree — not the DOM tree. A click inside a modal portal will bubble up to the React parent that rendered the modal, even though the modal is a child of document.body in the real DOM. Interviewers love testing this nuance.
Code splitting is the practice of breaking your JavaScript bundle into smaller chunks that are loaded on demand rather than upfront. React.lazy enables component-level code splitting — the component’s module is only downloaded when the component is first needed, reducing initial page load time.
import { lazy, Suspense } from 'react'; // The Dashboard module is NOT included in the initial bundle // It downloads only when the user navigates to /dashboard const Dashboard = lazy(() => import('./Dashboard')); const Settings = lazy(() => import('./Settings')); function App() { return ( // Suspense displays the fallback while the chunk is loading <Suspense fallback={<LoadingSpinner />}> <Routes> <Route path='/dashboard' element={<Dashboard />} /> <Route path='/settings' element={<Settings />} /> </Routes> </Suspense> ); }
Route-based splitting is the most impactful pattern — each route’s code only loads when the user visits that route. For large applications, this can reduce the initial bundle from megabytes to tens of kilobytes, dramatically improving time-to-interactive.
Webpack is a module bundler that processes all of a React project’s source files — JavaScript, CSS, images, fonts — and outputs optimized bundles for the browser. It is the build tool that makes the modern React development experience possible.
Flow: Source files (JSX, CSS, assets) → Webpack → Loader pipeline (Babel, CSS loaders) → Optimized bundles (.js, .css)
Create React App and Vite abstract Webpack configuration behind sensible defaults, but understanding what Webpack does explains why the build step exists at all.
Babel is a JavaScript compiler (transpiler) that converts modern JavaScript and JSX into syntax that all browsers can run. It is the tool that makes JSX usable — browsers have no built-in understanding of JSX syntax, and Babel transforms it into React.createElement() calls during the build step.
In practice, Create React App and Vite configure Babel (or the faster SWC/esbuild alternatives) automatically — but knowing what Babel does explains why raw JSX files cannot be served directly from a web server.
A structured preparation path is the most efficient route from ‘I know some React’ to ‘I can answer confidently under pressure.’ Work through these steps in order:
Master Fundamentals: Understand JSX (why Babel matters), component types (functional vs class), props vs state, one-way data flow, and the virtual DOM + reconciliation cycle. These form the foundation every other React topic builds on.
Get Comfortable with Hooks: Deep-dive into useState, useEffect (especially the dependency array), useRef, useContext, useMemo, and useCallback. Then build two or three custom hooks from scratch (useFetch, useDebounce, useLocalStorage).
Understand State Management: Learn when React’s local state and Context API are sufficient, and when a dedicated library (Redux Toolkit, Zustand) is warranted. Know the Redux data flow: action → dispatch → reducer → store → UI.
Practice Building Components: Implement common UI patterns from scratch — controlled forms, filtered/sorted lists, pagination, modal dialogs, tabs. Time yourself. The ability to write working React code quickly is what phone screens measure.
Study Advanced Topics: React Fiber and concurrent features (Suspense, startTransition), error boundaries, portals, code splitting with React.lazy, and performance optimization (React.memo, useMemo, useCallback).
Mock Interviews and Whiteboard Practice: Practice explaining your code while writing it. Interviewers assess your thought process, not just the final answer. Practice answering ‘why did you choose X over Y’ for every decision you make.
Landing top software engineering roles isn’t just about writing code—it’s about showing your skills the right way. The Software Engineering Interview Prep program by Interview Kickstart helps you do exactly that. With in-depth training, live sessions, and personalized 1:1 support, you’ll master the technical concepts and learn how to present them confidently in any interview.
Learn directly from FAANG+ instructors with real hiring experience, practice in realistic mock interviews, and get actionable feedback to improve fast. The program also helps with career essentials like resume building, LinkedIn optimization, and personal branding. If you want to move into top software engineering roles with confidence, this prep program gives you the roadmap.
This guide has covered the full spectrum of React interview questions — from foundational concepts like JSX, components, and the virtual DOM, through React hooks and state management patterns, to advanced topics including Fiber, error boundaries, portals, and code splitting. Whether you are preparing as a fresher or reviewing for a senior frontend role, the questions and answers here reflect what React interviewers consistently assess across companies at every scale.
React’s continued dominance is well-documented: the State of JS survey has consistently ranked React as the most widely used frontend framework for several consecutive years, with adoption rates far ahead of its nearest competitors. Understanding React deeply translates directly into competitive advantage in technical interviews.
To continue your preparation with more specialized topics, explore Interview Kickstart’s React interview resources — built and reviewed by engineers who have conducted frontend interviews at FAANG and top tech companies.
React’s core concepts — components, props, state, and JSX — are learnable in a few weeks with solid JavaScript knowledge. Hooks and state management add complexity and take consistent practice to master. For interview purposes, depth on a focused set of topics (hooks, virtual DOM, component patterns) outperforms shallow coverage of the entire API.
React is a JavaScript library for building web UIs — it renders to the browser’s DOM. React Native is a mobile application framework that uses the same component model and hooks as React but renders to native iOS and Android UI components instead of HTML. Both share the same mental model; the main difference is the rendering target and the platform-specific components available.
Yes — particularly for error boundaries (which still require class components) and for questions about legacy codebases. Most modern React interview questions focus on functional components and hooks, but understanding class components shows range. Knowing the lifecycle method equivalents for hooks (componentDidMount → useEffect with []) is still expected at mid-to-senior levels.
The core list: components (functional vs class), JSX, props, state, the virtual DOM, event handling, controlled vs uncontrolled inputs, the main hooks (useState, useEffect, useContext, useRef, useMemo, useCallback), and basic state management with Context or Redux. For senior roles, add Fiber, error boundaries, portals, code splitting, and performance optimization patterns.
30–50 questions covering fundamentals through advanced is a realistic and sufficient target. More important than volume is the ability to write correct React code in real time and explain the trade-offs behind your decisions. Prepare by building small features, not just memorizing definitions.
Recommended Reads:
Attend our free webinar to amp up your career and get the salary you deserve.
Time Zone:
Master ML interviews with DSA, ML System Design, Supervised/Unsupervised Learning, DL, and FAANG-level interview prep.
Get strategies to ace TPM interviews with training in program planning, execution, reporting, and behavioral frameworks.
Course covering SQL, ETL pipelines, data modeling, scalable systems, and FAANG interview prep to land top DE roles.
Course covering Embedded C, microcontrollers, system design, and debugging to crack FAANG-level Embedded SWE interviews.
Nail FAANG+ Engineering Management interviews with focused training for leadership, Scalable System Design, and coding.
End-to-end prep program to master FAANG-level SQL, statistics, ML, A/B testing, DL, and FAANG-level DS interviews.
Get your enrollment process started by registering for a Pre-enrollment Webinar with one of our Founders.
Time Zone:
Join 25,000+ tech professionals who’ve accelerated their careers with cutting-edge AI skills
25,000+ Professionals Trained
₹23 LPA Average Hike 60% Average Hike
600+ MAANG+ Instructors
Webinar Slot Blocked
Register for our webinar
Learn about hiring processes, interview strategies. Find the best course for you.
ⓘ Used to send reminder for webinar
Time Zone: Asia/Kolkata
Time Zone: Asia/Kolkata
Hands-on AI/ML learning + interview prep to help you win
Explore your personalized path to AI/ML/Gen AI success
The 11 Neural “Power Patterns” For Solving Any FAANG Interview Problem 12.5X Faster Than 99.8% OF Applicants
The 2 “Magic Questions” That Reveal Whether You’re Good Enough To Receive A Lucrative Big Tech Offer
The “Instant Income Multiplier” That 2-3X’s Your Current Tech Salary