
React18 Legacy Context
- 752 installs
- 37.1k repo stars
- Updated July 28, 2026
- github/awesome-copilot
react18-legacy-context migrates contextTypes and getChildContext to createContext across all providers and consumers.
About
React 18 Legacy Context Migration replaces contextTypes, childContextTypes, and getChildContext with the modern createContext API before React 19 removal. Unlike single-file migrations, context changes require coordinated updates to the provider, every consumer, and a new context module or the app reads undefined values at runtime. The prescribed order finds providers and consumers with grep, creates the context file, updates the provider, migrates class consumers via contextType and function consumers via useContext, then verifies no legacy warnings remain. Reference files cover single-context, multi-context, and context file template patterns. Legacy context warns in React 18.3.1 and is fully removed in React 19.
- Always cross-file: provider, all consumers, and new context module together.
- Ordered steps: find provider, find consumers, create context, migrate each file.
- Grep commands locate childContextTypes, contextTypes, and this.context usage.
- Class consumers use contextType; function consumers use useContext hook.
- Removed in React 19; warns under React 18.3.1 today.
React18 Legacy Context by the numbers
- 752 all-time installs (skills.sh)
- +17 installs in the week ending Jul 18, 2026 (Skillselion tracking)
- Ranked #457 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
react18-legacy-context capabilities & compatibility
- Capabilities
- cross file provider and consumer coordination st · grep scan commands for legacy context symbols · class contexttype and function usecontext migrat · single and multi context reference routing · post migration warning verification checklist
- Use cases
- refactoring · testing
What react18-legacy-context says it does
Unlike most other migrations that touch one file at a time, context migration requires coordinating:
It is **removed in React 19**.
npx skills add https://github.com/github/awesome-copilot --skill react18-legacy-contextAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 752 |
|---|---|
| repo stars | ★ 37.1k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 28, 2026 |
| Repository | github/awesome-copilot ↗ |
How do I replace legacy React context without breaking consumers during a React 18 to 19 upgrade?
Migrate React legacy context API across provider and all consumer files to createContext before React 19 removes contextTypes support.
Who is it for?
Codebases still using contextTypes or getChildContext in class components ahead of React 19.
Skip if: Skip for apps already on createContext, string ref migrations, or dependency version checks.
When should I use this skill?
grep finds childContextTypes, getChildContext, or contextTypes before any context migration code.
What you get
Cross-file migration order with grep discovery, context module creation, and consumer updates for classes and functions.
- Migrated createContext provider
- Updated consumer class components
By the numbers
- Legacy context deprecated in React 16.3 and removed in React 19
- React 18.3.1 emits warnings for legacy contextTypes usage
Files
React 18 Legacy Context Migration
Legacy context (contextTypes, childContextTypes, getChildContext) was deprecated in React 16.3 and warns in React 18.3.1. It is removed in React 19.
This Is Always a Cross-File Migration
Unlike most other migrations that touch one file at a time, context migration requires coordinating: 1. Create the context object (usually a new file) 2. Update the provider component 3. Update every consumer component
Missing any consumer leaves the app broken - it will read from the wrong context or get undefined.
Migration Steps (Always Follow This Order)
Step 1: Find the provider (childContextTypes + getChildContext)
Step 2: Find ALL consumers (contextTypes)
Step 3: Create the context file
Step 4: Update the provider
Step 5: Update each consumer (class components → contextType, function components → useContext)
Step 6: Verify - run the app, check no legacy context warnings remainScan Commands
# Find all providers
grep -rn "childContextTypes\|getChildContext" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\."
# Find all consumers
grep -rn "contextTypes\s*=" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\."
# Find this.context usage (may be legacy or modern - check which)
grep -rn "this\.context\." src/ --include="*.js" --include="*.jsx" | grep -v "\.test\."Reference Files
- `references/single-context.md` - complete migration for one context (theme, auth, etc.) with provider + class consumer + function consumer
- `references/multi-context.md` - apps with multiple legacy contexts (nested providers, multiple consumers of different contexts)
- `references/context-file-template.md` - the standard file structure for a new context module
Context File Template
Standard template for a new context module. Copy and fill in the name.
Template
// src/contexts/[Name]Context.js
import React from 'react';
// ─── 1. Default Value ───────────────────────────────────────────────────────
// Shape must match what the provider will pass as `value`
// Used when a consumer renders outside any provider (edge case protection)
const defaultValue = {
// fill in the shape
};
// ─── 2. Create Context ──────────────────────────────────────────────────────
export const [Name]Context = React.createContext(defaultValue);
// ─── 3. Display Name (for React DevTools) ───────────────────────────────────
[Name]Context.displayName = '[Name]Context';
// ─── 4. Optional: Custom Hook (strongly recommended) ────────────────────────
// Provides a clean import path and a helpful error if used outside provider
export function use[Name]() {
const context = React.useContext([Name]Context);
if (context === defaultValue) {
// Only throw if defaultValue is a sentinel - skip if a real default makes sense
// throw new Error('use[Name] must be used inside a [Name]Provider');
}
return context;
}Filled Example - AuthContext
// src/contexts/AuthContext.js
import React from 'react';
const defaultValue = {
user: null,
isAuthenticated: false,
login: () => Promise.resolve(),
logout: () => {},
};
export const AuthContext = React.createContext(defaultValue);
AuthContext.displayName = 'AuthContext';
export function useAuth() {
return React.useContext(AuthContext);
}Filled Example - ThemeContext
// src/contexts/ThemeContext.js
import React from 'react';
const defaultValue = {
theme: 'light',
toggleTheme: () => {},
};
export const ThemeContext = React.createContext(defaultValue);
ThemeContext.displayName = 'ThemeContext';
export function useTheme() {
return React.useContext(ThemeContext);
}Where to Put Context Files
src/
contexts/ ← preferred: dedicated folder
AuthContext.js
ThemeContext.jsAlternative acceptable locations:
src/context/ ← singular is also fine
src/store/contexts/ ← if co-located with state managementDo NOT put context files inside a component folder - contexts are cross-cutting and shouldn't be owned by any one component.
Provider Placement in the App
Context providers wrap the components that need access. Place as low in the tree as possible, not always at root:
// App.js
import { ThemeProvider } from './ThemeProvider';
import { AuthProvider } from './AuthProvider';
function App() {
return (
// Auth wraps everything - login state is needed everywhere
<AuthProvider>
{/* Theme wraps only the UI shell - not needed in pure data providers */}
<ThemeProvider>
<Router>
<AppShell />
</Router>
</ThemeProvider>
</AuthProvider>
);
}Multiple Legacy Contexts - Migration Reference
Identifying Multiple Contexts
A React 16/17 codebase often has several legacy contexts used for different concerns:
# Find distinct context names used in childContextTypes
grep -rn "childContextTypes" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\."
# Each hit is a separate context to migrateCommon patterns in class-heavy codebases:
- Theme context - dark/light mode, color palette
- Auth context - current user, login/logout functions
- Router context - current route, navigation (if using older react-router)
- Store context - Redux store, dispatch (if using older connect patterns)
- Locale/i18n context - language, translation function
- Toast/notification context - show/hide notifications
---
Migration Order
Migrate contexts one at a time. Each is an independent migration:
For each legacy context:
1. Create src/contexts/[Name]Context.js
2. Update the provider
3. Update all consumers
4. Run the app - verify no warning for this context
5. Move to the next contextDo not migrate all providers first then all consumers - it leaves the app in a broken intermediate state.
---
Multiple Contexts in the Same Provider
Some apps combined multiple contexts in one provider component:
// Before - one provider exports multiple context values:
class AppProvider extends React.Component {
static childContextTypes = {
theme: PropTypes.string,
user: PropTypes.object,
locale: PropTypes.string,
notifications: PropTypes.array,
};
getChildContext() {
return {
theme: this.state.theme,
user: this.state.user,
locale: this.state.locale,
notifications: this.state.notifications,
};
}
}Migration approach - split into separate contexts:
// src/contexts/ThemeContext.js
export const ThemeContext = React.createContext('light');
// src/contexts/AuthContext.js
export const AuthContext = React.createContext({ user: null, login: () => {}, logout: () => {} });
// src/contexts/LocaleContext.js
export const LocaleContext = React.createContext('en');
// src/contexts/NotificationContext.js
export const NotificationContext = React.createContext([]);// AppProvider.js - now wraps with multiple providers
import { ThemeContext } from './contexts/ThemeContext';
import { AuthContext } from './contexts/AuthContext';
import { LocaleContext } from './contexts/LocaleContext';
import { NotificationContext } from './contexts/NotificationContext';
class AppProvider extends React.Component {
render() {
const { theme, user, locale, notifications } = this.state;
return (
<ThemeContext.Provider value={theme}>
<AuthContext.Provider value={{ user, login: this.login, logout: this.logout }}>
<LocaleContext.Provider value={locale}>
<NotificationContext.Provider value={notifications}>
{this.props.children}
</NotificationContext.Provider>
</LocaleContext.Provider>
</AuthContext.Provider>
</ThemeContext.Provider>
);
}
}---
Consumer With Multiple Contexts (Class Component)
Class components can only use ONE static contextType. For multiple, use Consumer render props or convert to a function component.
Option A - Render Props (keep as class component)
import { ThemeContext } from '../contexts/ThemeContext';
import { AuthContext } from '../contexts/AuthContext';
class UserPanel extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{(theme) => (
<AuthContext.Consumer>
{({ user, logout }) => (
<div className={`panel panel-${theme}`}>
<span>{user?.name}</span>
<button onClick={logout}>Sign out</button>
</div>
)}
</AuthContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
}Option B - Convert to Function Component (preferred)
import { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
import { AuthContext } from '../contexts/AuthContext';
function UserPanel() {
const theme = useContext(ThemeContext);
const { user, logout } = useContext(AuthContext);
return (
<div className={`panel panel-${theme}`}>
<span>{user?.name}</span>
<button onClick={logout}>Sign out</button>
</div>
);
}If converting to a function component is out of scope for this migration sprint - use Option A. If the class component is simple (mostly just render), Option B is worth the minor rewrite.
---
Context File Naming Conventions
Use consistent naming across the codebase:
src/
contexts/
ThemeContext.js → exports: ThemeContext, ThemeProvider (optional)
AuthContext.js → exports: AuthContext, AuthProvider (optional)
LocaleContext.js → exports: LocaleContextEach file exports the context object. The provider can stay in its original file and just import the context.
---
Verification After All Contexts Migrated
# Should return zero hits for legacy context patterns
echo "=== childContextTypes ==="
grep -rn "childContextTypes" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\." | wc -l
echo "=== contextTypes (legacy) ==="
grep -rn "^\s*static contextTypes\s*=\|contextTypes\.propTypes" src/ --include="*.js" | grep -v "\.test\." | wc -l
echo "=== getChildContext ==="
grep -rn "getChildContext" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\." | wc -l
echo "All three should be 0"Note: static contextType (singular) is the MODERN API - that's correct. Only contextTypes (plural) is legacy.
Single Context Migration - Complete Before/After
Full Example: ThemeContext
This covers the most common pattern - one context with one provider and multiple consumers.
---
Step 1 - Before State (Legacy)
ThemeProvider.js (provider):
import PropTypes from 'prop-types';
class ThemeProvider extends React.Component {
static childContextTypes = {
theme: PropTypes.string,
toggleTheme: PropTypes.func,
};
state = { theme: 'light' };
toggleTheme = () => {
this.setState(s => ({ theme: s.theme === 'light' ? 'dark' : 'light' }));
};
getChildContext() {
return {
theme: this.state.theme,
toggleTheme: this.toggleTheme,
};
}
render() {
return this.props.children;
}
}ThemedButton.js (class consumer):
import PropTypes from 'prop-types';
class ThemedButton extends React.Component {
static contextTypes = {
theme: PropTypes.string,
toggleTheme: PropTypes.func,
};
render() {
const { theme, toggleTheme } = this.context;
return (
<button className={`btn btn-${theme}`} onClick={toggleTheme}>
Toggle Theme
</button>
);
}
}ThemedHeader.js (function consumer - if any):
// Function components couldn't use legacy context cleanly
// They had to use a class wrapper or render prop---
Step 2 - Create Context File
src/contexts/ThemeContext.js (new file):
import React from 'react';
// Default value matches the shape of getChildContext() return
export const ThemeContext = React.createContext({
theme: 'light',
toggleTheme: () => {},
});
// Named export for the context - both provider and consumers import from here---
Step 3 - Update Provider
ThemeProvider.js (after):
import React from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
class ThemeProvider extends React.Component {
state = { theme: 'light' };
toggleTheme = () => {
this.setState(s => ({ theme: s.theme === 'light' ? 'dark' : 'light' }));
};
render() {
// React 19 JSX shorthand: <ThemeContext value={...}>
// React 18: <ThemeContext.Provider value={...}>
return (
<ThemeContext.Provider
value={{
theme: this.state.theme,
toggleTheme: this.toggleTheme,
}}
>
{this.props.children}
</ThemeContext.Provider>
);
}
}
export default ThemeProvider;React 19 note: In React 19 you can write<ThemeContext value={...}>directly (no.Provider). For React 18.3.1 use<ThemeContext.Provider value={...}>.
---
Step 4 - Update Class Consumer
ThemedButton.js (after):
import React from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
class ThemedButton extends React.Component {
// singular contextType (not contextTypes)
static contextType = ThemeContext;
render() {
const { theme, toggleTheme } = this.context;
return (
<button className={`btn btn-${theme}`} onClick={toggleTheme}>
Toggle Theme
</button>
);
}
}
export default ThemedButton;Key differences from legacy:
static contextType(singular) notcontextTypes(plural)- No PropTypes declaration needed
this.contextis the full value object (not a partial - whatever you passed tovalue)- Only ONE context per class component via
contextType- useContext.Consumerrender prop for multiple
---
Step 5 - Update Function Consumer
ThemedHeader.js (after - now straightforward with hooks):
import { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
function ThemedHeader({ title }) {
const { theme } = useContext(ThemeContext);
return <h1 className={`header-${theme}`}>{title}</h1>;
}---
Step 6 - Multiple Contexts in One Class Component
If a class component consumed more than one legacy context, it gets complex. Class components can only have one static contextType. For multiple contexts, use the render prop form:
import { ThemeContext } from '../contexts/ThemeContext';
import { AuthContext } from '../contexts/AuthContext';
class Dashboard extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{({ theme }) => (
<AuthContext.Consumer>
{({ user }) => (
<div className={`dashboard-${theme}`}>
Welcome, {user.name}
</div>
)}
</AuthContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
}Or consider migrating the class component to a function component to use useContext cleanly.
---
Verification Checklist
After migrating one context:
# Provider - no legacy context exports remain
grep -n "childContextTypes\|getChildContext" src/ThemeProvider.js
# Consumers - no legacy context consumption remains
grep -rn "contextTypes\s*=" src/ --include="*.js" --include="*.jsx" | grep -v "ThemeContext\|\.test\."
# this.context usage - confirm it reads from contextType not legacy
grep -rn "this\.context\." src/ --include="*.js" | grep -v "\.test\."Each should return zero hits for the migrated context.
Related skills
How it compares
Pick react18-legacy-context when class components still use contextTypes or getChildContext and you need coordinated provider-consumer migration, not when starting fresh with function components and useContext.
FAQ
Can I migrate only the provider first?
No. Partial migration leaves consumers reading wrong or undefined context and breaks the app.
Is legacy context supported in React 19?
No. It warns in React 18.3.1 and is removed entirely in React 19.
How update class component consumers?
Set static contextType to the new context object and read this.context instead of legacy contextTypes.
Is React18 Legacy Context safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.