To type React context, pass a type argument to createContext and let useContext infer the value type. When the default is null, a custom hook that throws narrows the null away so consumers get a non-null value. Four cases cover most contexts: a plain value, a nullable value, a value with a setter, and a dispatch function.
Type the context with a generic
A type argument on createContext sets the value type. The default must match that type, and every useContext call returns it, so editors and the compiler agree on what flows through the tree.
import { createContext } from "react";
type Theme = "light" | "dark" | "system";
export const ThemeContext = createContext<Theme>("system");Consumers now get Theme back from useContext, and TypeScript rejects any other string passed as a provided value.
Handle a nullable context
When no sensible default exists, type the context as the value or null. A custom hook narrows the null away with a runtime check, so callers never handle null themselves.
import { createContext, useContext } from "react";
type Theme = "light" | "dark";
const ThemeContext = createContext<Theme | null>(null);
export function useTheme() {
const theme = useContext(ThemeContext);
if (theme === null) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return theme;
}The check narrows Theme or null down to Theme. The throw also makes a missing provider fail loudly during development instead of rendering a broken value.
Type a value with a setter
When the context exposes a state setter, describe the object shape with Dispatch and SetStateAction imported as types. The object type is what readers receive.
import { createContext, useState } from "react";
import type { Dispatch, SetStateAction } from "react";
type Theme = "light" | "dark";
type ThemeContextValue = {
theme: Theme;
setTheme: Dispatch<SetStateAction<Theme>>;
};
export const ThemeContext = createContext<ThemeContextValue | null>(null);ThemeContextValue is the shape readers receive. The setter type accepts either a Theme or an updater function, matching what useState returns exactly.
Provide the typed value
The provider owns typed state and passes an object that matches the context type. Type the state so the object lines up without extra casts.
import { useState } from "react";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>("light");
return (
<ThemeContext value={{ theme, setTheme }}>
{children}
</ThemeContext>
);
}Typing the state as Theme makes the object match ThemeContextValue. Consumers that call useTheme receive Theme and the correct setter, and the provider can only pass a valid Theme.
Type a reducer context
A context that carries a dispatch function uses the action type. Type the dispatch context with the Dispatch type and your Action type, so callers can only send the actions the reducer handles.
import { createContext } from "react";
import type { Dispatch } from "react";
type Action = { type: "added"; text: string };
export const TasksDispatchContext = createContext<Dispatch<Action> | null>(null);Readers that only dispatch get a Dispatch function typed with Action. A misspelled action type or a missing field becomes a compile error at the call site, before the reducer ever runs.
Common type errors
The first error usually appears at the provider, not the consumer. Passing a value that does not match the generic, such as a plain string where a Theme union is expected, fails on the value prop.
A missing provider surfaces as a null value, so the custom hook's throw is the clear signal. Read the error on the value prop before tracing the consumer.
What to learn next
The null narrowing pairs with the runtime guard in safe default values for React context. For the read and write split, see how to split React context by responsibility.
Rune AI
Key Insights
⚠ This article has a formatting issue and may not display correctly.
Our team has been notified. The content is shown as plain text below.
Frequently Asked Questions
What does createContext<Theme> do?
How do I remove the null from a nullable context?
What type should a setter in context use?
Conclusion
⚠ This article has a formatting issue and may not display correctly.
Our team has been notified. The content is shown as plain text below.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.