Upgrade React applications to latest versions

React modernization

by wshobson · MIT license · ★ 39,857 on GitHub · updated Sep 21

React modernization cover

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.

---
name: react-modernization
description: 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.
---

# React Modernization

Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.

## When to Use This Skill

- Upgrading React applications to latest versions
- Migrating class components to functional components with hooks
- Adopting concurrent React features (Suspense, transitions)
- Applying codemods for automated refactoring
- Modernizing state management patterns
- Updating to TypeScript
- Improving performance with React 18+ features

Overview

Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.

Manual install (npx, ZIP)
Claude Codenpx skills add wshobson/agents --skill react-modernization -g -a claude-code
Claude Code (manual)mkdir -p ~/.claude/skills && curl -sL https://codeload.github.com/wshobson/agents/tar.gz/4236bb91f839 \ | tar -xz -C ~/.claude/skills --strip-components=4 agents-4236bb91f839/plugins/framework-migration/skills/react-modernization
Windows (PowerShell)New-Item -ItemType Directory -Force "$HOME\.claude\skills" | Out-Null; Invoke-WebRequest "https://m.agentalley.io/zip/2aac0d667d329c1d21f1/react-modernization.zip" -OutFile "$env:TEMP\react-modernization.zip"; Expand-Archive "$env:TEMP\react-modernization.zip" -DestinationPath "$HOME\.claude\skills" -Force
Claude.ai / desktopDownload the ZIP and upload it under Settings → Capabilities → Skills.
Codex, Cursor, other agentsPoint them at SKILL.md. The other files are plain text.

Connects with Agent Alley MCP · agentalley.io/mcp

Files included

  • SKILL.md6.7 KB
  • references/1 file · 4.3 KB
Read the files here
wshobson/4236bb92 files
SKILL.md
Show the full text329 lines

React Modernization

Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.

When to Use This Skill

  • Upgrading React applications to latest versions
  • Migrating class components to functional components with hooks
  • Adopting concurrent React features (Suspense, transitions)
  • Applying codemods for automated refactoring
  • Modernizing state management patterns
  • Updating to TypeScript
  • Improving performance with React 18+ features

Version Upgrade Path

React 16 → 17 → 18

Breaking Changes by Version:

React 17:

  • Event delegation changes
  • No event pooling
  • Effect cleanup timing
  • JSX transform (no React import needed)

React 18:

  • Automatic batching
  • Concurrent rendering
  • Strict Mode changes (double invocation)
  • New root API
  • Suspense on server

Class to Hooks Migration

State Management
// 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>
  );
}
Lifecycle Methods to Hooks
// 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>;
}
Context and HOCs to Hooks
// 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>;
}

React 18 Concurrent Features

New Root API
// 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 />);
Automatic Batching
// 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
Transitions
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} />
    </>
  );
}
Suspense for Data Fetching
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} />;
}

Additional patterns and templates

More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.

1---
2name: react-modernization
3description: 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.
4---
5 
6# React Modernization
7 
8Master React version upgrades, class to hooks migration, concurrent features adoption, and codemods for automated transformation.
9 
10## When to Use This Skill
11 
12- Upgrading React applications to latest versions
13- Migrating class components to functional components with hooks
14- Adopting concurrent React features (Suspense, transitions)
15- Applying codemods for automated refactoring
16- Modernizing state management patterns
17- Updating to TypeScript
18- Improving performance with React 18+ features
19 
20## Version Upgrade Path
21 
22### React 16 → 17 → 18
23 
24**Breaking Changes by Version:**
25 
26**React 17:**
27 
28- Event delegation changes
29- No event pooling
30- Effect cleanup timing
31- JSX transform (no React import needed)
32 
33**React 18:**
34 
35- Automatic batching
36- Concurrent rendering
37- Strict Mode changes (double invocation)
38- New root API
39- Suspense on server
40 
41## Class to Hooks Migration
42 
43### State Management
44 
45```javascript
46// Before: Class component
47class Counter extends React.Component {
48 constructor(props) {
49 super(props);
50 this.state = {
51 count: 0,
52 name: "",
53 };
54 }
55 
56 increment = () => {
57 this.setState({ count: this.state.count + 1 });
58 };
59 
60 render() {
61 return (
62 <div>
63 <p>Count: {this.state.count}</p>
64 <button onClick={this.increment}>Increment</button>
65 </div>
66 );
67 }
68}
69 
70// After: Functional component with hooks
71function Counter() {
72 const [count, setCount] = useState(0);
73 const [name, setName] = useState("");
74 
75 const increment = () => {
76 setCount(count + 1);
77 };
78 
79 return (
80 <div>
81 <p>Count: {count}</p>
82 <button onClick={increment}>Increment</button>
83 </div>
84 );
85}
86```
87 
88### Lifecycle Methods to Hooks
89 
90```javascript
91// Before: Lifecycle methods
92class DataFetcher extends React.Component {
93 state = { data: null, loading: true };
94 
95 componentDidMount() {
96 this.fetchData();
97 }
98 
99 componentDidUpdate(prevProps) {
100 if (prevProps.id !== this.props.id) {
101 this.fetchData();
102 }
103 }
104 
105 componentWillUnmount() {
106 this.cancelRequest();
107 }
108 
109 fetchData = async () => {
110 const data = await fetch(`/api/${this.props.id}`);
111 this.setState({ data, loading: false });
112 };
113 
114 cancelRequest = () => {
115 // Cleanup
116 };
117 
118 render() {
119 if (this.state.loading) return <div>Loading...</div>;
120 return <div>{this.state.data}</div>;
121 }
122}
123 
124// After: useEffect hook
125function DataFetcher({ id }) {
126 const [data, setData] = useState(null);
127 const [loading, setLoading] = useState(true);
128 
129 useEffect(() => {
130 let cancelled = false;
131 
132 const fetchData = async () => {
133 try {
134 const response = await fetch(`/api/${id}`);
135 const result = await response.json();
136 
137 if (!cancelled) {
138 setData(result);
139 setLoading(false);
140 }
141 } catch (error) {
142 if (!cancelled) {
143 console.error(error);
144 }
145 }
146 };
147 
148 fetchData();
149 
150 // Cleanup function
151 return () => {
152 cancelled = true;
153 };
154 }, [id]); // Re-run when id changes
155 
156 if (loading) return <div>Loading...</div>;
157 return <div>{data}</div>;
158}
159```
160 
161### Context and HOCs to Hooks
162 
163```javascript
164// Before: Context consumer and HOC
165const ThemeContext = React.createContext();
166 
167class ThemedButton extends React.Component {
168 static contextType = ThemeContext;
169 
170 render() {
171 return (
172 <button style={{ background: this.context.theme }}>
173 {this.props.children}
174 </button>
175 );
176 }
177}
178 
179// After: useContext hook
180function ThemedButton({ children }) {
181 const { theme } = useContext(ThemeContext);
182 
183 return <button style={{ background: theme }}>{children}</button>;
184}
185 
186// Before: HOC for data fetching
187function withUser(Component) {
188 return class extends React.Component {
189 state = { user: null };
190 
191 componentDidMount() {
192 fetchUser().then((user) => this.setState({ user }));
193 }
194 
195 render() {
196 return <Component {...this.props} user={this.state.user} />;
197 }
198 };
199}
200 
201// After: Custom hook
202function useUser() {
203 const [user, setUser] = useState(null);
204 
205 useEffect(() => {
206 fetchUser().then(setUser);
207 }, []);
208 
209 return user;
210}
211 
212function UserProfile() {
213 const user = useUser();
214 if (!user) return <div>Loading...</div>;
215 return <div>{user.name}</div>;
216}
217```
218 
219## React 18 Concurrent Features
220 
221### New Root API
222 
223```javascript
224// Before: React 17
225import ReactDOM from "react-dom";
226 
227ReactDOM.render(<App />, document.getElementById("root"));
228 
229// After: React 18
230import { createRoot } from "react-dom/client";
231 
232const root = createRoot(document.getElementById("root"));
233root.render(<App />);
234```
235 
236### Automatic Batching
237 
238```javascript
239// React 18: All updates are batched
240function handleClick() {
241 setCount((c) => c + 1);
242 setFlag((f) => !f);
243 // Only one re-render (batched)
244}
245 
246// Even in async:
247setTimeout(() => {
248 setCount((c) => c + 1);
249 setFlag((f) => !f);
250 // Still batched in React 18!
251}, 1000);
252 
253// Opt out if needed
254import { flushSync } from "react-dom";
255 
256flushSync(() => {
257 setCount((c) => c + 1);
258});
259// Re-render happens here
260setFlag((f) => !f);
261// Another re-render
262```
263 
264### Transitions
265 
266```javascript
267import { useState, useTransition } from "react";
268 
269function SearchResults() {
270 const [query, setQuery] = useState("");
271 const [results, setResults] = useState([]);
272 const [isPending, startTransition] = useTransition();
273 
274 const handleChange = (e) => {
275 // Urgent: Update input immediately
276 setQuery(e.target.value);
277 
278 // Non-urgent: Update results (can be interrupted)
279 startTransition(() => {
280 setResults(searchResults(e.target.value));
281 });
282 };
283 
284 return (
285 <>
286 <input value={query} onChange={handleChange} />
287 {isPending && <Spinner />}
288 <Results data={results} />
289 </>
290 );
291}
292```
293 
294### Suspense for Data Fetching
295 
296```javascript
297import { Suspense } from "react";
298 
299// Resource-based data fetching (with React 18)
300const resource = fetchProfileData();
301 
302function ProfilePage() {
303 return (
304 <Suspense fallback={<Loading />}>
305 <ProfileDetails />
306 <Suspense fallback={<Loading />}>
307 <ProfileTimeline />
308 </Suspense>
309 </Suspense>
310 );
311}
312 
313function ProfileDetails() {
314 // This will suspend if data not ready
315 const user = resource.user.read();
316 return <h1>{user.name}</h1>;
317}
318 
319function ProfileTimeline() {
320 const posts = resource.posts.read();
321 return <Timeline posts={posts} />;
322}
323```
324 
325## Additional patterns and templates
326 
327More detailed templates and worked examples live in `references/details.md`. Read that file for the full pattern library.
328 
329 
View all files on GitHub →

Details

Updated
Sep 21, 2026
License
MIT
Stars on the repo
★ 39,857
Checked
Source
wshobson/agents ↗
Creator
wshobson · 179 listings
View creator

Similar skills

See all →
SkillManages shadcn components and projectsshadcn/uishadcn-ui/ui · by shadcn-uiManages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI, including chat interfaces. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".★ 124k GitHub starsSkillWrite agent code, build an agent with ADK, add a toolADK Code Referencegoogle/agents-cli · by googleThis skill should be used when the user wants to "write agent code", build an agent with ADK", "add a tool", "create a callback", "define an agent", use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).★ 6k GitHub starsSkillOpen and collaboratively edit a running local app in DesignVisual editBuilderIO/skills · by BuilderIOOpen and collaboratively edit a running local app in Design, with shared fallback previews and source handoff. Use when the user asks to inspect, share, or edit a real local app in Design.★ 4.5k GitHub starsSkillPatterns, cost review and ready templates for Claude ModsModsmithDannyMac180/skills · by DannyMac180Patterns, cost review and ready templates for Claude Mods (Claude Code plugins whose behaviour is a function-hooks module). Adds what the built-in mod authoring does not: proven designs (fork-check-draw, tool + ledger, mode registry, artifact-backed state), prompt-cache and token-cost review, a vetting pass for someone else's mod, and rules for mods that compose. USE WHEN: build a mod, make a claude mod, mod templates, quiz me after each turn, assumptions tool / register_assumption, supervisor that checks the turn, next-steps check, mode selector / mode registry, effort by domain, model router that keeps the cache, prompt cache cost of a mod, what does this mod cost per turn, review or vet someone's mod before installing, mods that work together, Kanban artifact as shared project state. NOT FOR: classic shell-command hooks in settings.json (PreToolUse etc.), a plain skill or slash command, MCP server authoring, or looking up the hooks API itself (use the engine's plugin-authoring guidance and generated types).★ 535 GitHub starsMCPLet your agents read, send, and react to email autonomouslyAtomic mail agenticAtomic-Mail/atomic-mail-agentic · by Atomic-MailLet your agents read, send, and react to email autonomously, without human involvement★ 266 GitHub starsSkillGenerates two genuinely distinct hook variantsA/B Hook Tester — two hooks, one variable, a real answerOotto-AI/claude-content-skills · by Ootto-AIGenerates two genuinely distinct hook variants for the same reel so you can post-test which opening actually wins. Use when the user says "give me two hooks to test", "A/B this hook", "which opening should I use", "hook variants".★ 139 GitHub stars

More from wshobson

SkillMaster C/.NET backend development patterns for buildingwshobson/agents · by wshobsonMaster C#/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications. Covers async/await, dependency injection, Entity Framework Core, Dapper, configuration, caching, and testing with xUnit. Use when developing .NET backends, reviewing C# code, or designing API architectures.★ 39.9k GitHub starsSkillImplement WCAG 2.2 compliant interfaces with mobileAccessibility compliancewshobson/agents · by wshobsonImplement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.★ 39.9k GitHub starsSkillMaster Material Design 3 and Jetpack Compose patternsAndroid Mobile Designwshobson/agents · by wshobsonMaster Material Design 3 and Jetpack Compose patterns for building native Android apps. Use when designing Android interfaces, implementing Compose UI, or following Google's Material Design guidelines.★ 39.9k GitHub starsSkillMigrate from AngularJS to Angular using hybrid modeAngular migrationwshobson/agents · by wshobsonMigrate from AngularJS to Angular using hybrid mode, incremental component rewriting, and dependency injection updates. Use when upgrading AngularJS applications, planning framework migrations, or modernizing legacy Angular code.★ 39.9k GitHub starsSkillUnderstand anti-reversing, obfuscationAnti reversing techniqueswshobson/agents · by wshobsonUnderstand anti-reversing, obfuscation, and protection techniques encountered during software analysis. Use this skill when analyzing malware evasion techniques, when implementing anti-debugging protections for CTF challenges, when reverse engineering packed binaries, or when building security research tools that need to detect virtualized environments.★ 39.9k GitHub starsSkillBuild production Apache Airflow DAGs with best practicesApache Airflow DAG Patternswshobson/agents · by wshobsonBuild production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. Use when creating data pipelines, orchestrating workflows, or scheduling batch jobs.★ 39.9k GitHub stars