React 19 source migration patterns
Migrate React source files away from React 19 removed and changed APIs by choosing the right before/after pattern, preserving behavior, and summarizing the code-level transformations.
Keep createRef() available for class component refs, and migrate provider/consumer pairs together for legacy context.
When to invoke
- "Fix React 19 source migration issues."
- "Convert ReactDOM.render to createRoot."
- "Replace findDOMNode and string refs for React 19."
- "Modernize forwardRef and useRef for React 19."
- "Handle defaultProps and propTypes during React 19 migration."
Quick reference table
| Pattern |
Action |
Reference |
ReactDOM.render(...) |
Replace with createRoot().render(). |
references/api-migrations.md |
ReactDOM.hydrate(...) |
Replace with hydrateRoot(...). |
references/api-migrations.md |
unmountComponentAtNode |
Replace with root.unmount(). |
Inline fix; keep or introduce a saved root reference. |
ReactDOM.findDOMNode |
Replace with a direct ref. |
Inline fix; refactor class components to hold DOM refs. |
forwardRef(...) wrapper |
Prefer ref as direct prop when targeting React 19. |
references/api-migrations.md |
Component.defaultProps = {} |
Replace function component defaults with ES6 default params. |
references/api-migrations.md |
useRef() no arg |
Replace with useRef(null). |
Inline fix: add null. |
| Legacy Context |
Replace contextTypes, childContextTypes, and getChildContext with createContext. |
references/api-migrations.md#legacy-context |
String refs this.refs.x |
Replace with React.createRef() or callback refs. |
references/api-migrations.md#string-refs |
import React from 'react' unused |
Remove only when no React. usage remains in the file. |
Verify per file. |
Source migration rules
| API or pattern |
Correct React 19 replacement |
Verification |
ReactDOM.render(<App />, el) |
import { createRoot } from 'react-dom/client'; const root = createRoot(el); root.render(<App />); |
Confirm the root is created once per container. |
ReactDOM.hydrate(<App />, el) |
import { hydrateRoot } from 'react-dom/client'; hydrateRoot(el, <App />); |
Keep server-rendered markup assumptions intact. |
unmountComponentAtNode(container) |
Save the root from createRoot(container) and call root.unmount(). |
Refactor ownership if no root reference exists. |
findDOMNode(componentRef) |
Attach a direct DOM ref and read ref.current. |
Avoid replacing it with document queries. |
forwardRef |
Accept ref as a direct prop for React 19 components. |
Preserve useImperativeHandle behavior when present. |
Function defaultProps |
Use parameter defaults or nullish coalescing. |
Preserve null versus undefined behavior. |
useRef() |
useRef(null) |
Check any TypeScript type expectations. |
| Legacy context |
React.createContext, provider value, and useContext or static contextType. |
Migrate provider and consumers together. |
| String refs |
React.createRef() fields or callback refs. |
Pair ref="x" with this.refs.x. |
PropTypes rule
Do not remove .propTypes assignments just because React 19 stops running built-in runtime checking. The prop-types package still works as a standalone validator and remains useful for documentation and IDE tooling.
Add this comment above any .propTypes block that remains:
// NOTE: React 19 no longer runs propTypes validation at runtime.
// PropTypes kept for documentation and IDE tooling only.
Progressive disclosure and bundled resources
references/api-migrations.md: complete before/after code for root APIs, findDOMNode, forwardRef with useImperativeHandle, defaultProps, useRef, legacy context, string refs, and unused React imports.
Gotchas
- Do not remove
import React from 'react' blindly: keep it when the file uses React.Component, React.createRef, React.createContext, React.useState, or any other React. API.
- Class component
defaultProps needs judgment: function component defaults are the primary removal target; preserve behavior for classes unless a safe refactor is clear.
useImperativeHandle remains valid: only the forwardRef wrapper changes when ref becomes a prop.
- Legacy context is cross-file: provider and consumer migrations must be coordinated.
Output template
## React 19 source migration result
**Status:** migrated | planned | blocked
**Scope:** `<files or components>`
| File | Pattern | Replacement | Notes |
| --- | --- | --- | --- |
| `<path>` | `ReactDOM.render(...)` | `createRoot().render()` | `<root ownership>` |
### PropTypes
- `<file>`: kept with React 19 note | not present | removed for another justified reason
### Validation
- Migration scan: `<remaining hits or none>`
- Tests/build: `<command and result, or not run>`
Quality gate
1---2name: react19-source-patterns3description: Apply React 19 source-file migration patterns for root APIs, hydration, unmounting, findDOMNode, forwardRef, defaultProps, useRef initial values, legacy context, string refs, propTypes comments, and unused React imports. Use this skill when fixing React 19 source migration issues.4---56<!-- Generated from harness/github-copilot/skills/react19-source-patterns/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->78# React 19 source migration patterns910Migrate React source files away from React 19 removed and changed APIs by choosing the right before/after pattern, preserving behavior, and summarizing the code-level transformations.1112Keep `createRef()` available for class component refs, and migrate provider/consumer pairs together for legacy context.1314## When to invoke1516- "Fix React 19 source migration issues."17- "Convert ReactDOM.render to createRoot."18- "Replace findDOMNode and string refs for React 19."19- "Modernize forwardRef and useRef for React 19."20- "Handle defaultProps and propTypes during React 19 migration."2122## Quick reference table2324| Pattern | Action | Reference |25| --- | --- | --- |26| `ReactDOM.render(...)` | Replace with `createRoot().render()`. | `references/api-migrations.md` |27| `ReactDOM.hydrate(...)` | Replace with `hydrateRoot(...)`. | `references/api-migrations.md` |28| `unmountComponentAtNode` | Replace with `root.unmount()`. | Inline fix; keep or introduce a saved root reference. |29| `ReactDOM.findDOMNode` | Replace with a direct ref. | Inline fix; refactor class components to hold DOM refs. |30| `forwardRef(...)` wrapper | Prefer ref as direct prop when targeting React 19. | `references/api-migrations.md` |31| `Component.defaultProps = {}` | Replace function component defaults with ES6 default params. | `references/api-migrations.md` |32| `useRef()` no arg | Replace with `useRef(null)`. | Inline fix: add `null`. |33| Legacy Context | Replace `contextTypes`, `childContextTypes`, and `getChildContext` with `createContext`. | `references/api-migrations.md#legacy-context` |34| String refs `this.refs.x` | Replace with `React.createRef()` or callback refs. | `references/api-migrations.md#string-refs` |35| `import React from 'react'` unused | Remove only when no `React.` usage remains in the file. | Verify per file. |3637## Source migration rules3839| API or pattern | Correct React 19 replacement | Verification |40| --- | --- | --- |41| `ReactDOM.render(<App />, el)` | `import { createRoot } from 'react-dom/client'; const root = createRoot(el); root.render(<App />);` | Confirm the root is created once per container. |42| `ReactDOM.hydrate(<App />, el)` | `import { hydrateRoot } from 'react-dom/client'; hydrateRoot(el, <App />);` | Keep server-rendered markup assumptions intact. |43| `unmountComponentAtNode(container)` | Save the root from `createRoot(container)` and call `root.unmount()`. | Refactor ownership if no root reference exists. |44| `findDOMNode(componentRef)` | Attach a direct DOM ref and read `ref.current`. | Avoid replacing it with document queries. |45| `forwardRef` | Accept `ref` as a direct prop for React 19 components. | Preserve `useImperativeHandle` behavior when present. |46| Function `defaultProps` | Use parameter defaults or nullish coalescing. | Preserve `null` versus `undefined` behavior. |47| `useRef()` | `useRef(null)` | Check any TypeScript type expectations. |48| Legacy context | `React.createContext`, provider value, and `useContext` or `static contextType`. | Migrate provider and consumers together. |49| String refs | `React.createRef()` fields or callback refs. | Pair `ref="x"` with `this.refs.x`. |5051## PropTypes rule5253Do **not** remove `.propTypes` assignments just because React 19 stops running built-in runtime checking. The `prop-types` package still works as a standalone validator and remains useful for documentation and IDE tooling.5455Add this comment above any `.propTypes` block that remains:5657```jsx58// NOTE: React 19 no longer runs propTypes validation at runtime.59// PropTypes kept for documentation and IDE tooling only.60```6162## Progressive disclosure and bundled resources6364- `references/api-migrations.md`: complete before/after code for root APIs, `findDOMNode`, `forwardRef` with `useImperativeHandle`, `defaultProps`, `useRef`, legacy context, string refs, and unused React imports.6566## Gotchas6768- **Do not remove `import React from 'react'` blindly**: keep it when the file uses `React.Component`, `React.createRef`, `React.createContext`, `React.useState`, or any other `React.` API.69- **Class component `defaultProps` needs judgment**: function component defaults are the primary removal target; preserve behavior for classes unless a safe refactor is clear.70- **`useImperativeHandle` remains valid**: only the `forwardRef` wrapper changes when ref becomes a prop.71- **Legacy context is cross-file**: provider and consumer migrations must be coordinated.7273## Output template7475```markdown76## React 19 source migration result7778**Status:** migrated | planned | blocked79**Scope:** `<files or components>`8081| File | Pattern | Replacement | Notes |82| --- | --- | --- | --- |83| `<path>` | `ReactDOM.render(...)` | `createRoot().render()` | `<root ownership>` |8485### PropTypes86- `<file>`: kept with React 19 note | not present | removed for another justified reason8788### Validation89- Migration scan: `<remaining hits or none>`90- Tests/build: `<command and result, or not run>`91```9293## Quality gate9495- [ ] Every changed API is mapped to the React 19 replacement table above.96- [ ] `references/api-migrations.md` was consulted for non-trivial changes.97- [ ] `.propTypes` assignments are preserved unless there is a separate, explicit reason to remove them.98- [ ] Remaining `import React from 'react'` lines are justified by `React.` usage; removed imports have no `React.` usage.99- [ ] `useRef()` calls in scope now pass an explicit initial value such as `null`.100- [ ] Root API migrations preserve root ownership for later `root.unmount()`.101- [ ] Follow-up scans or tests are reported.