Context API in TypeScript
Level: Beginner to Intermediate
- What Context API in TypeScript means in React + TypeScript
- How type safety improves React components
- How to model School Management System data with interfaces and types
- Common TypeScript mistakes to avoid
- How to explain this topic in interviews
Why This Matters
Context API in TypeScript helps you build React screens with fewer runtime bugs. In real .NET Web API projects, TypeScript makes API DTOs, component props, form state, and shared data contracts easier to understand and safer to change.
Typed contexts ensure providers and consumers are safely connected.
Prerequisite: Read React JS article 14 first.
The Problem
React JavaScript can fail at runtime when props, API responses, or form values have the wrong shape. This lesson shows how Context API in TypeScript uses TypeScript to catch many of those mistakes while you write code, before the student dashboard reaches users.
Typed Context Creation
interface User {
id: number;
name: string;
role: 'admin' | 'principal' | 'teacher' | 'student';
}
interface UserContextType {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
isLoading: boolean;
}
const UserContext = createContext<UserContextType | undefined>(undefined);
export function UserProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(false);
const login = async (email: string, password: string): Promise<void> => {
setIsLoading(true);
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const userData: User = await response.json();
setUser(userData);
} finally {
setIsLoading(false);
}
};
const logout = (): void => {
setUser(null);
};
const value: UserContextType = {
user,
login,
logout,
isLoading
};
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}
export function useUser(): UserContextType {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within UserProvider');
}
return context;
}
Multiple Typed Contexts
interface School {
id: number;
name: string;
code: string;
}
interface SchoolContextType {
school: School | null;
setSchool: (school: School) => void;
}
const SchoolContext = createContext<SchoolContextType | undefined>(undefined);
export function SchoolProvider({ children }: { children: React.ReactNode }) {
const [school, setSchool] = useState<School | null>(null);
return (
<SchoolContext.Provider value={{ school, setSchool }}>
{children}
</SchoolContext.Provider>
);
}
export function useSchool(): SchoolContextType {
const context = useContext(SchoolContext);
if (!context) throw new Error('useSchool must be in SchoolProvider');
return context;
}
// Combined Providers
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<UserProvider>
<SchoolProvider>
{children}
</SchoolProvider>
</UserProvider>
);
}
Using Multiple Contexts
function StudentDashboard() {
const { user } = useUser();
const { school } = useSchool();
if (!user || !school) return <p>Loading...</p>;
return (
<div>
<h1>{school.name}</h1>
<p>Welcome, {user.name}</p>
</div>
);
}
Memoized Context Value
export function UserProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const value = useMemo<UserContextType>(() => ({
user,
login: async (email: string, password: string) => {
// ...
},
logout: () => setUser(null)
}), [user]);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}
Theme Context Example
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>('light');
const toggleTheme = (): void => {
setTheme(t => t === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be in ThemeProvider');
return context;
}
Key Takeaways
- Type context value exactly
- Create custom hook for context
- Throw error if not provided
- Memoize context value
- Multiple contexts possible
- Next: Custom hooks with types
- undefined context — Check in custom hook
- Not memoizing — Causes re-renders
- Missing provider — Context undefined
- Any context type — Loses type safety
Always create custom hook that throws if not in provider:
export function useMyContext() {
const ctx = useContext(MyContext);
if (!ctx) throw new Error('Must be in provider');
return ctx;
}
Use ChatGPT, Claude, or Copilot to go deeper on Typed Context. Try these prompts:
"Why throw if context is undefined?""When should you memoize context value?""How do you combine multiple contexts?""Quiz me on context types"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Quick Definitions
- Context API in TypeScript - The main React + TypeScript concept explained in this lesson.
- Type/interface - A contract that describes the shape of data.
- Typed props/state - React data with clear compile-time expectations.
- API DTO - The request or response shape shared with ASP.NET Core Web API.
Common Mistakes
- Using
anytoo quickly instead of defining a useful type - Typing props loosely and losing the benefit of TypeScript
- Forgetting that API data can still be missing or invalid at runtime
- Making types too complex before the component is stable
- Not sharing clear DTO shapes between frontend and backend teams
Practice Task
Create a small React TypeScript example using Context API in TypeScript. Keep it connected to a School Management System scenario.
Suggested practice:
- Define a clear
Student,Teacher, orAttendancetype. - Build a small typed component or hook.
- Add one valid example and one intentionally wrong example to see TypeScript errors.
- Explain the type contract in your own words.
- Rebuild the same example once without looking at the article.
Quick Revision
| Question | Answer |
|---|---|
| What is the main idea? | Use TypeScript to make Context API in TypeScript safer in React. |
| Where is it used? | Props, state, forms, API responses, context, hooks, and routes. |
| What should beginners avoid? | Overusing any and ignoring runtime API validation. |
| What is the best debugging habit? | Read the TypeScript error, check the data shape, and fix the type contract. |
Context API in TypeScript is a React + TypeScript concept that improves safety and maintainability. I would explain which values need types, how TypeScript catches mistakes early, and how it helps when consuming ASP.NET Core Web API responses.
It is used in typed props, API response models, form state, route parameters, context values, custom hooks, and reusable UI components.