Skip to content

A React 18 instrument that imports only react-dom@18.x has its JSX compiled against React 19 and fails to render #1801

Open

Description

@joshunrau

A React 18 interactive instrument gets React 19's JSX runtime whenever it does not import react@18.x itself. resolveJsxImportSource decides which React the JSX compiles against by scanning the source for /runtime/v1/react@<version> only. An instrument that renders with /runtime/v1/react-dom@18.x/client and uses JSX does not need to import react@18.x: the automatic JSX runtime makes the import unnecessary for any component without hooks. So it falls through to the react@19.x default. React 19's JSX runtime stamps elements with Symbol.for('react.transitional.element'), which React 18's reconciler rejects. The task then fails at render with "Objects are not valid as a React child", which is exactly the failure this derivation exists to prevent (packages/instrument-bundler/AGENTS.md). For the same reason, a source that imports react-dom@18.x and react@19.x is not flagged as mixing majors.

Where

packages/instrument-bundler/src/build.ts:14-38:

const DEFAULT_REACT_PACKAGE = 'react@19.x';

const REACT_PACKAGE_PATTERN = /['"]\/runtime\/v1\/(react@[^'"/]+)/g;
// ...
  return `/runtime/v1/${packages.values().next().value ?? DEFAULT_REACT_PACKAGE}`;

Reproduce

  1. Bundle an interactive instrument whose index.tsx imports only createRoot from /runtime/v1/react-dom@18.x/client.js, declares const App = () => <p>hi</p>;, and calls createRoot(document.body).render(<App />) in render.
  2. Open it in apps/web.

Actual: the bundle imports /runtime/v1/react@19.x/jsx-runtime.js next to /runtime/v1/react-dom@18.x/client.js (verified on main), and rendering fails with React 18's "Objects are not valid as a React child" error.
Expected: the JSX compiles against /runtime/v1/react@18.x, and a source that imports react-dom of one major with react of another is a build error.

Tests

packages/instrument-bundler/src/__tests__/build.test.ts: it('should compile JSX against the react paired with an imported react-dom, so a react-dom 18 instrument without hooks still renders'). Add a fixture under src/__tests__/repositories/ (as the package's AGENTS.md asks) that imports only react-dom@18.x/client. Also add it('should reject react-dom and react of different majors in one instrument').

e2e: none needed beyond the fixture, since no built-in instrument uses React 18.

Suggested fix

Match both packages, /['"]\/runtime\/v1\/react(?:-dom)?@([^'"/]+)/g, collect the version, and build react@<version> from it. The existing "more than one version" error then also covers a mismatched react/react-dom pair.

Activity

  1. added
    BugType: existing behavior is wrong
    Priority: LowNice to have, cosmetic or speculative
    Difficulty: LowIsolated change, about 2 hours or less
    Area: InstrumentsInstrument authoring API, bundling, runtime, rendering, measures and the built-in library
    on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: InstrumentsInstrument authoring API, bundling, runtime, rendering, measures and the built-in libraryBugType: existing behavior is wrongDifficulty: LowIsolated change, about 2 hours or lessPriority: LowNice to have, cosmetic or speculative

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions