React Modernization

Upgrade React apps to latest versions with automated hooks migration

✨ The solution you've been looking for

Verified
Tested and verified by our team
25450 Stars

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 hooks migration modernization react-18 codemods typescript performance
Repository

See It In Action

Interactive preview & real-world examples

Live Demo
Skill Demo Animation

AI Conversation Simulator

See how users interact with this skill

User Prompt

I have a React class component with state and lifecycle methods. Help me migrate it to use hooks while maintaining the same functionality.

Skill Processing

Analyzing request...

Agent Response

Step-by-step migration guide with before/after code examples, proper hook usage patterns, and testing recommendations

Quick Start (3 Steps)

Get up and running in minutes

1

Install

claude-code skill install react-modernization

claude-code skill install react-modernization
2

Config

3

First Trigger

@react-modernization help

Commands

CommandDescriptionRequired Args
@react-modernization legacy-class-component-migrationConvert existing React class components to modern functional components with hooksNone
@react-modernization react-18-upgrade-pathUpgrade from React 16/17 to React 18 with concurrent features adoptionNone
@react-modernization automated-codemod-applicationUse codemods to automatically refactor large codebases during React upgradesNone

Typical Use Cases

Legacy Class Component Migration

Convert existing React class components to modern functional components with hooks

React 18 Upgrade Path

Upgrade from React 16/17 to React 18 with concurrent features adoption

Automated Codemod Application

Use codemods to automatically refactor large codebases during React upgrades

Overview

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

 1// Before: Class component
 2class Counter extends React.Component {
 3  constructor(props) {
 4    super(props);
 5    this.state = {
 6      count: 0,
 7      name: "",
 8    };
 9  }
10
11  increment = () => {
12    this.setState({ count: this.state.count + 1 });
13  };
14
15  render() {
16    return (
17      <div>
18        <p>Count: {this.state.count}</p>
19        <button onClick={this.increment}>Increment</button>
20      </div>
21    );
22  }
23}
24
25// After: Functional component with hooks
26function Counter() {
27  const [count, setCount] = useState(0);
28  const [name, setName] = useState("");
29
30  const increment = () => {
31    setCount(count + 1);
32  };
33
34  return (
35    <div>
36      <p>Count: {count}</p>
37      <button onClick={increment}>Increment</button>
38    </div>
39  );
40}

Lifecycle Methods to Hooks

 1// Before: Lifecycle methods
 2class DataFetcher extends React.Component {
 3  state = { data: null, loading: true };
 4
 5  componentDidMount() {
 6    this.fetchData();
 7  }
 8
 9  componentDidUpdate(prevProps) {
10    if (prevProps.id !== this.props.id) {
11      this.fetchData();
12    }
13  }
14
15  componentWillUnmount() {
16    this.cancelRequest();
17  }
18
19  fetchData = async () => {
20    const data = await fetch(`/api/${this.props.id}`);
21    this.setState({ data, loading: false });
22  };
23
24  cancelRequest = () => {
25    // Cleanup
26  };
27
28  render() {
29    if (this.state.loading) return <div>Loading...</div>;
30    return <div>{this.state.data}</div>;
31  }
32}
33
34// After: useEffect hook
35function DataFetcher({ id }) {
36  const [data, setData] = useState(null);
37  const [loading, setLoading] = useState(true);
38
39  useEffect(() => {
40    let cancelled = false;
41
42    const fetchData = async () => {
43      try {
44        const response = await fetch(`/api/${id}`);
45        const result = await response.json();
46
47        if (!cancelled) {
48          setData(result);
49          setLoading(false);
50        }
51      } catch (error) {
52        if (!cancelled) {
53          console.error(error);
54        }
55      }
56    };
57
58    fetchData();
59
60    // Cleanup function
61    return () => {
62      cancelled = true;
63    };
64  }, [id]); // Re-run when id changes
65
66  if (loading) return <div>Loading...</div>;
67  return <div>{data}</div>;
68}

Context and HOCs to Hooks

 1// Before: Context consumer and HOC
 2const ThemeContext = React.createContext();
 3
 4class ThemedButton extends React.Component {
 5  static contextType = ThemeContext;
 6
 7  render() {
 8    return (
 9      <button style={{ background: this.context.theme }}>
10        {this.props.children}
11      </button>
12    );
13  }
14}
15
16// After: useContext hook
17function ThemedButton({ children }) {
18  const { theme } = useContext(ThemeContext);
19
20  return <button style={{ background: theme }}>{children}</button>;
21}
22
23// Before: HOC for data fetching
24function withUser(Component) {
25  return class extends React.Component {
26    state = { user: null };
27
28    componentDidMount() {
29      fetchUser().then((user) => this.setState({ user }));
30    }
31
32    render() {
33      return <Component {...this.props} user={this.state.user} />;
34    }
35  };
36}
37
38// After: Custom hook
39function useUser() {
40  const [user, setUser] = useState(null);
41
42  useEffect(() => {
43    fetchUser().then(setUser);
44  }, []);
45
46  return user;
47}
48
49function UserProfile() {
50  const user = useUser();
51  if (!user) return <div>Loading...</div>;
52  return <div>{user.name}</div>;
53}

React 18 Concurrent Features

New Root API

 1// Before: React 17
 2import ReactDOM from "react-dom";
 3
 4ReactDOM.render(<App />, document.getElementById("root"));
 5
 6// After: React 18
 7import { createRoot } from "react-dom/client";
 8
 9const root = createRoot(document.getElementById("root"));
10root.render(<App />);

Automatic Batching

 1// React 18: All updates are batched
 2function handleClick() {
 3  setCount((c) => c + 1);
 4  setFlag((f) => !f);
 5  // Only one re-render (batched)
 6}
 7
 8// Even in async:
 9setTimeout(() => {
10  setCount((c) => c + 1);
11  setFlag((f) => !f);
12  // Still batched in React 18!
13}, 1000);
14
15// Opt out if needed
16import { flushSync } from "react-dom";
17
18flushSync(() => {
19  setCount((c) => c + 1);
20});
21// Re-render happens here
22setFlag((f) => !f);
23// Another re-render

Transitions

 1import { useState, useTransition } from "react";
 2
 3function SearchResults() {
 4  const [query, setQuery] = useState("");
 5  const [results, setResults] = useState([]);
 6  const [isPending, startTransition] = useTransition();
 7
 8  const handleChange = (e) => {
 9    // Urgent: Update input immediately
10    setQuery(e.target.value);
11
12    // Non-urgent: Update results (can be interrupted)
13    startTransition(() => {
14      setResults(searchResults(e.target.value));
15    });
16  };
17
18  return (
19    <>
20      <input value={query} onChange={handleChange} />
21      {isPending && <Spinner />}
22      <Results data={results} />
23    </>
24  );
25}

Suspense for Data Fetching

 1import { Suspense } from "react";
 2
 3// Resource-based data fetching (with React 18)
 4const resource = fetchProfileData();
 5
 6function ProfilePage() {
 7  return (
 8    <Suspense fallback={<Loading />}>
 9      <ProfileDetails />
10      <Suspense fallback={<Loading />}>
11        <ProfileTimeline />
12      </Suspense>
13    </Suspense>
14  );
15}
16
17function ProfileDetails() {
18  // This will suspend if data not ready
19  const user = resource.user.read();
20  return <h1>{user.name}</h1>;
21}
22
23function ProfileTimeline() {
24  const posts = resource.posts.read();
25  return <Timeline posts={posts} />;
26}

Codemods for Automation

Run React Codemods

 1# Install jscodeshift
 2npm install -g jscodeshift
 3
 4# React 16.9 codemod (rename unsafe lifecycle methods)
 5npx react-codeshift <transform> <path>
 6
 7# Example: Rename UNSAFE_ methods
 8npx react-codeshift --parser=tsx \
 9  --transform=react-codeshift/transforms/rename-unsafe-lifecycles.js \
10  src/
11
12# Update to new JSX Transform (React 17+)
13npx react-codeshift --parser=tsx \
14  --transform=react-codeshift/transforms/new-jsx-transform.js \
15  src/
16
17# Class to Hooks (third-party)
18npx codemod react/hooks/convert-class-to-function src/

Custom Codemod Example

 1// custom-codemod.js
 2module.exports = function (file, api) {
 3  const j = api.jscodeshift;
 4  const root = j(file.source);
 5
 6  // Find setState calls
 7  root
 8    .find(j.CallExpression, {
 9      callee: {
10        type: "MemberExpression",
11        property: { name: "setState" },
12      },
13    })
14    .forEach((path) => {
15      // Transform to useState
16      // ... transformation logic
17    });
18
19  return root.toSource();
20};
21
22// Run: jscodeshift -t custom-codemod.js src/

Performance Optimization

useMemo and useCallback

 1function ExpensiveComponent({ items, filter }) {
 2  // Memoize expensive calculation
 3  const filteredItems = useMemo(() => {
 4    return items.filter((item) => item.category === filter);
 5  }, [items, filter]);
 6
 7  // Memoize callback to prevent child re-renders
 8  const handleClick = useCallback((id) => {
 9    console.log("Clicked:", id);
10  }, []); // No dependencies, never changes
11
12  return <List items={filteredItems} onClick={handleClick} />;
13}
14
15// Child component with memo
16const List = React.memo(({ items, onClick }) => {
17  return items.map((item) => (
18    <Item key={item.id} item={item} onClick={onClick} />
19  ));
20});

Code Splitting

 1import { lazy, Suspense } from "react";
 2
 3// Lazy load components
 4const Dashboard = lazy(() => import("./Dashboard"));
 5const Settings = lazy(() => import("./Settings"));
 6
 7function App() {
 8  return (
 9    <Suspense fallback={<Loading />}>
10      <Routes>
11        <Route path="/dashboard" element={<Dashboard />} />
12        <Route path="/settings" element={<Settings />} />
13      </Routes>
14    </Suspense>
15  );
16}

TypeScript Migration

 1// Before: JavaScript
 2function Button({ onClick, children }) {
 3  return <button onClick={onClick}>{children}</button>;
 4}
 5
 6// After: TypeScript
 7interface ButtonProps {
 8  onClick: () => void;
 9  children: React.ReactNode;
10}
11
12function Button({ onClick, children }: ButtonProps) {
13  return <button onClick={onClick}>{children}</button>;
14}
15
16// Generic components
17interface ListProps<T> {
18  items: T[];
19  renderItem: (item: T) => React.ReactNode;
20}
21
22function List<T>({ items, renderItem }: ListProps<T>) {
23  return <>{items.map(renderItem)}</>;
24}

Migration Checklist

 1### Pre-Migration
 2
 3- [ ] Update dependencies incrementally (not all at once)
 4- [ ] Review breaking changes in release notes
 5- [ ] Set up testing suite
 6- [ ] Create feature branch
 7
 8### Class → Hooks Migration
 9
10- [ ] Identify class components to migrate
11- [ ] Start with leaf components (no children)
12- [ ] Convert state to useState
13- [ ] Convert lifecycle to useEffect
14- [ ] Convert context to useContext
15- [ ] Extract custom hooks
16- [ ] Test thoroughly
17
18### React 18 Upgrade
19
20- [ ] Update to React 17 first (if needed)
21- [ ] Update react and react-dom to 18
22- [ ] Update @types/react if using TypeScript
23- [ ] Change to createRoot API
24- [ ] Test with StrictMode (double invocation)
25- [ ] Address concurrent rendering issues
26- [ ] Adopt Suspense/Transitions where beneficial
27
28### Performance
29
30- [ ] Identify performance bottlenecks
31- [ ] Add React.memo where appropriate
32- [ ] Use useMemo/useCallback for expensive operations
33- [ ] Implement code splitting
34- [ ] Optimize re-renders
35
36### Testing
37
38- [ ] Update test utilities (React Testing Library)
39- [ ] Test with React 18 features
40- [ ] Check for warnings in console
41- [ ] Performance testing

Resources

  • references/breaking-changes.md: Version-specific breaking changes
  • references/codemods.md: Codemod usage guide
  • references/hooks-migration.md: Comprehensive hooks patterns
  • references/concurrent-features.md: React 18 concurrent features
  • assets/codemod-config.json: Codemod configurations
  • assets/migration-checklist.md: Step-by-step checklist
  • scripts/apply-codemods.sh: Automated codemod script

Best Practices

  1. Incremental Migration: Don’t migrate everything at once
  2. Test Thoroughly: Comprehensive testing at each step
  3. Use Codemods: Automate repetitive transformations
  4. Start Simple: Begin with leaf components
  5. Leverage StrictMode: Catch issues early
  6. Monitor Performance: Measure before and after
  7. Document Changes: Keep migration log

Common Pitfalls

  • Forgetting useEffect dependencies
  • Over-using useMemo/useCallback
  • Not handling cleanup in useEffect
  • Mixing class and functional patterns
  • Ignoring StrictMode warnings
  • Breaking change assumptions

What Users Are Saying

Real feedback from the community

Environment Matrix

Dependencies

React 16+ (source)
React 18+ (target)
Node.js 14+
jscodeshift (for codemods)
react-codeshift

Framework Support

Create React App ✓ Next.js ✓ (recommended) Vite ✓ Webpack ✓ TypeScript ✓ (recommended)

Context Window

Token Usage ~5K-15K tokens for complex component migrations

Security & Privacy

Information

Author
wshobson
Updated
2026-01-30
Category
automation-tools