Typing Context API
In this page:
Creating a Typed Context
Creating a typed context means passing an interface as createContext's generic argument, so every consumer of that context automatically gets the correct shape without re-declaring the type at each use.
Example: Creating a Typed Context
import React, { createContext } from "react";
interface ThemeContextValue {
theme: "light" | "dark";
}
const ThemeContext = createContext<ThemeContextValue>({ theme: "light" });
export default ThemeContext;
Using useContext
useContext returns whatever type the context was created with, so once the context itself is typed correctly, using it anywhere in the tree requires no extra type annotations at the call site.
Example: Using useContext
import React, { createContext, useContext } from "react";
const ThemeContext = createContext({ theme: "light" });
function ThemedText(): React.JSX.Element {
const { theme } = useContext(ThemeContext);
return <p>Theme: {theme}</p>;
}
export default ThemedText;
Context with State
Combining context with state means typing the context's value as an object holding both the current state and its setter function, letting consumers both read and update shared state with full type safety.
Example: Context with State
import React, { createContext, useState } from "react";
interface CountContextValue {
count: number;
setCount: (n: number) => void;
}
const CountContext = createContext<CountContextValue | undefined>(undefined);
export default CountContext;
Creating a Safe Context Hook
A safe context hook wraps useContext in a custom hook that throws if the context is accessed outside its provider, converting a possibly-undefined context value into a guaranteed non-null type for consumers.
Example: Creating a Safe Context Hook
import React, { createContext, useContext } from "react";
const CountContext = createContext<{ count: number } | undefined>(undefined);
function useCount() {
const ctx = useContext(CountContext);
if (!ctx) throw new Error("useCount must be used within CountContext.Provider");
return ctx;
}
export default useCount;
Context with Complex Values
Context holding complex, deeply nested values benefits from breaking the value's interface into smaller named types, keeping the context's overall type readable instead of one large inline object type.
Example: Context with Complex Values
import React, { createContext } from "react";
interface User {
name: string;
}
interface AppContextValue {
user: User | null;
permissions: string[];
}
const AppContext = createContext<AppContextValue>({ user: null, permissions: [] });
export default AppContext;
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: