Context API

I'll never forget the moment when I had to pass the user's authentication state through seven levels of components just so a "Logout" button could access it. The App component had auth state, which went to Layout, then Sidebar, then UserMenu, then MenuDropdown, then DropdownList, and finally to LogoutButton. Six components that didn't care about auth, just passing it along like a relay race. That's when I learned about Context.

Context is React's built-in solution for sharing state globally without passing props through every level. It's perfect for data that many components need: theme settings, user authentication, shopping cart, language preferences. In the POS system, I use Context for cart state and user authenticationβ€”data that needs to be accessible anywhere in the app.

When to Use Context vs Props

Use props when:

  • Data is only needed by direct children

  • The component hierarchy is shallow (1-2 levels)

  • You want explicit data flow

Use Context when:

  • Many components at different levels need the same data

  • You're passing props through components that don't use them (prop drilling)

  • The data is truly global (auth, theme, cart)

In the POS system:

  • Product data β†’ Props (only needed by product components)

  • User authentication β†’ Context (needed everywhere)

  • Cart state β†’ Context (needed by many components)

  • Individual product quantity β†’ Props (local to that component)

Creating Your First Context: Cart

Let's solve the cart prop drilling problem with Context.

Step 1: Create the Context

What's happening here?

  1. Create context: createContext<CartContextType>()

  2. Provider component: Manages state and provides value

  3. Custom hook: Makes using context easier and safer

Step 2: Wrap Your App with the Provider

Everything inside <CartProvider> can now access cart state.

Step 3: Use the Context in Any Component

No more prop drilling! Any component can access cart state with useCart().

Creating Authentication Context

Authentication is another perfect use case for Context. Let's build it for the POS system.

Using Auth Context

Composing Multiple Contexts

Real applications need multiple contexts. The pattern:

Nesting can get deep. Here's a cleaner pattern:

Context with Local Storage Persistence

Let's make the cart persist across page refreshes:

Now the cart survives page refreshes!

Real POS Example: Complete Context Setup

Here's the production-quality setup I use in the POS system:

Using it across the app:

When NOT to Use Context

Context isn't always the answer. Don't use Context for:

1. Frequently Changing Values

Better: Keep fast-changing state local or use a state management library.

2. Simple Parent-Child Communication

Better: Pass props directly.

3. Complex State Logic

Better: Split into multiple contexts or use a state management library.

Common Mistakes to Avoid

1. Creating Context Without Provider

Fix: Always wrap with provider.

2. Not Checking for Undefined

Fix: Check and throw error:

3. Creating New Objects Every Render

Fix: Use useMemo or define functions outside render:

4. Too Many Contexts

Fix: Combine related contexts or use a state management library for complex apps.

Key Learnings

  1. Context solves prop drilling for global state

  2. Use Context for: auth, theme, cart, language

  3. Don't use Context for: frequent updates, simple parent-child communication

  4. Always provide a custom hook (useCart) for type safety

  5. Wrap app with Provider to make context available

  6. Check for undefined in custom hooks

  7. Combine multiple contexts by nesting providers

  8. Persist state with localStorage + useEffect

Next Steps

You now know how to share state globally with Context. But Context provides values, not reusable logic. What if you want to extract data fetching logic or cart operations into reusable functions? That's where custom hooks come in. In the next article, we'll build useProducts, useCart, and useAuth hooks to organize our POS system's logic.

Continue to: Custom Hooks β†’

Last updated