{"kind":"Skill","metadata":{"namespace":"community","name":"react19-source-patterns","version":"0.1.0"},"spec":{"description":"Reference for React 19 source-file migration patterns, including API changes, ref handling, and context updates.","files":{"SKILL.md":"---\nname: react19-source-patterns\ndescription: 'Reference for React 19 source-file migration patterns, including API changes, ref handling, and context updates.'\n---\n\n# React 19 Source Migration Patterns\n\nReference for every source-file migration required for React 19.\n\n## Quick Reference Table\n\n| Pattern | Action | Reference |\n|---|---|---|\n| `ReactDOM.render(...)` | → `createRoot().render()` | See references/api-migrations.md |\n| `ReactDOM.hydrate(...)` | → `hydrateRoot(...)` | See references/api-migrations.md |\n| `unmountComponentAtNode` | → `root.unmount()` | Inline fix |\n| `ReactDOM.findDOMNode` | → direct ref | Inline fix |\n| `forwardRef(...)` wrapper | → ref as direct prop | See references/api-migrations.md |\n| `Component.defaultProps = {}` | → ES6 default params | See references/api-migrations.md |\n| `useRef()` no arg | → `useRef(null)` | Inline fix  add `null` |\n| Legacy Context | → `createContext` | [→ api-migrations.md#legacy-context](references/api-migrations.md#legacy-context) |\n| String refs `this.refs.x` | → `createRef()` | [→ api-migrations.md#string-refs](references/api-migrations.md#string-refs) |\n| `import React from 'react'` (unused) | Remove | Only if no `React.` usage in file |\n\n## PropTypes Rule\n\nDo **not** remove `.propTypes` assignments. The `prop-types` package still works as a standalone validator. React 19 only removes the built-in runtime checking from the React package  the package itself remains valid.\n\nAdd this comment above any `.propTypes` block:\n```jsx\n// NOTE: React 19 no longer runs propTypes validation at runtime.\n// PropTypes kept for documentation and IDE tooling only.\n```\n\n## Read the Reference\n\nFor full before/after code for each migration, read **`references/api-migrations.md`**. It contains the complete patterns including edge cases for `forwardRef` with `useImperativeHandle`, `defaultProps` null vs undefined behavior, and legacy context provider/consumer cross-file migrations.\n","references/api-migrations.md":"---\ntitle: React 19 API Migrations Reference\n---\n\n# React 19 API Migrations Reference\n\nComplete before/after patterns for all React 19 breaking changes and removed APIs.\n\n---\n\n## ReactDOM Root API Migration\n\nReact 19 requires `createRoot()` or `hydrateRoot()` for all apps. If the React 18 migration already ran, this is done. Verify it's correct.\n\n### Pattern 1: createRoot()  CSR App\n\n```jsx\n// Before (React 18 or earlier):\nimport ReactDOM from 'react-dom';\nReactDOM.render(\u003cApp /\u003e, document.getElementById('root'));\n\n// After (React 19):\nimport { createRoot } from 'react-dom/client';\nconst root = createRoot(document.getElementById('root'));\nroot.render(\u003cApp /\u003e);\n```\n\n### Pattern 2: hydrateRoot()  SSR/Static App\n\n```jsx\n// Before (React 18 server-rendered app):\nimport ReactDOM from 'react-dom';\nReactDOM.hydrate(\u003cApp /\u003e, document.getElementById('root'));\n\n// After (React 19):\nimport { hydrateRoot } from 'react-dom/client';\nhydrateRoot(document.getElementById('root'), \u003cApp /\u003e);\n```\n\n### Pattern 3: unmountComponentAtNode() Removed\n\n```jsx\n// Before (React 18):\nimport ReactDOM from 'react-dom';\nReactDOM.unmountComponentAtNode(container);\n\n// After (React 19):\nconst root = createRoot(container); // Save the root reference\n// later:\nroot.unmount();\n```\n\n**Caveat:** If the root reference was never saved, you must refactor to pass it around or use a global registry.\n\n---\n\n## findDOMNode() Removed\n\n### Pattern 1: Direct ref\n\n```jsx\n// Before (React 18):\nimport { findDOMNode } from 'react-dom';\nconst domNode = findDOMNode(componentRef);\n\n// After (React 19):\nconst domNode = componentRef.current; // refs point directly to DOM\n```\n\n### Pattern 2: Class Component ref\n\n```jsx\n// Before (React 18):\nimport { findDOMNode } from 'react-dom';\nclass MyComponent extends React.Component {\n  render() {\n    return \u003cdiv ref={ref =\u003e this.node = ref}\u003eContent\u003c/div\u003e;\n  }\n  \n  getWidth() {\n    return findDOMNode(this).offsetWidth;\n  }\n}\n\n// After (React 19):\n// Note: findDOMNode() is removed in React 19. Eliminate the call entirely\n// and use direct refs to access DOM nodes instead.\nclass MyComponent extends React.Component {\n  nodeRef = React.createRef();\n  \n  render() {\n    return \u003cdiv ref={this.nodeRef}\u003eContent\u003c/div\u003e;\n  }\n  \n  getWidth() {\n    return this.nodeRef.current.offsetWidth;\n  }\n}\n```\n\n---\n\n## forwardRef() - Optional Modernization\n\n### Pattern 1: Function Component Direct ref\n\n```jsx\n// Before (React 18):\nimport { forwardRef } from 'react';\n\nconst Input = forwardRef((props, ref) =\u003e (\n  \u003cinput ref={ref} {...props} /\u003e\n));\n\nfunction App() {\n  const inputRef = useRef(null);\n  return \u003cInput ref={inputRef} /\u003e;\n}\n\n// After (React 19):\n// Simply accept ref as a regular prop:\nfunction Input({ ref, ...props }) {\n  return \u003cinput ref={ref} {...props} /\u003e;\n}\n\nfunction App() {\n  const inputRef = useRef(null);\n  return \u003cInput ref={inputRef} /\u003e;\n}\n```\n\n### Pattern 2: forwardRef + useImperativeHandle\n\n```jsx\n// Before (React 18):\nimport { forwardRef, useImperativeHandle } from 'react';\n\nconst TextInput = forwardRef((props, ref) =\u003e {\n  const inputRef = useRef();\n  \n  useImperativeHandle(ref, () =\u003e ({\n    focus: () =\u003e inputRef.current.focus(),\n    clear: () =\u003e { inputRef.current.value = ''; }\n  }));\n  \n  return \u003cinput ref={inputRef} {...props} /\u003e;\n});\n\nfunction App() {\n  const textRef = useRef(null);\n  return (\n    \u003c\u003e\n      \u003cTextInput ref={textRef} /\u003e\n      \u003cbutton onClick={() =\u003e textRef.current.focus()}\u003eFocus\u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n\n// After (React 19):\nfunction TextInput({ ref, ...props }) {\n  const inputRef = useRef(null);\n  \n  useImperativeHandle(ref, () =\u003e ({\n    focus: () =\u003e inputRef.current.focus(),\n    clear: () =\u003e { inputRef.current.value = ''; }\n  }));\n  \n  return \u003cinput ref={inputRef} {...props} /\u003e;\n}\n\nfunction App() {\n  const textRef = useRef(null);\n  return (\n    \u003c\u003e\n      \u003cTextInput ref={textRef} /\u003e\n      \u003cbutton onClick={() =\u003e textRef.current.focus()}\u003eFocus\u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n**Note:** `useImperativeHandle` is still valid; only the `forwardRef` wrapper is removed.\n\n---\n\n## defaultProps Removed\n\n### Pattern 1: Function Component with defaultProps\n\n```jsx\n// Before (React 18):\nfunction Button({ label = 'Click', disabled = false }) {\n  return \u003cbutton disabled={disabled}\u003e{label}\u003c/button\u003e;\n}\n\n// WORKS BUT is removed in React 19:\nButton.defaultProps = {\n  label: 'Click',\n  disabled: false\n};\n\n// After (React 19):\n// ES6 default params are now the ONLY way:\nfunction Button({ label = 'Click', disabled = false }) {\n  return \u003cbutton disabled={disabled}\u003e{label}\u003c/button\u003e;\n}\n\n// Remove all defaultProps assignments\n```\n\n### Pattern 2: Class Component defaultProps\n\n```jsx\n// Before (React 18):\nclass Button extends React.Component {\n  static defaultProps = {\n    label: 'Click',\n    disabled: false\n  };\n  \n  render() {\n    return \u003cbutton disabled={this.props.disabled}\u003e{this.props.label}\u003c/button\u003e;\n  }\n}\n\n// After (React 19):\n// Use default params in constructor or class field:\nclass Button extends React.Component {\n  constructor(props) {\n    super(props);\n    this.label = props.label || 'Click';\n    this.disabled = props.disabled || false;\n  }\n  \n  render() {\n    return \u003cbutton disabled={this.disabled}\u003e{this.label}\u003c/button\u003e;\n  }\n}\n\n// Or simplify to function component with ES6 defaults:\nfunction Button({ label = 'Click', disabled = false }) {\n  return \u003cbutton disabled={disabled}\u003e{label}\u003c/button\u003e;\n}\n```\n\n### Pattern 3: defaultProps with null\n\n```jsx\n// Before (React 18):\nfunction Component({ value }) {\n  // defaultProps can set null to reset a parent-passed value\n  return \u003cdiv\u003e{value}\u003c/div\u003e;\n}\n\nComponent.defaultProps = {\n  value: null\n};\n\n// After (React 19):\n// Use explicit null checks or nullish coalescing:\nfunction Component({ value = null }) {\n  return \u003cdiv\u003e{value}\u003c/div\u003e;\n}\n\n// Or:\nfunction Component({ value }) {\n  return \u003cdiv\u003e{value ?? null}\u003c/div\u003e;\n}\n```\n\n---\n\n## useRef Without Initial Value\n\n### Pattern 1: useRef()\n\n```jsx\n// Before (React 18):\nconst ref = useRef(); // undefined initially\n\n// After (React 19):\n// Explicitly pass null as initial value:\nconst ref = useRef(null);\n\n// Then use current:\nref.current = someElement; // Set it manually later\n```\n\n### Pattern 2: useRef with DOM Elements\n\n```jsx\n// Before:\nfunction Component() {\n  const inputRef = useRef();\n  return \u003cinput ref={inputRef} /\u003e;\n}\n\n// After:\nfunction Component() {\n  const inputRef = useRef(null); // Explicit null\n  return \u003cinput ref={inputRef} /\u003e;\n}\n```\n\n---\n\n## Legacy Context API Removed\n\n### Pattern 1: React.createContext vs contextTypes\n\n```jsx\n// Before (React 18  not recommended but worked):\n// Using contextTypes (old PropTypes-style context):\nclass MyComponent extends React.Component {\n  static contextTypes = {\n    theme: PropTypes.string\n  };\n  \n  render() {\n    return \u003cdiv style={{ color: this.context.theme }}\u003eText\u003c/div\u003e;\n  }\n}\n\n// Provider using getChildContext (old API):\nclass App extends React.Component {\n  static childContextTypes = {\n    theme: PropTypes.string\n  };\n  \n  getChildContext() {\n    return { theme: 'dark' };\n  }\n  \n  render() {\n    return \u003cMyComponent /\u003e;\n  }\n}\n\n// After (React 19):\n// Use createContext (modern API):\nconst ThemeContext = React.createContext(null);\n\nfunction MyComponent() {\n  const theme = useContext(ThemeContext);\n  return \u003cdiv style={{ color: theme }}\u003eText\u003c/div\u003e;\n}\n\nfunction App() {\n  return (\n    \u003cThemeContext.Provider value=\"dark\"\u003e\n      \u003cMyComponent /\u003e\n    \u003c/ThemeContext.Provider\u003e\n  );\n}\n```\n\n### Pattern 2: Class Component Consuming createContext\n\n```jsx\n// Before (class component consuming old context):\nclass MyComponent extends React.Component {\n  static contextType = ThemeContext;\n  \n  render() {\n    return \u003cdiv style={{ color: this.context }}\u003eText\u003c/div\u003e;\n  }\n}\n\n// After (still works in React 19):\n// No change needed for static contextType\n// Continue using this.context\n```\n\n**Important:** If you're still using the old `contextTypes` + `getChildContext` pattern (not modern `createContext`), you **must** migrate to `createContext`  the old pattern is completely removed.\n\n---\n\n## String Refs Removed\n\n### Pattern 1: this.refs String Refs\n\n```jsx\n// Before (React 18):\nclass Component extends React.Component {\n  render() {\n    return (\n      \u003c\u003e\n        \u003cinput ref=\"inputRef\" /\u003e\n        \u003cbutton onClick={() =\u003e this.refs.inputRef.focus()}\u003eFocus\u003c/button\u003e\n      \u003c/\u003e\n    );\n  }\n}\n\n// After (React 19):\nclass Component extends React.Component {\n  inputRef = React.createRef();\n  \n  render() {\n    return (\n      \u003c\u003e\n        \u003cinput ref={this.inputRef} /\u003e\n        \u003cbutton onClick={() =\u003e this.inputRef.current.focus()}\u003eFocus\u003c/button\u003e\n      \u003c/\u003e\n    );\n  }\n}\n```\n\n### Pattern 2: Callback Refs (Recommended)\n\n```jsx\n// Before (React 18):\nclass Component extends React.Component {\n  render() {\n    return (\n      \u003c\u003e\n        \u003cinput ref=\"inputRef\" /\u003e\n        \u003cbutton onClick={() =\u003e this.refs.inputRef.focus()}\u003eFocus\u003c/button\u003e\n      \u003c/\u003e\n    );\n  }\n}\n\n// After (React 19  callback is more flexible):\nclass Component extends React.Component {\n  constructor(props) {\n    super(props);\n    this.inputRef = null;\n  }\n  \n  render() {\n    return (\n      \u003c\u003e\n        \u003cinput ref={(el) =\u003e { this.inputRef = el; }} /\u003e\n        \u003cbutton onClick={() =\u003e this.inputRef?.focus()}\u003eFocus\u003c/button\u003e\n      \u003c/\u003e\n    );\n  }\n}\n```\n\n---\n\n## Unused React Import Removal\n\n### Pattern 1: React Import After JSX Transform\n\n```jsx\n// Before (React 18):\nimport React from 'react'; // Needed for JSX transform\n\nfunction Component() {\n  return \u003cdiv\u003eText\u003c/div\u003e;\n}\n\n// After (React 19 with new JSX transform):\n// Remove the React import if it's not used:\nfunction Component() {\n  return \u003cdiv\u003eText\u003c/div\u003e;\n}\n\n// BUT keep it if you use React.* APIs:\nimport React from 'react';\n\nfunction Component() {\n  return \u003cdiv\u003e{React.useState ? 'yes' : 'no'}\u003c/div\u003e;\n}\n```\n\n### Scan for Unused React Imports\n\n```bash\n# Find imports that can be removed:\ngrep -rn \"^import React from 'react';\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Then check if the file uses React.*, useContext, etc.\n```\n\n---\n\n## Complete Migration Checklist\n\n```bash\n# 1. Find all ReactDOM.render calls:\ngrep -rn \"ReactDOM.render\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Should be converted to createRoot\n\n# 2. Find all ReactDOM.hydrate calls:\ngrep -rn \"ReactDOM.hydrate\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Should be converted to hydrateRoot\n\n# 3. Find all forwardRef usages:\ngrep -rn \"forwardRef\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Check each one to see if it can be removed (most can)\n\n# 4. Find all .defaultProps assignments:\ngrep -rn \"\\.defaultProps\\s*=\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Replace with ES6 default params\n\n# 5. Find all useRef() without initial value:\ngrep -rn \"useRef()\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Add null: useRef(null)\n\n# 6. Find old context (contextTypes):\ngrep -rn \"contextTypes\\|childContextTypes\\|getChildContext\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Migrate to createContext\n\n# 7. Find string refs (ref=\"name\"):\ngrep -rn 'ref=\"' src/ --include=\"*.js\" --include=\"*.jsx\"\n# Migrate to createRef or callback ref\n\n# 8. Find unused React imports:\ngrep -rn \"^import React from 'react';\" src/ --include=\"*.js\" --include=\"*.jsx\"\n# Check if React is used in the file\n```\n"},"import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/tree/541b7819d8c3545c6df122491af4fa1eae415779/plugins/react19-upgrade/skills/react19-source-patterns"}},"content_hash":[69,55,221,144,142,45,246,6,231,182,132,213,224,92,198,245,230,80,117,12,113,49,6,71,91,238,237,11,82,69,73,215],"trust_level":"unsigned","yanked":false}
