Advanced React Interview Questions
Questions for senior React developers.
Q1: Explain React Fiber Architecture
Answer: Fiber is React's reconciliation algorithm (React 16+) that enables incremental rendering.
Key features:
- Incremental Rendering: Work can be split into chunks
- Pause & Resume: Work can be paused and resumed
- Priority: Different priorities for different updates
- Concurrent Mode: Multiple state updates without blocking
Fiber is a unit of work. Each fiber represents a component instance with:
- Type, key, props
- State, memoized output
- Links to parent, child, sibling fibers
Q2: What is Concurrent Mode and Suspense?
Answer:
Concurrent Mode allows React to work on multiple tasks simultaneously, keeping the app responsive.
1// Concurrent features
2import { startTransition, useTransition, useDeferredValue } from 'react'
3
4function Search() {
5 const [query, setQuery] = useState('')
6 const [isPending, startTransition] = useTransition()
7
8 const handleChange = (e) => {
9 // Urgent: update input
10 setQuery(e.target.value)
11
12 // Non-urgent: update results
13 startTransition(() => {
14 setSearchResults(e.target.value)
15 })
16 }
17}Suspense lets components wait for something before rendering:
1import { Suspense } from 'react'
2
3function App() {
4 return (
5 <Suspense fallback={<Loading />}>
6 <LazyComponent />
7 </Suspense>
8 )
9}Q3: How would you implement a design system in React?
Answer:
- Token-based theming
1const theme = {
2 colors: { primary: '#007bff', secondary: '#6c757d' },
3 spacing: { sm: '0.5rem', md: '1rem', lg: '2rem' },
4 typography: { fontFamily: 'Inter', sizes: { sm: '0.875rem' } }
5}- Compound Components
1<Card>
2 <Card.Header>Title</Card.Header>
3 <Card.Body>Content</Card.Body>
4 <Card.Footer>Actions</Card.Footer>
5</Card>- Polymorphic Components
1<Button as="a" href="/home">Link Button</Button>
2<Button as="button">Regular Button</Button>- Variant-based styling
1<Button variant="primary" size="lg" />- Documentation with Storybook
Q4: Explain React Server Components
Answer: React Server Components render on the server and send HTML to the client, reducing JavaScript bundle size.
1// Server Component (default in Next.js 13+)
2async function ProductPage({ id }) {
3 const product = await db.products.findById(id) // Direct DB access
4 return <ProductDetails product={product} />
5}
6
7// Client Component
8'use client'
9function AddToCart({ productId }) {
10 const [adding, setAdding] = useState(false)
11 // Interactive logic
12}Benefits:
- Zero JavaScript shipped for server components
- Direct database/API access
- Better initial load performance
- Automatic code splitting
Q5: How do you handle state management at scale?
Answer:
- Local State - Component-specific UI state
- Lifted State - Shared between siblings
- Context - App-wide, infrequently changing
- External Libraries - Complex, frequently changing
Architecture pattern:
┌─────────────────────────────────────┐
│ Server State │
│ (React Query, SWR, Apollo) │
├─────────────────────────────────────┤
│ Global UI State │
│ (Zustand, Redux Toolkit) │
├─────────────────────────────────────┤
│ Form State │
│ (React Hook Form, Formik) │
├─────────────────────────────────────┤
│ Local State │
│ (useState, useReducer) │
└─────────────────────────────────────┘
Q6: What are the common security concerns in React?
Answer:
- XSS (Cross-Site Scripting)
1// React escapes by default
2<div>{userInput}</div> // Safe
3
4// ❌ Dangerous
5<div dangerouslySetInnerHTML={{ __html: userInput }} />
6
7// ✅ Sanitize if needed
8import DOMPurify from 'dompurify'
9<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />- Injection via URLs
1// ❌ Dangerous
2<a href={userProvidedUrl}>Link</a>
3
4// ✅ Validate
5const safeUrl = userUrl.startsWith('https://') ? userUrl : '#'- Sensitive data in state
- Don't store tokens in localStorage (XSS vulnerable)
- Use httpOnly cookies for auth tokens
- Clear sensitive state on logout
- Dependency vulnerabilities
- Regular
npm audit - Keep dependencies updated
Q7: How do you test complex React applications?
Answer:
Testing Pyramid:
E2E Tests (Cypress, Playwright)
/ \
/ Integration Tests (RTL) \
/ \
/________Unit Tests (Jest)___________\
Unit Tests - Individual functions/hooks
1test('useCounter increments', () => {
2 const { result } = renderHook(() => useCounter())
3 act(() => result.current.increment())
4 expect(result.current.count).toBe(1)
5})Integration Tests - Component behavior
1test('form submits correctly', async () => {
2 const handleSubmit = jest.fn()
3 render(<Form onSubmit={handleSubmit} />)
4
5 await userEvent.type(screen.getByLabelText('Email'), 'test@test.com')
6 await userEvent.click(screen.getByRole('button', { name: 'Submit' }))
7
8 expect(handleSubmit).toHaveBeenCalledWith({ email: 'test@test.com' })
9})E2E Tests - Full user flows
1test('user can checkout', async ({ page }) => {
2 await page.goto('/products')
3 await page.click('text=Add to Cart')
4 await page.click('text=Checkout')
5 await expect(page).toHaveURL('/checkout')
6})Q8: How would you handle internationalization (i18n)?
Answer:
Using react-i18next:
1// i18n.ts
2import i18n from 'i18next'
3import { initReactI18next } from 'react-i18next'
4
5i18n.use(initReactI18next).init({
6 resources: {
7 en: { translation: { welcome: 'Welcome' } },
8 es: { translation: { welcome: 'Bienvenido' } }
9 },
10 lng: 'en',
11 fallbackLng: 'en'
12})
13
14// Component
15import { useTranslation } from 'react-i18next'
16
17function Welcome() {
18 const { t, i18n } = useTranslation()
19
20 return (
21 <div>
22 <h1>{t('welcome')}</h1>
23 <button onClick={() => i18n.changeLanguage('es')}>
24 Español
25 </button>
26 </div>
27 )
28}Considerations:
- RTL support
- Date/number formatting
- Pluralization
- Dynamic content
Q9: Explain React's batching behavior
Answer: React batches multiple state updates into a single re-render for performance.
1// React 18 - automatic batching everywhere
2function handleClick() {
3 setCount(c => c + 1) // Batched
4 setFlag(f => !f) // Batched
5 // Only ONE re-render
6}
7
8// Even in async code (React 18+)
9async function handleClick() {
10 await someAsyncOperation()
11 setCount(c => c + 1) // Batched
12 setFlag(f => !f) // Batched
13 // Only ONE re-render
14}
15
16// Opt out of batching
17import { flushSync } from 'react-dom'
18
19function handleClick() {
20 flushSync(() => setCount(c => c + 1)) // Re-renders immediately
21 flushSync(() => setFlag(f => !f)) // Re-renders immediately
22}Q10: How do you architect a large React application?
Answer:
Directory Structure:
src/
├── app/ # Routes/pages
├── components/
│ ├── ui/ # Generic UI components
│ └── features/ # Feature-specific components
├── hooks/ # Custom hooks
├── lib/ # Utilities, API clients
├── stores/ # State management
├── types/ # TypeScript types
└── constants/ # App constants
Key Principles:
- Separation of concerns - UI vs logic
- Colocation - Keep related code together
- Feature-based organization - Not type-based
- Dependency direction - Features depend on shared, not vice versa
- Testing strategy - Unit, integration, E2E
These questions test deep React knowledge!
