Best for
- Upgrading React applications to latest versions
- Migrating class components to functional components with hooks
- Adopting concurrent React features (Suspense, transitions)
wshobson/agents/plugins/framework-migration/skills/react-modernization/SKILL.md
Upgrade React applications to latest versions, migrate from class components to hooks, and adopt concurrent features. Use when modernizing React codebases, migrating to React Hooks, or upgrading to latest React versions.
Decision brief
Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/wshobson/agents --skill "plugins/framework-migration/skills/react-modernization"Inspect the Agent Skill "react-modernization" from https://github.com/wshobson/agents/blob/d82998e7df393c671ede2387a8435075f0b633f5/plugins/framework-migration/skills/react-modernization/SKILL.md at commit d82998e7df393c671ede2387a8435075f0b633f5. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Upgrading React applications to latest versions
Breaking Changes by Version:
Breaking Changes by Version:
Review the “Class to Hooks Migration” section in the pinned source before continuing.
Review the “State Management” section in the pinned source before continuing.
Permission review
The documentation includes network, browsing, or remote request actions.
const data = await fetch(`/api/${this.props.id}`);The documentation includes network, browsing, or remote request actions.
const response = await fetch(`/api/${id}`);The documentation asks the agent to read local files, directories, or repositories.
More detailed templates and worked examples live in `references/details.md`. Read that file for the full pattern library.Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 75/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 39,130 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.
Breaking Changes by Version:
React 17:
React 18:
// Before: Class component
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
name: "",
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
// After: Functional component with hooks
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState("");
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
// Before: Lifecycle methods
class DataFetcher extends React.Component {
state = { data: null, loading: true };
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
componentWillUnmount() {
this.cancelRequest();
}
fetchData = async () => {
const data = await fetch(`/api/${this.props.id}`);
this.setState({ data, loading: false });
};
cancelRequest = () => {
// Cleanup
};
render() {
if (this.state.loading) return <div>Loading...</div>;
return <div>{this.state.data}</div>;
}
}
// After: useEffect hook
function DataFetcher({ id }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
const response = await fetch(`/api/${id}`);
const result = await response.json();
if (!cancelled) {
setData(result);
setLoading(false);
}
} catch (error) {
if (!cancelled) {
console.error(error);
}
}
};
fetchData();
// Cleanup function
return () => {
cancelled = true;
};
}, [id]); // Re-run when id changes
if (loading) return <div>Loading...</div>;
return <div>{data}</div>;
}
// Before: Context consumer and HOC
const ThemeContext = React.createContext();
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return (
<button style={{ background: this.context.theme }}>
{this.props.children}
</button>
);
}
}
// After: useContext hook
function ThemedButton({ children }) {
const { theme } = useContext(ThemeContext);
return <button style={{ background: theme }}>{children}</button>;
}
// Before: HOC for data fetching
function withUser(Component) {
return class extends React.Component {
state = { user: null };
componentDidMount() {
fetchUser().then((user) => this.setState({ user }));
}
render() {
return <Component {...this.props} user={this.state.user} />;
}
};
}
// After: Custom hook
function useUser() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser);
}, []);
return user;
}
function UserProfile() {
const user = useUser();
if (!user) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
// Before: React 17
import ReactDOM from "react-dom";
ReactDOM.render(<App />, document.getElementById("root"));
// After: React 18
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render(<App />);
// React 18: All updates are batched
function handleClick() {
setCount((c) => c + 1);
setFlag((f) => !f);
// Only one re-render (batched)
}
// Even in async:
setTimeout(() => {
setCount((c) => c + 1);
setFlag((f) => !f);
// Still batched in React 18!
}, 1000);
// Opt out if needed
import { flushSync } from "react-dom";
flushSync(() => {
setCount((c) => c + 1);
});
// Re-render happens here
setFlag((f) => !f);
// Another re-render
import { useState, useTransition } from "react";
function SearchResults() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
// Urgent: Update input immediately
setQuery(e.target.value);
// Non-urgent: Update results (can be interrupted)
startTransition(() => {
setResults(searchResults(e.target.value));
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<Results data={results} />
</>
);
}
import { Suspense } from "react";
// Resource-based data fetching (with React 18)
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<Loading />}>
<ProfileDetails />
<Suspense fallback={<Loading />}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// This will suspend if data not ready
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
const posts = resource.posts.read();
return <Timeline posts={posts} />;
}
More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.
Frequently asked questions
Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.
The source record exposes this install command: npx skills add https://github.com/wshobson/agents --skill "plugins/framework-migration/skills/react-modernization". Inspect the command and pinned source before running it.
Static rules flagged network, read-files in the source; the page lists the matching lines and excerpts.
Alternatives
event4u-app/agent-config
Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.
UiPath/skills
UiPath Coded Apps — scaffold, build, run, and deploy Coded Web Apps and Coded Action Apps: React/TypeScript apps that call UiPath Cloud APIs via the `@uipath/uipath-typescript` SDK and ship to Automation Cloud (push/pull to Studio Web, pack, publish, deploy, OAuth-PKCE). Also generates live analytics & governance dashboards from a plain-language request, wired to tenant data via the Insights real-time API, with edit and deploy flows. For RPA→uipath-rpa, Python agents→uipath-agents, Maestro flows
fcakyon/claude-codex-settings
Guide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view
yonatangross/orchestkit
json-render component catalog patterns for AI-safe generative UI. Define Zod-typed catalogs that constrain what AI can generate, use @json-render/shadcn for 36 pre-built components, optimize specs with YAML mode, and apply the three edit modes (patch/merge/diff) for progressive updates. Use when building AI-generated UIs, defining component catalogs, or integrating json-render into React/Vue/Svelte/React Native/Ink/Next.js projects.