React Interview Questions and Answers 2026
1 Core Concepts
Q: What is React and why use it over vanilla JavaScript?
Answer: React is a JavaScript library for building user interfaces, developed by Meta. It uses a component-based architecture and a virtual DOM for efficient rendering.
Why React over vanilla JS:
- Declarative UI makes code predictable
- Component reusability reduces duplication
- Virtual DOM optimizes real DOM updates
- Rich ecosystem (routing, state management, testing)
- Strong TypeScript support
- Large community and Meta backing
Q: Explain the Virtual DOM and reconciliation.
Answer: The Virtual DOM is a lightweight JavaScript representation of the actual DOM. When state changes, React creates a new Virtual DOM tree, compares it with the previous snapshot (diffing algorithm), calculates minimal DOM operations needed, and batches these updates efficiently.
Reconciliation is this entire process. React 18+ uses a priority-based reconciler (Fiber architecture) enabling concurrent features.
Q: What's the difference between Real DOM and Virtual DOM?
| Real DOM | Virtual DOM |
|---|---|
| Direct HTML element manipulation | JavaScript object representation |
| Slow updates | Fast updates |
| Re-renders entire tree on change | Only updates changed elements |
| High memory usage | Lower memory footprint |
| No direct control over batching | Efficient diffing and batching |
2 Components & JSX
Q: Explain Class vs Functional Components (2026 perspective)
Answer: Functional components are now the standard. Class components are legacy.
Functional components are defined as functions, while class components are defined as classes that extend the React.Component class.
Here is an example of a functional component:
function UserProfile({ name, role }) { const [isOnline, setIsOnline] = useState(false); useEffect(() => { subscribeToStatus(role); return () => unsubscribeFromStatus(role); }, [role]); returnAnd here is an example of a class component:
class UserProfile extends React.Component { state = { isOnline: false }; componentDidMount() { } componentDidUpdate(prevProps) { } componentWillUnmount() { } render() { return- Simpler syntax, less boilerplate
- Hooks enable state and lifecycle features
- Better performance (no this binding, smaller bundle)
- Easier to test and tree-shake
Q: What are Pure Components and React.memo?
Answer: Both prevent unnecessary re-renders through shallow comparison.
Pure components are components that do not have any side effects and always return the same output given the same props.
React.memo is a higher-order component that memoizes a component so that it is not re-rendered unless its props change.
Here is an example of using React.memo:
const ExpensiveList = React.memo(({ items }) => { return items.map(item =>3 Hooks Mastery
Q: Explain useState and its nuances
useState is a hook that allows you to add state to functional components.
Here is an example of using useState:
const [count, setCount] = useState(0);You can update the state by calling the setCount function:
setCount(prev => prev + 1);Or:
setCount(c => c + 1); setCount(c => c + 1); 💡 Important: State updates in React 18 are automatically batched, even in promises and timeouts.Q: Deep dive into useEffect
useEffect is a hook that allows you to run side effects after rendering a component.
Here is an example of using useEffect:
useEffect(() => { const subscription = api.subscribe(id); return () => { subscription.unsubscribe(); }; }, [id]);useEffect Variations:
- No dependency array: runs after every render
- Empty []: runs once (mount only)
- With dependencies: runs when dependencies change
Q: useCallback vs useMemo — when to use which?
useCallback and useMemo are both hooks that allow you to memoize values, but they serve different purposes.
useCallback is used to memoize functions, while useMemo is used to memoize values.
Here is an example of using useMemo:
const sortedList = useMemo(() => { return items.sort((a, b) => a.name.localeCompare(b.name)); }, [items]);And here is an example of using useCallback:
const handleClick = useCallback((id) => { setSelected(prev => prev === id ? null : id); }, []); 💡 Rule of thumb: Only use when passing to memoized child components or as dependency to other hooks. Premature optimization hurts readability.Q: Explain useRef and its use cases
useRef is a hook that allows you to create a reference to a DOM element or a value that persists between renders.
Here is an example of using useRef:
function Timer() { const intervalRef = useRef(null); const countRef = useRef(0); const startTimer = () => { intervalRef.current = setInterval(() => { countRef.current += 1; console.log(countRef.current); }, 1000); }; useEffect(() => { return () => clearInterval(intervalRef.current); }, []); return ; }Use cases for useRef:
- DOM element references
- Storing mutable values without re-rendering
- Keeping previous values
- Holding timer/timeout IDs
Q: What are custom hooks? Give an example.
Custom hooks are functions that use other hooks to provide a specific functionality.
Here is an example of a custom hook:
function useDebounce(value, delay = 500) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debouncedValue; }And here is an example of using the custom hook:
function SearchComponent() { const [query, setQuery] = useState(''); const debouncedQuery = useDebounce(query, 300); useEffect(() => { if (debouncedQuery) fetchResults(debouncedQuery); }, [debouncedQuery]); }
4 React 18 & 19 Features
Q: What's new in React 18?
Answer:
- Concurrent rendering — interruptible rendering for better UX
- Automatic batching — groups state updates from anywhere
- Transitions — mark updates as non-urgent
- Suspense on server — streaming SSR
- New hooks: useId, useTransition, useDeferredValue, useSyncExternalStore
Here is an example of using the useTransition hook:
const [isPending, startTransition] = useTransition(); function handleSearch(input) { setInputValue(input); startTransition(() => { setSearchResults(filterData(input)); }); }And here is an example of using the useDeferredValue hook:
const deferredQuery = useDeferredValue(query); const results = useMemo(() => filterData(deferredQuery), [deferredQuery]);Q: What's expected in React 19?
Answer: New in 2026
- React Server Components (RSC) stable — zero client JS for server-only components
- Actions —
- useOptimistic — instant UI updates
- useFormStatus — form pending states
- Document metadata —
, tags directly in components - Asset loading — preload, preinit APIs
- Ref as a prop — no more forwardRef
Here is an example of using React Server Components:
async function BlogPost({ slug }) { const post = await db.posts.findBySlug(slug); return{post.title}
And here is an example of using the useOptimistic hook:
function LikeButton({ postId, initialLikes }) { const [optimisticLikes, addOptimisticLike] = useOptimistic( initialLikes, (state, newLike) => state + 1 ); async function handleLike() { addOptimisticLike(1); await likePost(postId); } return ; }Q: Explain React Server Components (RSC)
Answer: RSC allows components to run exclusively on the server. Server Components can access databases/files directly with zero bundle size. Client Components ('use client') are traditional interactive components.
5 State Management
Q: Compare state management solutions in 2026
| Solution | Use Case | When to Use |
|---|---|---|
| useState/useReducer | Component-level state | Simple local state |
| Context API | Low-frequency updates | Theme, auth, localization |
| Zustand | Medium complexity | Simple global state, no boilerplate |
| Jotai/Recoil | Atomic state | Fine-grained reactivity |
| Redux Toolkit | Complex state logic | Large apps with middleware needs |
| TanStack Query | Server state | API caching, synchronization |
| XState | State machines | Complex workflows, visualizations |
Zustand Example (Popular in 2026):
The following example demonstrates how to create a simple store using Zustand:
store.js
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
reset: () => set({ count: 0 }),
}));
6 Performance Optimization
Q: How do you optimize React app performance?
- Code splitting with React.lazy and dynamic imports
- Memoization — React.memo, useMemo, useCallback (judiciously)
- Virtualization — react-window or @tanstack/virtual for long lists
- Image optimization — lazy loading, WebP, responsive sizes
- Bundle analysis — monitor with bundle analyzer
- Avoid anonymous functions as props when possible
- Use useTransition for expensive updates
- Proper key usage in lists
- RSC for reducing client JS
- Tree shaking — ensure imports are tree-shakeable
For example, you can use code splitting to load components only when they are needed:
CodeSplitting.jsx
const Dashboard = lazy(() => import('./Dashboard'));
Q: What causes infinite re-renders and how to fix them?
Problem 1: Setting state in render creates an infinite loop.
Problem 2: Object/array dependency in useEffect creates new reference every render.
Problem 3: Inline function causing child re-render on every parent render.
Fixes: Move references outside or memoize them with useMemo and useCallback.
7 Testing
Q: How do you test React components?
For example, you can use the following test to verify that a login form submits with the correct user data:
LoginForm.test.jsx
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('submits form with user data', async () => {
const user = userEvent.setup();
const handleSubmit = vi.fn();
render(
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
await user.click(screen.getByRole('button', { name: /submit/i }));
await waitFor(() => {
expect(handleSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
});
8 Advanced Patterns
Q: Explain compound components pattern
The compound components pattern allows you to create a set of components that share state and work together to achieve a common goal. This is used in libraries like @headlessui/react and @radix-ui/react-tabs.
For example, you can create a tab component that uses the compound components pattern:
Tabs.jsx
function Tabs({ children, defaultTab }) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
);
}
Tabs.Tab = function Tab({ id, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
);
};
Q: What are Higher-Order Components? Are they still relevant?
These patterns are largely replaced by hooks but still appear in legacy codebases. A HOC is a function taking a component and returning an enhanced component.
Modern approach: Instead of HOCs, simply use the hook directly in your component. This is cleaner and avoids "wrapper hell."
9 Error Handling
Q: How do you handle errors in React?
For example, you can use an error boundary to catch and handle errors:
ErrorBoundary.jsx
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback ||
Something went wrong
;}
return this.props.children;
}
}
For async errors, use TanStack Query's built-in error handling or React Error Boundaries with react-error-boundary library.
10 System Design / Architecture
Q: How would you architect a large-scale React application?
Recommended Folder Structure:
Project Structure
src/
├── app/
│ ├── providers.tsx
│ └── router.tsx
├── features/
│ ├── auth/
│ ├── dashboard/
│ └── settings/
├── shared/
│ ├── components/
│ ├── hooks/
│ └── utils/
├── entities/
├── styles/
└── instrumentation/
Key Architectural Principles:
- Feature-based folder structure for scalability
- Shared UI kit (design system) for consistency
- TypeScript throughout for type safety
- API layer abstracted (TanStack Query or similar)
- Code splitting at route level for performance
- Feature flags for gradual rollout
- Comprehensive error boundaries at strategic levels
- Monitoring and observability (OpenTelemetry, Sentry)
- Testing strategy: unit (vitest), integration (RTL), e2e (Playwright)
Quick Tips for 2026 Interviews
- Know the React 19 changes well — RSC, Actions, useOptimistic are hot topics
- Understand Server Components vs Client Components — this is the new paradigm
- Be practical about optimization — don't memoize everything, profile first
- TypeScript proficiency is expected
- Accessibility (ARIA attributes, keyboard navigation) is increasingly important
- Testing philosophy — know what to test (behavior, not implementation)
- Frameworks — understand Next.js, Remix, or TanStack Start (full-stack React frameworks)
- Edge runtimes — understand streaming, partial prerendering
For expert-level guidance on React development, consider consulting the professionals at Chulbul Design.
Frequently Asked Questions
Question?
What is the most efficient way to handle state management in a React application?
Question?
How can I optimize the performance of my React application?
Question?
What are some best practices for testing React components?
Question?
How can I ensure that my React application is accessible to all users?