react-modernizationUpgrade React apps by migrating class components to hooks, adopting React 18/19 concurrent features, running codemods, and adding TypeScript types.
Install via ClawdBot CLI:
clawdbot install wpank/react-modernizationUpgrade React applications from class components to hooks, adopt concurrent features, and migrate between major versions.
Systematic patterns for modernizing React codebases:
react upgrade, class to hooks, useEffect, useState, react 18, react 19, concurrent, suspense, transition, codemod, migrate, modernize, functional component
npx clawhub@latest install react-modernization
| Change | Impact | Migration |
|--------|--------|-----------|
| New root API | Required | ReactDOM.render → createRoot |
| Automatic batching | Behavior | State updates batch in async code now |
| Strict Mode | Dev only | Effects fire twice (mount/unmount/mount) |
| Suspense on server | Optional | Enable SSR streaming |
| Change | Impact | Migration |
|--------|--------|-----------|
| use() hook | New API | Read promises/context in render |
| ref as prop | Simplified | No more forwardRef needed |
| Context as provider | Simplified | not |
| Async actions | New pattern | useActionState, useOptimistic |
// componentDidMount → useEffect with empty deps
useEffect(() => {
fetchData()
}, [])
// componentDidUpdate → useEffect with deps
useEffect(() => {
updateWhenIdChanges()
}, [id])
// componentWillUnmount → useEffect cleanup
useEffect(() => {
const subscription = subscribe()
return () => subscription.unsubscribe()
}, [])
// shouldComponentUpdate → React.memo
const Component = React.memo(({ data }) => <div>{data}</div>)
// getDerivedStateFromProps → useMemo
const derivedValue = useMemo(() => computeFrom(props), [props])
// BEFORE: Class with multiple state properties
class UserProfile extends React.Component {
state = { user: null, loading: true, error: null }
componentDidMount() {
fetchUser(this.props.id)
.then(user => this.setState({ user, loading: false }))
.catch(error => this.setState({ error, loading: false }))
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.setState({ loading: true })
fetchUser(this.props.id)
.then(user => this.setState({ user, loading: false }))
}
}
render() {
const { user, loading, error } = this.state
if (loading) return <Spinner />
if (error) return <Error message={error.message} />
return <Profile user={user} />
}
}
// AFTER: Custom hook + functional component
function useUser(id: string) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
let cancelled = false
setLoading(true)
fetchUser(id)
.then(data => {
if (!cancelled) {
setUser(data)
setLoading(false)
}
})
.catch(err => {
if (!cancelled) {
setError(err)
setLoading(false)
}
})
return () => { cancelled = true }
}, [id])
return { user, loading, error }
}
function UserProfile({ id }: { id: string }) {
const { user, loading, error } = useUser(id)
if (loading) return <Spinner />
if (error) return <Error message={error.message} />
return <Profile user={user} />
}
// BEFORE: Higher-Order Component
function withUser(Component) {
return function WithUser(props) {
const [user, setUser] = useState(null)
useEffect(() => { fetchUser().then(setUser) }, [])
return <Component {...props} user={user} />
}
}
const ProfileWithUser = withUser(Profile)
// AFTER: Custom hook (simpler, composable)
function useCurrentUser() {
const [user, setUser] = useState(null)
useEffect(() => { fetchUser().then(setUser) }, [])
return user
}
function Profile() {
const user = useCurrentUser()
return user ? <div>{user.name}</div> : null
}
// 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 />)
function SearchResults() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
// Urgent: update input immediately
setQuery(e.target.value)
// Non-urgent: can be interrupted
startTransition(() => {
setResults(searchDatabase(e.target.value))
})
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending ? <Spinner /> : <ResultsList data={results} />}
</>
)
}
// With React 19's use() hook
function ProfilePage({ userId }: { userId: string }) {
return (
<Suspense fallback={<ProfileSkeleton />}>
<ProfileDetails userId={userId} />
</Suspense>
)
}
function ProfileDetails({ userId }: { userId: string }) {
// use() suspends until promise resolves
const user = use(fetchUser(userId))
return <h1>{user.name}</h1>
}
// Read promises directly in render
function Comments({ commentsPromise }) {
const comments = use(commentsPromise)
return comments.map(c => <Comment key={c.id} {...c} />)
}
// Read context (simpler than useContext)
function ThemeButton() {
const theme = use(ThemeContext)
return <button className={theme}>Click</button>
}
// useActionState for form submissions
function UpdateName() {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get('name'))
if (error) return error
redirect('/profile')
},
null
)
return (
<form action={submitAction}>
<input name="name" />
<button disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
)
}
# Update to new JSX transform (no React import needed)
npx codemod@latest react/19/replace-reactdom-render
# Update deprecated APIs
npx codemod@latest react/19/replace-string-ref
# Class to function components
npx codemod@latest react/19/replace-use-form-state
# Find class components
rg "class \w+ extends (React\.)?Component" --type tsx
# Find deprecated lifecycle methods
rg "componentWillMount|componentWillReceiveProps|componentWillUpdate" --type tsx
# Find ReactDOM.render (needs migration to createRoot)
rg "ReactDOM\.render" --type tsx
// Add types to functional components
interface ButtonProps {
onClick: () => void
children: React.ReactNode
variant?: 'primary' | 'secondary'
}
function Button({ onClick, children, variant = 'primary' }: ButtonProps) {
return (
<button onClick={onClick} className={variant}>
{children}
</button>
)
}
// Type event handlers
function Form() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
</form>
)
}
// Generic components
interface ListProps<T> {
items: T[]
renderItem: (item: T) => React.ReactNode
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <>{items.map(renderItem)}</>
}
useStateuseEffectcreateRoot API// eslint-disable-next-line react-hooks/exhaustive-deps without understanding whyGenerated Mar 1, 2026
A large financial services company needs to modernize its React 16-based internal dashboard to React 19 to leverage concurrent features for better performance. The app has hundreds of class components that must be migrated to hooks while maintaining business logic and integrating TypeScript for type safety.
An e-commerce site built with React 17 experiences slow page loads during high traffic. The team aims to upgrade to React 18, implement Suspense for code-splitting and data fetching, and refactor HOCs to custom hooks to improve user experience and reduce bounce rates.
A SaaS provider wants to enhance its React 17 application by adopting React 18's useTransition for smoother UI updates during data filtering and search operations. They plan to migrate class components to functional components and use automated codemods for bulk refactoring to minimize downtime.
A healthcare portal built with React 16 and class components requires an upgrade to React 19 to support new features like the use() hook for asynchronous data handling. The migration includes adding TypeScript for better code maintainability and converting lifecycle methods to hooks for improved performance.
A media streaming app uses React 17 with render props and HOCs, leading to complex component hierarchies. The goal is to migrate to React 18, replace render props with custom hooks for state management, and implement Suspense for lazy-loading video components to enhance streaming efficiency.
Offer specialized consulting to enterprises for React modernization projects, including code audits, migration planning, and hands-on refactoring. This model generates revenue through hourly or project-based fees, targeting companies with legacy codebases seeking expert guidance.
Provide training sessions and workshops to development teams on migrating from class components to hooks, adopting concurrent features, and using TypeScript with React. Revenue comes from per-attendee fees or corporate packages, helping teams upskill efficiently.
Develop and sell automated codemods or tools that assist in bulk refactoring of React codebases, such as converting class components to hooks or upgrading between major versions. Revenue is generated through one-time purchases or subscription licenses for continuous updates.
💬 Integration Tip
Start by running automated codemods for simple class-to-hook conversions, then manually refactor complex logic. Integrate TypeScript gradually to avoid breaking changes.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Expert frontend design guidelines for creating beautiful, modern UIs. Use when building landing pages, dashboards, or any user interface.
Use when building UI with shadcn/ui components, Tailwind CSS layouts, form patterns with react-hook-form and zod, theming, dark mode, sidebar layouts, mobile navigation, or any shadcn component question.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when building web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Create distinctive, production-grade static sites with React, Tailwind CSS, and shadcn/ui — no mockups needed. Generates bold, memorable designs from plain text requirements with anti-AI-slop aesthetics, mobile-first responsive patterns, and single-file bundling. Use when building landing pages, marketing sites, portfolios, dashboards, or any static web UI. Supports both Vite (pure static) and Next.js (Vercel deploy) workflows.
AI skill for automated UI audits. Evaluate interfaces against proven UX principles for visual hierarchy, accessibility, cognitive load, navigation, and more. Based on Making UX Decisions by Tommy Geoco.