If you are preparing for a React.js interview, you’ve landed on the most complete guide. This article covers 100+ React interview questions and answers from beginner to advanced, including React hooks, performance optimization, coding exercises, and tricky conceptual questions.
Whether you’re a fresher or an experienced developer, by the end of this article, you’ll have the knowledge and confidence to crack your React interviews.
Table of Contents
Basic React Interview Questions and Answers
What is React and why use it?
Answer:
React is an open-source JavaScript library created by Facebook for building user interfaces.
- It focuses on the view layer (UI) of applications.
- Uses component-based architecture for reusability.
- Offers Virtual DOM for faster rendering.
- Ideal for single-page applications (SPAs).
Companies use React because it is fast, scalable, and developer-friendly.
Explain the difference between a library and a framework.
Answer:
- Library: Provides specific functionalities that developers can use. Example: React (only UI).
- Framework: Provides a complete structure for applications. Example: Angular (UI + routing + state management).
React is a library, giving developers flexibility to choose other tools (like Redux, Router, etc.).
What are components in React?
Answer:
Components are building blocks of a React app. They define parts of the UI.
- Functional components → written as functions.
- Class components → written as ES6 classes.
// Functional Component
function Greeting() {
return <h1>Hello World</h1>;
}
Difference between functional and class components.
Answer:
Functional Component | Class Component |
---|---|
Simple JS functions | ES6 Classes |
Use hooks for state & lifecycle | Use state and lifecycle methods |
Easier to read & test | More verbose |
Preferred in modern React | Older, but still supported |
What is JSX? Why is it used?
Answer:
JSX (JavaScript XML) is a syntax extension that lets you write HTML-like code inside JavaScript.
- Easier to read and maintain.
- Compiled into
React.createElement()
calls.
const element = <h1>Hello JSX!</h1>;
Difference between Real DOM and Virtual DOM.
Answer:
- Real DOM: Directly updates the browser DOM. Slow when large changes happen.
- Virtual DOM: React’s in-memory representation of DOM. Faster updates using diffing algorithm.
Virtual DOM improves app performance significantly.
How does Virtual DOM improve performance?
Answer:
React compares the previous Virtual DOM with the new one (diffing). Only the changed elements are updated in the Real DOM (reconciliation).
This avoids re-rendering the entire UI, making updates faster and efficient.
What is create-react-app
and why is it useful?
Answer:
create-react-app
(CRA) is a toolchain by Facebook to set up React apps quickly.
- Pre-configures Webpack, Babel, ESLint.
- Supports hot reloading.
- Zero configuration needed to start.
Example:
npx create-react-app my-app
What is the significance of the key
prop?
Answer:
Keys help React identify which items changed when rendering lists.
Without keys, React may re-render unnecessarily.
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
Explain props in React.
Answer:
Props (properties) are inputs passed from parent to child components. They are read-only.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
Difference between props and state.
Answer:
Props | State |
---|---|
Passed from parent | Managed inside component |
Immutable | Mutable |
Used for configuration | Used for dynamic data |
Read-only | Can be updated with setState or useState |
What is one-way data binding in React?
Answer:
React uses one-way data flow, meaning data flows from parent to child via props.
This makes the app more predictable and easier to debug.
Explain the concept of lifting state up.
Answer:
When multiple components need the same state, we lift the state up to their common parent.
function Parent() {
const [value, setValue] = useState('');
return (
<>
<Child1 value={value} setValue={setValue} />
<Child2 value={value} />
</>
);
}
What are fragments in React?
Answer:
Fragments let you group multiple elements without adding extra DOM nodes.
return (
<>
<h1>Title</h1>
<p>Description</p>
</>
);
Difference between React.Fragment
and <>
shorthand.
Answer:
React.Fragment
can take a key prop.<> </>
shorthand cannot.
Use full form when key is needed.
<React.Fragment key="1">
<p>Hello</p>
</React.Fragment>
What are default props in React?
Answer:
Default props provide fallback values when props are not passed.
function Button({ label }) {
return <button>{label}</button>;
}
Button.defaultProps = {
label: "Click Me"
};
What are propTypes? Why are they used?
Answer:
propTypes
help with type-checking props.
import PropTypes from 'prop-types';
function Welcome({ name }) {
return <h1>Hello {name}</h1>;
}
Welcome.propTypes = {
name: PropTypes.string.isRequired
};
Helps catch bugs early.
What are synthetic events in React?
Answer:
Synthetic events are React’s cross-browser wrapper around native DOM events.
- Works the same across all browsers.
- Provides the same API as native events.
<button onClick={(e) => console.log(e.type)}>Click</button>
How does React handle forms?
Answer:
React handles forms using controlled components.
- Form inputs are tied to component state.
function Form() {
const [name, setName] = useState('');
return (
<input value={name} onChange={e => setName(e.target.value)} />
);
}
Difference between controlled and uncontrolled components.
Answer:
Controlled | Uncontrolled |
---|---|
Form data managed by React state | Form data managed by DOM |
Uses value and onChange |
Uses ref |
Easier to validate | Less flexible |
Controlled components are the recommended approach in modern React.
Intermediate React Interview Questions and Answers
These questions are usually asked for 1–3 years of experience in React roles.
What is the component lifecycle in React?
Answer:
The lifecycle describes how a React component is created, updated, and destroyed.
Phases:
- Mounting – when component is added to the DOM.
- Updating – when state/props change.
- Unmounting – when component is removed.
Difference between mounting, updating, and unmounting phases.
Answer:
- Mounting →
constructor
,componentDidMount
, first render. - Updating → happens when props/state change. Methods:
shouldComponentUpdate
,componentDidUpdate
. - Unmounting → cleanup using
componentWillUnmount
(oruseEffect
cleanup in hooks).
Explain componentDidMount
with an example.
Answer:
componentDidMount
runs after first render. Commonly used for API calls.
class User extends React.Component {
state = { users: [] };
componentDidMount() {
fetch("/api/users")
.then(res => res.json())
.then(data => this.setState({ users: data }));
}
render() {
return <ul>{this.state.users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
}
What is shouldComponentUpdate
? Why use it?
Answer:
- Used in class components to control re-renders.
- Returns
true
orfalse
.
shouldComponentUpdate(nextProps, nextState) {
return nextProps.value !== this.props.value;
}
Helps in performance optimization.
Difference between componentWillUnmount
and useEffect cleanup
.
Answer:
componentWillUnmount
(class component) → cleanup before component is removed.useEffect
cleanup (functional component) → same purpose.
useEffect(() => {
const timer = setInterval(() => console.log("Tick"), 1000);
return () => clearInterval(timer); // cleanup
}, []);
How does React handle conditional rendering?
Answer:
Ways to conditionally render:
- if-else
- Ternary operator
- Logical && operator
{isLoggedIn ? <Dashboard /> : <Login />}
Different ways to style components in React.
Answer:
- Inline styles →
<div style={{ color: 'red' }}>
- CSS Stylesheets →
import './App.css'
- CSS Modules → Scoped styles
import styles from './App.module.css'
- Styled-components → CSS-in-JS
- TailwindCSS → Utility-first CSS
Explain inline styling vs CSS modules.
Answer:
- Inline styles → simple but no pseudo-classes.
- CSS Modules → automatically scopes class names, avoids conflicts.
import styles from './App.module.css';
<div className={styles.container}>Hello</div>
Difference between inline styles and styled-components.
Answer:
- Inline styles → static, simple.
- Styled-components → dynamic, allows conditional styling.
import styled from "styled-components";
const Button = styled.button`
background: ${props => props.primary ? "blue" : "gray"};
color: white;
`;
What is React Router?
Answer:
React Router is a library for routing in React apps.
- Allows navigation between pages without reloading.
- Provides components like
<Route>
,<Link>
,<Switch>
.
Explain <BrowserRouter>
and <HashRouter>
.
Answer:
- BrowserRouter → Uses HTML5 history API (
/about
). Best for modern apps. - HashRouter → Uses URL hash (
#/about
). Useful when server doesn’t support history API.
Difference between <Link>
and <NavLink>
in React Router.
Answer:
<Link>
→ Basic navigation.<NavLink>
→ Same as Link but adds active class when route matches.
<NavLink to="/home" activeClassName="active">Home</NavLink>
What is exact
in React Router?
Answer:
exact
ensures route matches only if the path is exact.
<Route exact path="/" component={Home} />
Without exact
, /
would also match /about
.
Explain nested routing in React.
Answer:
Nested routing allows routes inside another route.
<Route path="/dashboard" component={Dashboard}>
<Route path="profile" component={Profile} />
</Route>
How to pass props through routes?
Answer:
<Route path="/user/:id" render={(props) => <User {...props} name="John" />} />
What are dynamic routes in React?
Answer:
Dynamic routes use URL parameters.
<Route path="/product/:id" component={Product} />
Access with props.match.params.id
.
What is lazy loading in React Router?
Answer:
Lazy loading loads components on demand.
const About = React.lazy(() => import('./About'));
<Suspense fallback={<p>Loading...</p>}>
<Route path="/about" component={About} />
</Suspense>
Explain Redirect
in React Router.
Answer:
Redirect
automatically navigates to another route.
<Route path="/old-path">
<Redirect to="/new-path" />
</Route>
Difference between client-side routing and server-side routing.
Answer:
- Client-side → React Router handles navigation, faster.
- Server-side → Server returns new HTML on each request.
React mostly uses client-side routing.
What are portals in React? Give an example.
Answer:
Portals allow rendering a component outside its parent DOM hierarchy. Useful for modals, tooltips.
ReactDOM.createPortal( <ModalContent />, document.getElementById('modal-root') );
Top 40+ JSON Interview Questions and Answers PDF Updated
JavaScript foreach Function Examples
React Hooks Interview Questions and Answers
These are extremely common in modern interviews because hooks are now the standard way of writing React apps.
What are React Hooks? Why were they introduced?
Answer:
Hooks are functions that let you use state and lifecycle features in functional components.
- Introduced in React 16.8.
- Replaced class components for most use cases.
- Make code more concise and reusable.
Explain useState
with an example.
Answer:
useState
is used to add state variables in functional components.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count+1)}>+</button>
</div>
);
}
How does useEffect
work?
Answer:
useEffect
lets you perform side effects (API calls, subscriptions, timers).
Runs after render by default.
useEffect(() => {
document.title = `Count is ${count}`;
}, [count]);
Difference between useEffect
with []
, [deps]
, and no dependencies.
Answer:
useEffect(() => {})
→ Runs after every render.useEffect(() => {}, [])
→ Runs once on mount.useEffect(() => {}, [count])
→ Runs only whencount
changes.
Explain cleanup function in useEffect
.
Answer:
Used to clean up timers, subscriptions, or listeners.
useEffect(() => {
const timer = setInterval(() => console.log("Tick"), 1000);
return () => clearInterval(timer); // cleanup
}, []);
What is useContext
hook?
Answer:
useContext
allows you to use Context API directly.
const ThemeContext = React.createContext("light");
function App() {
const theme = useContext(ThemeContext);
return <p>Theme: {theme}</p>;
}
Explain useReducer
hook.
Answer:
useReducer
is an alternative to useState
for complex state logic.
function reducer(state, action) {
switch (action.type) {
case "increment": return { count: state.count + 1 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
Difference between useState
and useReducer
.
Answer:
- useState → Simple state, one variable.
- useReducer → Complex state, multiple actions, best for large apps.
Explain useRef
with example.
Answer:
useRef
creates a mutable reference that doesn’t cause re-renders.
const inputRef = useRef();
function FocusInput() {
return (
<>
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}
Difference between useRef
and createRef
.
Answer:
useRef
→ Same ref persists between renders (functional components).createRef
→ Creates new ref each render (class components).
What is useMemo
?
Answer:
useMemo
memoizes the result of an expensive computation.
const expensive = useMemo(() => slowFunction(count), [count]);
Difference between useMemo
and useCallback
.
Answer:
useMemo
→ memoizes value.useCallback
→ memoizes function.
const memoizedFn = useCallback(() => doSomething(a, b), [a, b]);
Explain custom hooks with an example.
Answer:
Custom hooks allow reusing logic.
function useFetch(url) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url).then(r => r.json()).then(setData);
}, [url]);
return data;
}
const users = useFetch("/api/users");
Why should we avoid using hooks inside loops?
Answer:
Hooks must be called in the same order on every render.
If used in loops, the order breaks, causing errors.
Explain rules of hooks.
Answer:
- Call hooks only at the top level (not inside loops, conditions).
- Call hooks only inside React functions.
Difference between useLayoutEffect
and useEffect
.
Answer:
useEffect
→ Runs after paint (non-blocking).useLayoutEffect
→ Runs before paint, blocks rendering.
UseuseLayoutEffect
for DOM measurements.
Explain useImperativeHandle
.
Answer:
Customizes the instance value exposed when using ref
.
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus()
}));
What is useTransition
hook in React 18?
Answer:
useTransition
marks state updates as non-urgent.
Improves UI responsiveness.
const [isPending, startTransition] = useTransition();
startTransition(() => setState(newValue));
Explain useDeferredValue
hook.
Answer:
Delays updating a value until less urgent tasks finish.
const deferredQuery = useDeferredValue(query);
Useful for search inputs.
How to fetch API data with hooks?
Answer:
Use useEffect
with useState
.
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("/api/users")
.then(res => res.json())
.then(setUsers);
}, []);
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Performance Optimization React Interview Questions and answers
This section focuses on why re-renders happen, how to avoid them, bundling strategies, list performance, image & network optimizations, and React 18 concurrency.
Why do React components re-render?
Answer: A component re-renders when:
- Its state changes.
- Its props (including context values) change.
- Its parent re-renders and passes new props (or referentially new objects/functions).
- Key changes for a list item.
Optimize by stabilizing props (memoize objects/functions), splitting components, and using
React.memo
.
What does React.memo
do and when should you use it?
Answer: React.memo(Component)
memoizes a function component so it skips re-render if shallow-equal props didn’t change.
const Price = React.memo(function Price({ value }) {
return <span>${value.toFixed(2)}</span>;
});
Use it for pure components that re-render frequently with the same props. Avoid slapping it everywhere-measure first.
How do useCallback
and useMemo
help performance?
Answer:
useCallback(fn, deps)
memoizes a function reference so child props stay stable.useMemo(factory, deps)
memoizes a computed value to avoid expensive recalculation.
const handleClick = useCallback(() => doThing(id), [id]);
const filtered = useMemo(() => items.filter(p), [items, p]);
Don’t overuse-each hook has its own cost. Use where referential stability matters.
What’s the difference between React.memo
and useMemo
?
Answer:
React.memo
prevents a child component from re-rendering when props are shallow-equal.useMemo
memoizes a value inside a component.
They’re complementary, not interchangeable.
How do you avoid re-rendering children when parent updates?
Answer:
- Wrap children with
React.memo
. - Pass stable props (memoize functions/objects).
- Lift expensive work out of render (to
useMemo
/selectors). - Split the parent into smaller memoized components.
const stableConfig = useMemo(() => ({ mode }), [mode]);
<Child onClick={handle} config={stableConfig} />
Why can inline objects/arrays/functions cause re-renders?
Answer: They are new references every render:
<Chart options={{ theme }} /> // new object each render
Fix by memoizing:
const options = useMemo(() => ({ theme }), [theme]);
<Chart options={options} />
How do you optimize large lists?
Answer:
- Virtualization: render only visible items (e.g.,
react-window
,react-virtualized
). - Windowing + sticky headers, dynamic sizing if needed.
- Ensure stable keys (unique ids).
- Avoid heavy item components; memoize rows.
import { FixedSizeList as List } from 'react-window';
<List height={600} itemSize={44} itemCount={items.length} itemData={items}>
{Row}
</List>
What is key choice impact on list performance?
Answer:
Using indexes as keys causes item DOM reuse bugs when items are inserted/removed/reordered. Prefer stable unique ids to minimize DOM ops and preserve state per row.
How to throttle expensive UI updates (search/scroll)?
Answer:
- Debounce change handlers.
- Throttle scroll/resize handlers.
- In React 18, mark non-urgent updates with
useTransition
.
const debounced = useMemo(() => debounce(setQuery, 300), []);
<input onChange={e => debounced(e.target.value)} />
How to optimize forms with many inputs?
Answer:
- Use uncontrolled inputs with refs where possible, or controlled but batched updates.
- Split the form into memoized sections.
- Avoid storing derived values in state; compute on demand with
useMemo
. - Use libraries with perf in mind (e.g.,
react-hook-form
).
What is code splitting, and how do you implement it?
Answer: Split bundles to load only what’s needed:
const Settings = React.lazy(() => import('./Settings'));
<Suspense fallback={<Spinner />}><Settings/></Suspense>
Also split by route with your router. Reduces TTI and improves Core Web Vitals.
How do dynamic imports differ from lazy components?
Answer:
- Dynamic import:
import('./mod')
returns a promise for any module. - React.lazy: specifically for default-exported React components to render with
Suspense
.
Use dynamic imports for helpers too:
const runHeavy = async () => (await import('./heavy')).run();
What is tree-shaking and how do you enable it?
Answer: Dead-code elimination at build time.
- Use ESM (
import
/export
), sideEffects: false in package.json when safe. - Prefer named imports:
import { pick } from 'lodash-es'; // allows shake
How do you analyze and reduce bundle size?
Answer:
- Use bundle analyzer (Webpack Bundle Analyzer / Source Map Explorer).
- Replace heavy libs with lighter alternatives (date-fns vs moment, lodash-es per-method).
- Use polyfill-on-demand (core-js with targets, or browserlist).
- Remove unused locales/assets.
How to optimize images in React apps?
Answer:
- Use responsive images (
srcset
,sizes
). - Serve WebP/AVIF.
- Lazy-load below-the-fold images (
loading="lazy"
). - Preload critical hero image.
- Use a CDN / image optimizer (Next.js
next/image
if applicable).
<img src="hero.avif" loading="eager" fetchpriority="high" alt="Hero" />
<img src="card.webp" loading="lazy" alt="Card" />
How to reduce unnecessary state and derived data?
Answer:
- Don’t store derived values in state; compute with
useMemo
. - Keep state close to where it changes.
- Use selectors for Redux/Zustand and memoize with
reselect
.
const total = useMemo(() => items.reduce((s,i)=>s+i.price,0), [items]);
What are Suspense and streaming SSR good for?
Answer:
- Suspense (client): boundary for loading fallbacks while fetching/lazy-loading.
- Streaming SSR (React 18): progressively send HTML chunks; show shell early, stream the rest for faster TTFB/TTI (often in frameworks like Next.js/Remix).
How do useTransition
and useDeferredValue
improve perceived performance?
Answer:
useTransition
marks updates as non-urgent (UI stays responsive).
const [isPending, startTransition] = useTransition();
startTransition(() => setFiltered(expensiveFilter(data, q)));
useDeferredValue
defers a rapidly changing value (like typing) used in expensive computations.
const deferredQ = useDeferredValue(q);
const results = useMemo(() => expensiveFilter(data, deferredQ), [deferredQ, data]);
How to optimize context usage?
Answer:
Context value changes re-render all consumers.
- Split contexts by concern (theme vs auth vs settings).
- Provide stable context values (
useMemo
). - Use selectors (e.g.,
use-context-selector
) to subscribe to slices.
const value = useMemo(() => ({ mode, setMode }), [mode]);
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
What production build optimizations matter most?
Answer:
- Ensure production mode (
NODE_ENV=production
) to enable minification and remove dev warnings. - Minify & compress (gzip/brotli).
- HTTP/2 or HTTP/3, long-term caching with content hashes, and proper cache-control.
- Prefetch/preload critical chunks, fonts, and above-the-fold CSS.
- Monitor Core Web Vitals (LCP, CLS, INP) and fix regressions via RUM + profiling.
Extra: Common Perf Pitfalls & Fixes (quick hits)
- Rendering huge tables → virtualize, paginate.
- Big JSON in state → keep normalized and memoized selectors.
- Recomputing filters/sorts →
useMemo
+ stable deps. - Heavy third-party charts/editors → lazy load and islands (render only when visible).
onScroll
jank → passive listeners,requestAnimationFrame
, throttle.
React Coding Interview Questions/ React Interview Coding Challenges
Counter (inc/dec/reset)
Build a counter with +1 / −1 / Reset.
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div style={{ display: "grid", gap: 8 }}>
<h3>{count}</h3>
<div style={{ display: "flex", gap: 8 }}>
<button onClick={() => setCount(c => c + 1)}>+1</button>
<button onClick={() => setCount(c => c - 1)}>-1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
</div>
);
}
Todo list (add/delete/toggle)
CRUD: add item, toggle done, delete item.
import { useState } from "react";
export default function Todos() {
const [text, setText] = useState("");
const [todos, setTodos] = useState([]);
const add = () => {
if (!text.trim()) return;
setTodos(t => [...t, { id: crypto.randomUUID(), text, done: false }]);
setText("");
};
const toggle = id => setTodos(ts => ts.map(t => t.id === id ? { ...t, done: !t.done } : t));
const remove = id => setTodos(ts => ts.filter(t => t.id !== id));
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} placeholder="Add todo" />
<button onClick={add}>Add</button>
<ul>
{todos.map(t => (
<li key={t.id}>
<label>
<input type="checkbox" checked={t.done} onChange={() => toggle(t.id)} />
<span style={{ textDecoration: t.done ? "line-through" : "none" }}>{t.text}</span>
</label>
<button onClick={() => remove(t.id)}>x</button>
</li>
))}
</ul>
</div>
);
}
Search filter
Filter a list as user types.
import { useMemo, useState } from "react";
const DATA = ["React", "Redux", "Hooks", "Context", "TypeScript"];
export default function SearchFilter() {
const [q, setQ] = useState("");
const filtered = useMemo(
() => DATA.filter(x => x.toLowerCase().includes(q.toLowerCase())),
[q]
);
return (
<div>
<input value={q} onChange={e => setQ(e.target.value)} placeholder="Search..." />
<ul>{filtered.map(x => <li key={x}>{x}</li>)}</ul>
</div>
);
}
Fetch & display (loading/error)
Fetch users, show loading/error states.
import { useEffect, useState } from "react";
export default function Users() {
const [state, setState] = useState({ data: [], loading: true, error: null });
useEffect(() => {
let alive = true;
fetch("https://jsonplaceholder.typicode.com/users")
.then(r => r.ok ? r.json() : Promise.reject(r.status))
.then(d => alive && setState({ data: d, loading: false, error: null }))
.catch(e => alive && setState(s => ({ ...s, loading: false, error: String(e) })));
return () => { alive = false; };
}, []);
if (state.loading) return <p>Loading…</p>;
if (state.error) return <p>Error: {state.error}</p>;
return <ul>{state.data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Form with validation
Email & password with inline validation and submit.
import { useState } from "react"; export default function LoginForm() { const [values, setValues] = useState({ email: "", password: "" }); const [touched, setTouched] = useState({}); const errors = { email: !values.email.includes("@") ? "Invalid email" : "", password: values.password.length < 6 ? "Min 6 chars" : "" }; const handleChange = e => setValues(v => ({ ...v, [e.target.name]: e.target.value })); const handleBlur = e => setTouched(t => ({ ...t, [e.target.name]: true })); const canSubmit = !errors.email && !errors.password; const submit = e => { e.preventDefault(); if (!canSubmit) return; alert(JSON.stringify(values, null, 2)); }; return ( <form onSubmit={submit} style={{ display: "grid", gap: 8 }}> <input name="email" placeholder="Email" value={values.email} onChange={handleChange} onBlur={handleBlur}/> {touched.email && errors.email && <small style={{ color: "crimson" }}>{errors.email}</small>} <input name="password" type="password" placeholder="Password" value={values.password} onChange={handleChange} onBlur={handleBlur}/> {touched.password && errors.password && <small style={{ color: "crimson" }}>{errors.password}</small>} <button disabled={!canSubmit}>Submit</button> </form>
); }
Modal (portal)
Show/hide modal using a portal.
import { useEffect } from "react";
import { createPortal } from "react-dom";
function Modal({ open, onClose, children }) {
useEffect(() => {
const onEsc = e => e.key === "Escape" && onClose();
if (open) document.addEventListener("keydown", onEsc);
return () => document.removeEventListener("keydown", onEsc);
}, [open, onClose]);
if (!open) return null;
return createPortal(
<div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,.5)" }} onClick={onClose}>
<div onClick={e => e.stopPropagation()} style={{ margin:"10% auto", background:"#fff", padding:16, width:360 }}>
{children}
<button onClick={onClose} style={{ marginTop: 12 }}>Close</button>
</div>
</div>,
document.body
);
}
export default function ModalDemo() {
const [open, setOpen] = React.useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Open Modal</button>
<Modal open={open} onClose={() => setOpen(false)}>
<h3>Hello from Modal</h3>
<p>Accessible, focus-trap omitted for brevity.</p>
</Modal>
</>
);
}
Custom hook: useLocalStorage
Persist state to localStorage
.
import { useEffect, useState } from "react";
export function useLocalStorage(key, initial) {
const [value, setValue] = useState(() => {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : initial;
});
useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]);
return [value, setValue];
}
// Usage
export default function ThemeToggle() {
const [mode, setMode] = useLocalStorage("mode", "light");
return <button onClick={() => setMode(m => m === "light" ? "dark" : "light")}>Mode: {mode}</button>;
}
Dropdown (keyboard + click outside)
Simple accessible dropdown with outside-click close.
import { useEffect, useRef, useState } from "react";
export default function Dropdown() {
const [open, setOpen] = useState(false);
const ref = useRef(null);
useEffect(() => {
const handler = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener("click", handler);
return () => document.removeEventListener("click", handler);
}, []);
return (
<div ref={ref} style={{ position: "relative", display: "inline-block" }}>
<button aria-expanded={open} onClick={() => setOpen(o => !o)}>Menu ▾</button>
{open && (
<ul role="menu" style={{ position:"absolute", marginTop:4, listStyle:"none", padding:8, background:"#fff", border:"1px solid #ddd" }}>
<li role="menuitem"><button onClick={() => alert("Profile")}>Profile</button></li>
<li role="menuitem"><button onClick={() => alert("Settings")}>Settings</button></li>
</ul>
)}
</div>
);
}
Pagination
Paginate an array (client-side).
import { useMemo, useState } from "react";
const data = Array.from({ length: 103 }, (_, i) => `Item ${i + 1}`);
export default function Pagination() {
const [page, setPage] = useState(1);
const size = 10;
const pages = Math.ceil(data.length / size);
const slice = useMemo(() => data.slice((page - 1) * size, page * size), [page]);
return (
<div>
<ul>{slice.map(it => <li key={it}>{it}</li>)}</ul>
<div style={{ display: "flex", gap: 8 }}>
<button disabled={page === 1} onClick={() => setPage(p => p - 1)}>Prev</button>
<span>{page}/{pages}</span>
<button disabled={page === pages} onClick={() => setPage(p => p + 1)}>Next</button>
</div>
</div>
);
}
Theme toggle (context)
Light/Dark theme with Context.
import React, { createContext, useContext, useMemo, useState } from "react";
const ThemeCtx = createContext(null);
export function ThemeProvider({ children }) {
const [mode, setMode] = useState("light");
const value = useMemo(() => ({ mode, setMode }), [mode]);
return <ThemeCtx.Provider value={value}>{children}</ThemeCtx.Provider>;
}
export const useTheme = () => useContext(ThemeCtx);
export default function ThemeButton() {
const { mode, setMode } = useTheme();
return <button onClick={() => setMode(m => m === "light" ? "dark" : "light")}>Theme: {mode}</button>;
}
Infinite scroll
Load more when sentinel enters viewport.
import { useEffect, useRef, useState } from "react";
export default function InfiniteList() {
const [items, setItems] = useState(Array.from({ length: 20 }, (_, i) => i));
const [page, setPage] = useState(1);
const ref = useRef(null);
useEffect(() => {
const io = new IntersectionObserver(([e]) => {
if (e.isIntersecting) setPage(p => p + 1);
}, { rootMargin: "200px" });
if (ref.current) io.observe(ref.current);
return () => io.disconnect();
}, []);
useEffect(() => {
if (page === 1) return;
const next = Array.from({ length: 20 }, (_, i) => page * 20 + i);
setTimeout(() => setItems(it => [...it, ...next]), 400); // simulate fetch
}, [page]);
return (
<>
<ul>{items.map(n => <li key={n}>Item {n}</li>)}</ul>
<div ref={ref} style={{ height: 1 }} />
</>
);
}
Timer/Stopwatch
Start/stop/reset stopwatch.
import { useEffect, useRef, useState } from "react";
export default function Stopwatch() {
const [ms, setMs] = useState(0);
const [running, setRunning] = useState(false);
const id = useRef(null);
useEffect(() => {
if (!running) return;
id.current = setInterval(() => setMs(m => m + 10), 10);
return () => clearInterval(id.current);
}, [running]);
return (
<div>
<h3>{(ms / 1000).toFixed(2)}s</h3>
<button onClick={() => setRunning(true)}>Start</button>
<button onClick={() => setRunning(false)}>Stop</button>
<button onClick={() => setMs(0)}>Reset</button>
</div>
);
}
Responsive navbar with Router
Simple SPA nav.
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
function Home() { return <h2>Home</h2>; }
function About() { return <h2>About</h2>; }
export default function App() {
return (
<BrowserRouter>
<nav style={{ display:"flex", gap:16 }}>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About/>} />
</Routes>
</BrowserRouter>
);
}
Drag & drop (HTML5)
Reorder list with drag & drop.
import { useState } from "react";
export default function DragDrop() {
const [items, setItems] = useState(["A","B","C","D"]);
const [dragIdx, setDragIdx] = useState(null);
const onDragStart = idx => setDragIdx(idx);
const onDrop = idx => {
setItems(arr => {
const copy = [...arr];
const [moved] = copy.splice(dragIdx, 1);
copy.splice(idx, 0, moved);
return copy;
});
setDragIdx(null);
};
return (
<ul>
{items.map((x,i) => (
<li key={x}
draggable
onDragStart={() => onDragStart(i)}
onDragOver={e => e.preventDefault()}
onDrop={() => onDrop(i)}
style={{ padding:8, border:"1px solid #ddd", marginBottom:6, cursor:"grab" }}>
{x}
</li>
))}
</ul>
);
}
Multi-step form (wizard)
Steps with next/back and final submit.
import { useState } from "react";
export default function Wizard() {
const [step, setStep] = useState(1);
const [data, setData] = useState({ name: "", email: "", age: "" });
const next = () => setStep(s => s + 1);
const back = () => setStep(s => s - 1);
return (
<div style={{ display: "grid", gap: 8, maxWidth: 320 }}>
<p>Step {step} / 3</p>
{step === 1 && <>
<input placeholder="Name" value={data.name} onChange={e => setData(d => ({ ...d, name: e.target.value }))} />
<button onClick={next} disabled={!data.name}>Next</button>
</>}
{step === 2 && <>
<input placeholder="Email" value={data.email} onChange={e => setData(d => ({ ...d, email: e.target.value }))} />
<div>
<button onClick={back}>Back</button>
<button onClick={next} disabled={!data.email.includes("@")}>Next</button>
</div>
</>}
{step === 3 && <>
<input placeholder="Age" value={data.age} onChange={e => setData(d => ({ ...d, age: e.target.value }))} />
<div>
<button onClick={back}>Back</button>
<button onClick={() => alert(JSON.stringify(data, null, 2))}>Submit</button>
</div>
</>}
</div>
);
}
Weather (API) with debounce
Search city, fetch weather (demo endpoint placeholder).
import { useEffect, useMemo, useState } from "react";
function debounce(fn, ms){ let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn(...a), ms); }; }
export default function Weather() {
const [q, setQ] = useState("");
const [city, setCity] = useState("");
const [data, setData] = useState(null);
const debouncedSetCity = useMemo(() => debounce(setCity, 500), []);
useEffect(() => { if (!city) return;
(async () => {
// Replace with your real API & key:
const res = await fetch(`https://api.weatherapi.com/v1/current.json?key=demo&q=${encodeURIComponent(city)}`);
if (res.ok) setData(await res.json());
})();
}, [city]);
return (
<div>
<input value={q} onChange={e => { setQ(e.target.value); debouncedSetCity(e.target.value); }} placeholder="City…" />
{data ? <p>{data.location.name}: {data.current.temp_c}°C</p> : <p>Type a city…</p>}
</div>
);
}
Autocomplete
Suggest top 5 matches while typing.
import { useMemo, useState } from "react";
const OPTIONS = ["React","Redux","Relay","Remix","Recharts","Recoil","Reselect","Router"];
export default function Autocomplete() {
const [q, setQ] = useState("");
const matches = useMemo(() =>
q ? OPTIONS.filter(o => o.toLowerCase().startsWith(q.toLowerCase())).slice(0,5) : [],
[q]);
return (
<div style={{ position:"relative", maxWidth: 240 }}>
<input value={q} onChange={e => setQ(e.target.value)} placeholder="Search library…" />
{matches.length > 0 && (
<ul style={{ position:"absolute", left:0, right:0, background:"#fff", border:"1px solid #ddd", listStyle:"none", margin:0, padding:4 }}>
{matches.map(m => (
<li key={m}><button onClick={() => setQ(m)} style={{ width:"100%", textAlign:"left" }}>{m}</button></li>
))}
</ul>
)}
</div>
);
}
Reusable Button (loading/disabled/variants)
Component with variant
, loading
, onClick
.
export function Button({ variant="primary", loading=false, disabled, children, ...rest }) {
const base = { padding:"8px 12px", borderRadius:6, border:"1px solid #ccc", cursor:"pointer" };
const styles = {
primary: { ...base, background:"#2563eb", color:"#fff", borderColor:"#1d4ed8" },
outline: { ...base, background:"#fff", color:"#111" }
}[variant];
return (
<button {...rest} disabled={disabled || loading} style={{ ...styles, opacity: (disabled || loading) ? .6 : 1 }}>
{loading ? "…" : children}
</button>
);
}
// Usage
// <Button onClick={save} loading={saving}>Save</Button>
Quiz app (score + next/back)
Multiple choice quiz, track score.
import { useMemo, useState } from "react";
const QUESTIONS = [
{ q: "React is a ___?", options: ["Library","Framework","Language"], answer: 0 },
{ q: "Hook for state?", options: ["useMemo","useState","useRef"], answer: 1 }
];
export default function Quiz() {
const [i, setI] = useState(0);
const [answers, setAnswers] = useState({});
const score = useMemo(() => Object.entries(answers)
.reduce((s,[k,v]) => s + (QUESTIONS[+k].answer === v ? 1 : 0), 0), [answers]);
const cur = QUESTIONS[i];
return (
<div>
<p>Q{i+1}: {cur.q}</p>
{cur.options.map((opt, idx) => (
<label key={idx} style={{ display:"block" }}>
<input type="radio" name={`q${i}`} checked={answers[i]===idx} onChange={() => setAnswers(a => ({ ...a, [i]: idx }))}/>
{opt}
</label>
))}
<div style={{ marginTop: 8 }}>
<button disabled={i===0} onClick={() => setI(n => n-1)}>Back</button>
<button disabled={i===QUESTIONS.length-1} onClick={() => setI(n => n+1)}>Next</button>
</div>
<p>Score: {score}/{QUESTIONS.length}</p>
</div>
);
}
File upload with preview
Choose an image and preview it.
import { useState } from "react";
export default function FilePreview() {
const [src, setSrc] = useState("");
const onChange = e => {
const file = e.target.files?.[0];
if (!file) return;
const url = URL.createObjectURL(file);
setSrc(url);
};
return (
<div style={{ display:"grid", gap: 8 }}>
<input type="file" accept="image/*" onChange={onChange} />
{src && <img src={src} alt="preview" style={{ maxWidth: 240, borderRadius: 8 }} />}
</div>
);
}
React System Design and Best Practices
- Use component-driven design (small reusable components).
- Organize files by feature modules.
- Use error boundaries for reliability.
- Use lazy loading for routes and components.
- Follow accessibility (a11y) best practices.
React FAQs
Q1: Is React a framework or library?
React is a library, not a framework.
Q2: Can React work without JSX?
Yes, JSX is optional, but makes code easier to read.
Q3: How do you handle SEO in React apps?
Use Server-Side Rendering (SSR) with Next.js or pre-rendering tools.
Conclusion
React interviews test not only your knowledge of core concepts (state, props, hooks, lifecycle) but also your ability to write optimized, real-world React code.
This guide covered 100+ React interview questions with answers and coding examples, enough to prepare you for both fresher and senior-level interviews.
- Want deeper coverage? Check out our dedicated guide: 60+ Must-Know React Testing Interview Questions and Answers (2025)
- Bookmark this page and revisit before your next interview.
- Keep practicing by building small React projects to strengthen your concepts.