Skip to content

Upgrade jac-client from React 18 to React 19 #5454

@ahzan-dev

Description

@ahzan-dev

Summary

Upgrade jac-client from React 18.2.0 to React 19.x. React 19 was released in Dec 2024 with significant new features, performance improvements, and API changes. The jac-client codebase is already largely compatible but needs dependency bumps and a few targeted fixes.

Note: The test fixture with-ts already uses React 19.2.0 but has mismatched @types/react@18.2.45 — this confirms React 19 partially works but types need alignment.


Current State

Package Location Current Version
react @jac-client/jac-client-deps/package.json ^18.2.0
react-dom @jac-client/jac-client-deps/package.json ^18.2.0
@types/react @jac-client/jac-client-devDeps/package.json ^18.2.0
@types/react-dom @jac-client/jac-client-devDeps/package.json ^18.2.0
react-router-dom @jac-client/jac-client-deps/package.json ^6.22.0
react-hook-form @jac-client/jac-client-deps/package.json ^7.71.0
@vitejs/plugin-react @jac-client/jac-client-devDeps/package.json ^4.2.1

What's Already Compatible (No Changes Needed)

The codebase uses modern React patterns which are fully React 19 compatible:

  • createRoot — Already using react-dom/client (no legacy ReactDOM.render)
  • HooksuseState, useEffect, useRef, useCallback, useMemo, useContext all unchanged
  • Context API — Uses createContext() (not legacy contextTypes/getChildContext)
  • No deprecated patterns — No string refs, no PropTypes, no defaultProps on function components
  • Modern JSX transformtsconfig.json uses "jsx": "react-jsx" (automatic transform)
  • Vite build@vitejs/plugin-react v4.2.1+ supports React 19
  • Error Boundariesreact-error-boundary package is React 19 compatible

Breaking Changes & Areas Requiring Attention

1. TypeScript Type Changes (HIGH)

React 19 ships new @types/react@19 with breaking type changes:

  • useRef() requires an argumentuseRef() without args is now a TS error. Must use useRef(null) or useRef(undefined). Check all .tsx and .jac files.
  • ReactElement["props"] defaults to unknown instead of any
  • Global JSX namespace removed — Must use React.JSX for module augmentation
  • Ref callback return type enforced — Arrow ref callbacks like ref={el => (myRef = el)} break because the return is interpreted as a cleanup function. Must use ref={el => { myRef = el }}

Mitigation: Run npx types-react-codemod@latest preset-19 ./path

2. Ref as Regular Prop (MEDIUM)

ref is now a regular prop on function components. forwardRef is no longer needed (will be deprecated later). Audit all components and the client_runtime for forwardRef usage. (Mostly used in shadcn)

3. Context Provider Shorthand (LOW)

<Context.Provider> still works but <Context> can now be used directly as a provider. Current code in BudgetContext.jac uses <BudgetContext.Provider> — works fine, but can be simplified later.

4. Suspense Behavior Change (MEDIUM)

When a component suspends, React 19 immediately commits the nearest Suspense fallback without waiting for siblings. This could affect rendering behavior and tests if Suspense is used anywhere.

5. Error Handling Changes (LOW)

Uncaught errors now go to window.reportError instead of being re-thrown. createRoot/hydrateRoot gain new callbacks: onUncaughtError, onCaughtError. The current JacClientErrorBoundary using react-error-boundary should handle this fine.

6. Third-Party Dependency Peer Deps (HIGH)

Many packages still declare "react": "^18.0.0" as a peer dependency. Each dependency must be checked:

Dependency React 19 Support Action
react-router-dom@6.x ✅ Supported Update to latest v6 or consider v7
react-hook-form@7.x ✅ Supported (v7.54+) Update to latest
@hookform/resolvers ✅ Supported Update to latest
react-error-boundary ✅ Supported (v5+) Already on v5
@vitejs/plugin-react ✅ Supported (v4.3+) Update to latest v4

7. Removed APIs (VERIFY)

These APIs are removed in React 19. Not found in current code but should be verified:

  • ReactDOM.render()createRoot().render() ✅ Already migrated
  • ReactDOM.findDOMNode() → removed
  • react-dom/test-utilsact moved to import { act } from 'react'
  • react-test-renderer/shallow → use react-shallow-renderer package
  • React.createFactory() → removed

New React 19 Features to Consider Adopting

These aren't required for migration but are worth leveraging after upgrade:

  • useActionState — Manages async form/action state with pending indicators
  • useFormStatus — Read parent <form> pending status without prop drilling
  • useOptimistic — Optimistic UI updates
  • use(promise) / use(context) — Can be called conditionally, suspends on promises
  • Form Actions<form action={fn}> for server-side form handling
  • Document Metadata<title>, <meta>, <link> auto-hoisted to <head>
  • Server Components (stable) — For future SSR support

Migration Plan

Step 1: Prep (React 18.3)

# Upgrade to React 18.3 first to get deprecation warnings
npm install react@^18.3.0 react-dom@^18.3.0
# Fix all deprecation warnings before proceeding

Step 2: Run Codemods

npx codemod@latest react/19/migration-recipe
npx types-react-codemod@latest preset-19 ./path

Step 3: Bump Dependencies

npm install react@^19.0.0 react-dom@^19.0.0
npm install -D @types/react@^19.0.0 @types/react-dom@^19.0.0

Step 4: Update Third-Party Packages

  • Update react-router-dom, react-hook-form, @hookform/resolvers to latest
  • Update @vitejs/plugin-react to latest v4

Step 5: Fix Test Fixture Version Mismatch

  • Align jac_client/tests/fixtures/with-ts/package.json types with React 19

Step 6: Test

  • Run full test suite
  • Test all example apps (all-in-one, ts-support)
  • Verify Vite dev server and production builds work

Files to Modify

  • jac-client/@jac-client/jac-client-deps/package.json — Bump react, react-dom, react-router-dom, react-hook-form
  • jac-client/@jac-client/jac-client-devDeps/package.json — Bump @types/react, @types/react-dom, @vitejs/plugin-react
  • jac-client/jac_client/tests/fixtures/with-ts/package.json — Fix @types mismatch
  • jac-client/jac_client/plugin/defaults/tsconfig.json — Verify compatibility
  • Any .tsx / .jac files with useRef() calls without arguments
  • Any ref callback arrow functions returning values implicitly

Labels

  • enhancement
  • jac-client

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions