# React 18 → 19 Migration Playbook
## Phase 1: Upgrade to React 18.3 and fix deprecation warnings (S)
- npm install react@18.3 react-dom@18.3
- Run the app and fix all console deprecation warnings:
- Remove string refs in src/legacy/OldModal.tsx, src/legacy/OldTooltip.tsx
- Replace ReactDOM.findDOMNode in src/legacy/DropdownMenu.tsx
- Run test suite to confirm no regressions
- Validation: Zero deprecation warnings in dev console, all tests pass
## Phase 2: Migrate forwardRef to ref-as-prop (M)
- Remove forwardRef wrappers from 23 components in src/components/ui/:
- Button.tsx, Input.tsx, Select.tsx, TextArea.tsx (high usage)
- Modal.tsx, Popover.tsx, Tooltip.tsx (medium usage)
- +16 more components (full list below)
- Pass ref as a regular prop instead of wrapping in forwardRef
- Update TypeScript types: change RefObject<T> to RefObject<T> | null
- Update src/types/refs.d.ts to match new React 19 ref semantics
- Validation: All ref-forwarding components render, focus management works
## Phase 3: Remove deprecated APIs (M)
- Replace legacy Context (contextTypes/getChildContext) in:
- src/legacy/ThemeProvider.tsx → already uses createContext (no change)
- src/legacy/LocaleContext.tsx → convert to createContext pattern
- Remove propTypes from 12 files (TypeScript handles validation)
- Remove defaultProps from function components (use JS defaults)
- Validation: Context values propagate correctly, no runtime warnings
## Phase 4: Update data fetching and Suspense patterns (L)
- Review Suspense boundaries in src/components/layouts/
- Update any components relying on fallback behavior changes
- Test lazy-loaded routes in src/routes/ with new Suspense semantics
- Validation: All lazy routes load, loading states render correctly
## Phase 5: Install React 19 and adopt new features (M)
- npm install react@19 react-dom@19
- Replace useFormState with useActionState in src/hooks/useFormField.ts
- Update react-dom imports: ReactDOM.render → already using createRoot
- Enable the new JSX transform if not already active
- Validation: Full test suite passes, app runs without errors
## Phase 6: Update third-party libraries (L)
- react-hook-form → upgrade to v8 (React 19 compatible)
- @radix-ui/react-* → upgrade to latest (ref-as-prop support)
- react-router-dom → verify v6.4+ compatibility
- Validation: Forms submit correctly, UI primitives render, routing works
## Risks
- forwardRef removal is highest-volume change — 23 components, many
consumed by other teams
- TypeScript ref type changes may surface null-safety issues
- react-hook-form v8 has its own breaking changes — test form
validation thoroughly