UtilsDaily

React Interview Prep

12 topics from fundamentals to lead-level concepts. TypeScript examples included.

12 topics
βš›οΈ Core Fundamentals Fundamentals β–Ύ

JSX

JSX compiles to React.createElement(type, props, ...children). It's not HTML β€” className, htmlFor, camelCase events.

// JSX
const el = ;

// Compiled to:
const el = React.createElement('button', { onClick: handleClick, className: 'btn' }, 'Click');

Props

  • Read-only β€” never mutate props
  • One-way data flow (parent β†’ child)
  • Spread carefully: <Comp {...props} />

Controlled vs Uncontrolled Forms

// Controlled: React owns the value
const [email, setEmail] = useState('');
 setEmail(e.target.value)} />

// Uncontrolled: DOM owns the value
const inputRef = useRef(null);

Keys in Lists

Keys help React identify which items changed. Use stable, unique IDs β€” never array indices (causes bugs on reorder/delete).

// BAD: index as key
{items.map((item, i) => 
  • )} // GOOD: stable ID {items.map(item =>
  • )}
  • πŸͺ Hooks Deep Dive Hooks β–Ύ

    useState β€” Lazy Init & Functional Updates

    // Lazy init: only called once
    const [filters, setFilters] = useState(() =>
      JSON.parse(localStorage.getItem('filters') || '[]')
    );
    
    // Functional update: safe when depending on prev state
    setCount(prev => prev + 1);

    useEffect β€” Dependency Array Rules

    useEffect(() => {
      const controller = new AbortController();
      fetch(url, { signal: controller.signal })
        .then(r => r.json())
        .then(setData);
      return () => controller.abort(); // Cleanup!
    }, [url]); // Re-runs when url changes

    useRef β€” Beyond DOM Refs

    // Store mutable value without triggering re-render
    const renderCount = useRef(0);
    renderCount.current++;
    
    // Store previous value
    const prevValue = useRef(value);
    useEffect(() => { prevValue.current = value; }, [value]);

    useReducer β€” Complex State

    type Action = { type: 'increment' } | { type: 'reset'; payload: number };
    
    function reducer(state: number, action: Action): number {
      switch (action.type) {
        case 'increment': return state + 1;
        case 'reset':    return action.payload;
      }
    }

    Rules of Hooks

    • Only call hooks at the top level (never inside loops/conditions)
    • Only call hooks in React functions (not regular JS)
    • Why? Hooks use a linked list β€” call order must be consistent

    Custom Hook Example

    function useLocalStorage(key: string, initial: T) {
      const [value, setValue] = useState(() => {
        const stored = localStorage.getItem(key);
        return stored ? JSON.parse(stored) : initial;
      });
      const set = (v: T) => {
        setValue(v);
        localStorage.setItem(key, JSON.stringify(v));
      };
      return [value, set] as const;
    }
    🧩 Component Patterns Patterns β–Ύ

    Composition over Inheritance

    React favors composition. Pass components as props (children, named slots) rather than extending classes.

    Higher-Order Components (HOC)

    function withAuth

    (WrappedComponent: React.ComponentType

    ) { return function AuthGuard(props: P) { const { user } = useAuth(); if (!user) return ; return ; }; } const ProtectedDashboard = withAuth(Dashboard);

    Compound Components

    Components that work together, sharing implicit state via Context. Example: <Select> + <Option>.

    const TabCtx = React.createContext('');
    
    function Tabs({ children, defaultTab }: Props) {
      const [active, setActive] = useState(defaultTab);
      return {children};
    }
    
    function Tab({ id, children }: TabProps) {
      const active = useContext(TabCtx);
      return ;
    }

    Error Boundaries

    Class components only (as of React 18). Catch JS errors in the component tree and show fallback UI.

    class ErrorBoundary extends React.Component {
      state = { hasError: false };
      static getDerivedStateFromError() { return { hasError: true }; }
      componentDidCatch(err: Error, info: React.ErrorInfo) {
        logErrorToService(err, info);
      }
      render() {
        return this.state.hasError ?  : this.props.children;
      }
    }
    πŸ—‚οΈ State Management State β–Ύ

    Decision Matrix

    ToolBest ForWhen to avoid
    useStateSimple, local stateShared across many components
    Context APILow-frequency updates (auth, theme)High-frequency updates (causes re-renders)
    Redux ToolkitLarge apps, complex async, time-travel debugSimple apps (too much boilerplate)
    ZustandMedium apps, lightweight global stateWhen you need Redux DevTools fully
    TanStack QueryServer state (fetching, caching, sync)Client-only state

    Context with useReducer (Production Pattern)

    const AuthCtx = createContext(null);
    
    function AuthProvider({ children }: Props) {
      const [state, dispatch] = useReducer(authReducer, initialState);
      return (
        
          {children}
        
      );
    }
    
    function useAuth() {
      const ctx = useContext(AuthCtx);
      if (!ctx) throw new Error('useAuth must be inside AuthProvider');
      return ctx;
    }

    Zustand (Minimal Boilerplate)

    const useStore = create((set) => ({
      count: 0,
      increment: () => set(s => ({ count: s.count + 1 })),
    }));
    
    // In component:
    const count = useStore(s => s.count);  // Fine-grained subscription
    ⚑ Performance Optimization Performance β–Ύ

    React.memo β€” Memoize Component Output

    const ExpensiveList = React.memo(function ExpensiveList({ items }: Props) {
      return 
      {items.map(i =>
    • {i.name}
    • )}
    ; }); // memo only helps if parent re-renders with same props // For function props, use useCallback in parent!

    useMemo & useCallback

    // useMemo: memoize expensive computed value
    const sorted = useMemo(() =>
      [...items].sort((a, b) => a.name.localeCompare(b.name)),
      [items]
    );
    
    // useCallback: stable function reference for React.memo children
    const handleDelete = useCallback((id: string) => {
      setItems(prev => prev.filter(i => i.id !== id));
    }, []);  // no deps = never recreated

    Code Splitting

    const Dashboard = React.lazy(() => import('./Dashboard'));
    
    function App() {
      return (
        }>
          
            } />
          
        
      );
    }

    Virtualization for Long Lists

    Use react-window or @tanstack/react-virtual. Only render visible rows (e.g. 50 of 10,000) instead of all.

    Web Vitals Targets

    MetricMeasuresGood
    LCPLargest content render time< 2.5s
    FID/INPInput responsiveness< 100ms
    CLSLayout shift< 0.1
    🌐 Routing & Data Fetching Ecosystem β–Ύ

    React Router v6

    // App.tsx
    
      }>
        } />
        } />
      
    
    
    // Layout.tsx
    function Layout() {
      return <>

    Key Router Hooks

    • useNavigate() β€” programmatic navigation
    • useParams() β€” URL params (:id)
    • useSearchParams() β€” query string
    • useLocation() β€” current path + state

    TanStack Query β€” Server State

    const { data, isLoading, error } = useQuery({
      queryKey: ['users', userId],
      queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
      staleTime: 5 * 60 * 1000,  // 5 min cache
    });
    
    // Mutation with optimistic update
    const mutation = useMutation({
      mutationFn: updateUser,
      onMutate: async (newUser) => {
        await queryClient.cancelQueries({ queryKey: ['users'] });
        const prev = queryClient.getQueryData(['users']);
        queryClient.setQueryData(['users'], old => updateInList(old, newUser));
        return { prev };  // Rollback context
      },
      onError: (_, __, ctx) => queryClient.setQueryData(['users'], ctx.prev),
    });
    πŸ”¬ Advanced Concepts Internals β–Ύ

    React Fiber Architecture

    Fiber is React's reconciliation engine (React 16+). It represents each component as a "fiber" unit of work. Key capability: interruptible rendering.

    • Work is broken into units that can pause/resume
    • Higher-priority updates (user input) can interrupt lower-priority ones
    • Enables Concurrent Mode, Suspense, time-slicing

    Concurrent Features (React 18+)

    // useTransition: mark updates as non-urgent
    const [isPending, startTransition] = useTransition();
    startTransition(() => setSearchQuery(value));  // Can be interrupted
    
    // useDeferredValue: defer rendering of slow parts
    const deferred = useDeferredValue(query);
    // deferred lags behind query β€” list re-renders at lower priority

    Automatic Batching (React 18)

    // React 18: all setStates batched even in async/event handlers
    async function handleClick() {
      setLoading(true);   // }
      setError(null);     // } β€” React 18 batches all 3,
      setData(result);    // }   one single re-render
    }

    Server Components (React 19 / Next.js)

    • Render on the server, send HTML β€” zero JS to client
    • Can access DB/FS directly (no useEffect needed)
    • Cannot use useState, useEffect, browser APIs
    • Client components: add 'use client' directive
    // Server Component (default in Next.js App Router)
    async function UserList() {
      const users = await db.user.findMany();  // Direct DB access!
      return 
      {users.map(u =>
    • {u.name}
    • )}
    ; }
    πŸ§ͺ Testing React Testing β–Ύ

    Philosophy: Test behavior, not implementation. Query by what users see, not internal state.

    React Testing Library Queries (Priority Order)

    1. getByRole β€” most accessible, preferred
    2. getByLabelText β€” for form inputs
    3. getByPlaceholderText β€” fallback for inputs
    4. getByText β€” for non-interactive text
    5. getByTestId β€” last resort only

    Component Test Example

    import { render, screen } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    
    test('submits login form', async () => {
      const user = userEvent.setup();
      render();
    
      await user.type(screen.getByLabelText(/email/i), 'a@b.com');
      await user.type(screen.getByLabelText(/password/i), 'secret');
      await user.click(screen.getByRole('button', { name: /log in/i }));
    
      expect(mockSubmit).toHaveBeenCalledWith({ email: 'a@b.com', password: 'secret' });
    });

    Mock Service Worker (MSW)

    // handlers.ts
    export const handlers = [
      http.get('/api/users', () => {
        return HttpResponse.json([{ id: 1, name: 'Alice' }]);
      }),
    ];
    
    // No real network calls in tests!

    Testing Async

    test('loads and displays users', async () => {
      render();
      expect(screen.getByText(/loading/i)).toBeInTheDocument();
      await waitFor(() => screen.getByText('Alice'));
      expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
    });
    πŸ› οΈ Build Tools & Ecosystem Tooling β–Ύ
    ToolRoleNotes
    ViteDev server + bundlerESM-based, instant HMR, uses Rollup for prod
    WebpackBundlerMature, highly configurable, slower
    SWCTranspilerRust-based, 20Γ— faster than Babel
    ESLintLinterUse react-hooks/rules-of-hooks plugin
    TypeScriptType safetyUse discriminated unions for action types

    TypeScript with React

    // Props typing
    interface ButtonProps {
      label: string;
      onClick: () => void;
      variant?: 'primary' | 'secondary';
      children?: React.ReactNode;
    }
    
    // Event handler typing
    const handleChange = (e: React.ChangeEvent) => {
      setEmail(e.target.value);
    };

    CSS Strategy Comparison

    ApproachProsCons
    CSS ModulesNo class collision, co-locatedVerbose, no dynamic styles
    Tailwind CSSFast dev, no naming, tiny prod CSSLong class strings, learning curve
    styled-componentsDynamic styles, TypeScript propsRuntime overhead, larger bundle
    πŸ—οΈ System Design & Architecture Architecture β–Ύ

    Folder Structure: Feature-Based (Preferred)

    src/
      features/
        auth/
          components/LoginForm.tsx
          hooks/useAuth.ts
          api/authApi.ts
          index.ts         ← barrel export
        dashboard/
          components/...
          hooks/...
      shared/
        components/Button.tsx
        utils/formatDate.ts
      app/
        App.tsx
        routes.tsx

    Anti-pattern: layer-based folders (components/, hooks/, services/) β€” forces cross-cutting changes across many directories.

    Barrel Exports (Index Files)

    // features/auth/index.ts
    export { LoginForm } from './components/LoginForm';
    export { useAuth } from './hooks/useAuth';
    
    // Import cleanly:
    import { LoginForm, useAuth } from 'features/auth';

    Storybook

    Document and develop components in isolation. Each component gets a "story" showing all its states. Essential for design systems and shared component libraries.

    πŸ‘” Lead Role Questions Leadership β–Ύ

    Code Review Checklist for React PRs

    • Are hooks dependencies arrays correct? (no stale closures)
    • Is state minimal? (derive values, don't duplicate)
    • Are effects cleaning up? (subscriptions, timers, abort)
    • Any unnecessary any TypeScript casts?
    • Accessibility: ARIA roles, keyboard navigation, focus management
    • Performance: any render waterfall? Unnecessary re-renders?

    ADR (Architecture Decision Record)

    Document significant technical decisions: context, options considered, decision, and consequences. Prevents "why did we do this?" months later.

    Mentoring Junior Developers

    • Pair program on complex problems β€” explain the "why"
    • PR comments as teaching opportunities, not criticism
    • Assign "stretch" tasks just beyond their comfort zone
    • Code reviews: ask questions before suggesting ("what happens if…?")

    Migration Strategy (e.g. React 18 upgrade)

    1. Audit breaking changes and deprecated APIs
    2. Update in a separate branch, fix one issue at a time
    3. Update tests to reflect new behavior (batching, Strict Mode)
    4. Feature-flag rollout with monitoring
    5. Write an ADR capturing trade-offs

    Performance Budgeting

    • Set LCP < 2.5s, INP < 100ms, CLS < 0.1 as CI gates
    • Bundle size budgets per route (e.g. max 200KB initial JS)
    • Use Lighthouse CI or SpeedCurve for tracking over time
    β–² React vs Next.js Framework β–Ύ
    ReactNext.js
    TypeUI LibraryFull-stack Framework
    RoutingReact Router (manual)File-based (automatic)
    RenderingCSR only (by default)CSR, SSR, SSG, ISR, Streaming
    SEONeeds extra setupBuilt-in metadata API
    API layerSeparate backend neededRoute Handlers / Server Actions
    ImagesManual optimization<Image> with auto-optimization

    Rendering Strategies

    • CSR β€” JavaScript renders in browser; bad for SEO, fast navigations
    • SSR β€” rendered on every request; always fresh, slower TTFB
    • SSG β€” built at build time; fastest, but stale until redeploy
    • ISR β€” SSG + revalidate after N seconds; best of SSG + SSR
    • Streaming SSR β€” progressive HTML chunks; faster FCP

    App Router vs Pages Router

    Next.js App Router (13.4+) is the new standard: Server Components by default, nested layouts, server actions. Pages Router still works but is legacy. New projects should use App Router.

    When to Choose React without Next.js

    • Internal dashboards / admin tools (SEO irrelevant)
    • When you already have a separate backend (Rails, Django)
    • PWAs or Electron apps
    No topics match your search. Try different keywords.

    What is React Interview Prep?

    This is a structured, interactive reference covering the 12 React topics that appear most frequently in frontend and full-stack engineering interviews β€” from mid-level to lead engineer level. Each topic includes a concise explanation alongside TypeScript-typed code examples that reflect modern React (React 18/19) best practices.

    React powers the frontends of Meta, Airbnb, Netflix, and thousands of other companies. But React interviews go far beyond "what is JSX?" β€” senior and lead candidates are expected to understand the Fiber reconciliation engine, know when useMemo helps versus hurts, and explain how Server Components change the rendering model.

    How Does This Interview Prep Tool Work?

    All 12 topics are displayed open by default so their content is immediately readable. Click any topic header to collapse it and declutter the view. Use the search bar to instantly filter topics by keyword β€” type "hooks", "memo", "fiber", or any other term to jump straight to what you need. The progress bar tracks how many topics are currently expanded in your session.

    Topics Covered in This Guide

    • Fundamentals: JSX, props, state, controlled vs uncontrolled forms, keys in lists, conditional rendering
    • Hooks: useState (lazy init, functional updates), useEffect (cleanup, stale closures), useRef, useReducer, custom hooks, Rules of Hooks
    • Patterns: Higher-Order Components, render props, compound components, error boundaries
    • State Management: Context API, Redux Toolkit, Zustand, Jotai β€” with a decision matrix
    • Performance: React.memo, useMemo, useCallback, code splitting, virtualization, Web Vitals (LCP, INP, CLS)
    • Routing & APIs: React Router v6, TanStack Query, optimistic updates, race condition handling
    • Internals: Fiber architecture, concurrent features (useTransition, useDeferredValue), Server Components, automatic batching
    • Testing: React Testing Library, Jest, Mock Service Worker (MSW), async testing patterns
    • Tooling: Vite, SWC, TypeScript with React, CSS Modules vs Tailwind
    • Architecture: Feature-based folder structure, barrel exports, Storybook, monorepo patterns
    • Leadership: Code review checklists, ADRs, mentoring, performance budgets, migration strategies
    • Framework: React vs Next.js β€” rendering strategies (CSR/SSR/SSG/ISR), App Router, Server Actions

    Who Should Use This Guide?

    • Mid-level engineers solidifying their understanding of hooks, component patterns, and state management
    • Senior candidates who need to speak confidently about Fiber internals, performance optimization, and system design
    • Lead / Staff candidates preparing for leadership and architecture questions (code review standards, ADRs, migration strategies)
    • Backend engineers transitioning to full-stack who need a structured React foundation

    Benefits of Using This Tool

    • Searchable: Find any topic instantly β€” type a keyword and only matching sections appear
    • TypeScript examples: All code examples use TypeScript, matching modern production standards
    • React 18/19 current: Covers concurrent features, Server Components, and the React Compiler
    • Lead-level depth: Includes architecture, mentoring, and code review content β€” not just technical Q&A
    • Free & browser-based: No login or installation required

    How to Prepare for a React Interview

    React interviews at senior level test whether you understand why the API is designed the way it is. Why are Hooks called in a linked list? Why does useEffect run after paint, not before? Why does automatic batching in React 18 change existing code behavior? Knowing the answers to these "why" questions is what separates a confident senior candidate from someone who's just used React for years.

    Pair this reference guide with hands-on practice: build a custom hook (useFetch, useDebounce), deliberately cause a stale closure bug in useEffect and fix it, profile a slow list with React DevTools, and write an optimistic update with TanStack Query. Active experimentation is the fastest path to interview confidence.

    Embed This Tool on Your Website

    β–Ό