Context API Patterns

Before reaching for Redux or Zustand, you should master React’s built-in Context API. It’s perfect for low-frequency updates like themes, user authentication, or language preferences.

However, it has a dark side: Performance. If used incorrectly, Context can cause your entire app to re-render on every keystroke.

1. The Problem: Prop Drilling

Prop drilling occurs when you pass data through components that don’t need it, just to get it to a deep child component.

Interactive: Prop Drilling Visualizer

Visualize how data travels. Click a node to see the path.

Click the Target Component at the bottom right to trace the data flow.

2. Pattern: The Split Context

A common mistake is putting both state and dispatch (or setter functions) into the same context object.

// ❌ BAD: Re-renders everything when *either* changes
const MyContext = createContext({ state, dispatch });

If a component only needs dispatch (e.g., a button), it will still re-render when state changes!

The Solution: Split Components

Create two separate contexts.

import { createContext, useContext, useState, ReactNode } from 'react';

// Define context types
interface User {
  name: string;
  email: string;
}

type StateContextType = User | null;
type DispatchContextType = (user: User | null) => void;

const StateContext = createContext<StateContextType>(null);
const DispatchContext = createContext<DispatchContextType>(() => {});

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  return (
    <StateContext.Provider value={user}>
      <DispatchContext.Provider value={setUser}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}

// Custom Hooks for consumption
export const useUser = () => useContext(StateContext);
export const useSetUser = () => useContext(DispatchContext);

Now, components that only need setUser (like a Logout button) won’t re-render when user changes (like updating the profile name).

3. When to use Context vs Redux/Zustand?

Criteria Context API Zustand / Redux
Frequency Low (Themes, Auth, Locale) High (Dashboard data, Real-time feeds)
Complexity Simple values or small objects Complex nested objects, arrays
DevTools Basic (React DevTools) Advanced (Time Travel, Diffing)
Performance Requires manual optimization (Split/Memo) Optimized out of the box (Selectors)

[!IMPORTANT] Context is NOT a state management library. It is a dependency injection mechanism. It doesn’t manage state; it transports it. You still use useState or useReducer to manage the actual data.