🟦 React with TypeScript
Estimated reading: 4 minutes 47 views

🟦 React Type-safe Context & Hooks – Share State with Confidence (2025 Guide)


🧲 Introduction – Why Use Type-safe Context & Hooks?

React’s Context API and custom hooks allow you to share data like themes, auth, or settings across components. But without TypeScript, they’re prone to:

  • πŸ”΄ Misused values
  • ❌ Undefined contexts
  • πŸ”§ Runtime errors from improper access

Type-safe context and hooks ensure:

  • 🧠 Correct types for context values
  • πŸ”’ Compile-time protection
  • βš™οΈ Reusable and scalable global state logic

🎯 In this guide, you’ll learn:

  • How to create a type-safe React context
  • Write custom hooks with generics
  • Handle default values, nulls, and error states safely
  • Best practices for shared logic

🧱 1. Creating a Type-safe React Context

βœ… Define Types & Create Context

type ThemeContextType = {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
};

const ThemeContext = React.createContext<ThemeContextType | undefined>(undefined);

πŸ“˜ Always use undefined and check before accessing


βœ… Provider Component

const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  const toggleTheme = () => {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

βœ… Fully typed shared state and actions


🧠 2. Create a Custom Hook for Access

function useTheme(): ThemeContextType {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

πŸ“˜ Centralizes the logic and removes boilerplate
βœ… Protects against missing providers


πŸ”„ 3. Usage in Components

const ThemeToggler = () => {
  const { theme, toggleTheme } = useTheme();
  return (
    <button onClick={toggleTheme}>
      Current Theme: {theme}
    </button>
  );
};

βœ… Autocomplete and type checking for theme and toggleTheme


πŸ“¦ 4. Generic Reusable Context Creator

function createTypedContext<T>() {
  const context = React.createContext<T | undefined>(undefined);
  const useCtx = () => {
    const ctx = useContext(context);
    if (!ctx) throw new Error('Context must be used within Provider');
    return ctx;
  };
  return [context.Provider, useCtx] as const;
}

βœ… Usage:

type Auth = { user: string; logout: () => void };

const [AuthProvider, useAuth] = createTypedContext<Auth>();

πŸ“˜ Reduces repetition across multiple contexts
βœ… Enforces consistent typing for shared logic


πŸ§ͺ 5. Typing Context with Async Data

type UserContextType = {
  user: User | null;
  loading: boolean;
};

const UserContext = createContext<UserContextType | undefined>(undefined);

πŸ“˜ Use when fetching from APIs (auth, profile)
βœ… Avoid any or nullable types without checks


🧩 6. Type-safe Custom Hooks with Generics

function useList<T>(initial: T[]): [T[], (item: T) => void] {
  const [list, setList] = useState<T[]>(initial);
  const add = (item: T) => setList((prev) => [...prev, item]);
  return [list, add];
}

βœ… Usage:

const [names, addName] = useList<string>([]);

βœ… Fully reusable with different types
πŸ“˜ Promotes DRY and scalable logic


πŸ“˜ Best Practices

Best PracticeWhy It Helps
βœ… Default context as undefinedForces useContext checks
βœ… Use custom hooks to access contextHides error handling & improves DX
βœ… Use as const with genericsPreserves tuple shape and immutability
βœ… Avoid any in contextUse interfaces or union types instead
βœ… Split provider and hook logicBetter testing and composition

πŸ“Œ Summary – Recap & Next Steps

Using TypeScript with React context and hooks improves safety, reusability, and clarity. You get complete control over data shape, access patterns, and prevent misuse at compile time.

πŸ” Key Takeaways:

  • Use <T | undefined> for context default values
  • Wrap useContext with a custom hook for type safety
  • Use generics for reusable state hooks
  • Validate context access with runtime guards
  • Keep types, context, and hooks modular

βš™οΈ Real-World Relevance:
Used in design systems (Radix UI, MUI), enterprise apps, and large codebases to manage global themes, auth, language, and UI preferences.


❓ FAQ Section

❓ Why should I set context default as undefined?
βœ… So your custom hook can throw an error when the context is accessed outside the provider.


❓ What’s the difference between createContext<T | null> and T | undefined?
βœ… undefined is more idiomatic for missing context. null is fine but less explicit in TS checks.


❓ Can I create multiple typed contexts?
βœ… Yes. Use a context factory like createTypedContext<T>() to reuse logic for any type.


❓ Should I export context or just the hook?
βœ… Prefer exporting only the custom hook. Keep Provider internal to the module for better encapsulation.


❓ Can I use context with async logic like login/logout?
βœ… Yes. Use useEffect inside the provider and define a loading state along with your context value.


Share Now :

Leave a Reply

Your email address will not be published. Required fields are marked *

Share

🟦 React Type-safe Context & Hooks

Or Copy Link

CONTENTS
Scroll to Top