Skip to content

fix(ai-components): use simple-markdown 0.7.3 to stop npm nesting React 18 under core - #3988

Merged
isekovanic merged 1 commit into
developfrom
fix/simple-markdown-react-19-npm
Nov 12, 2026
Merged

isekovanic merged 1 commit into
developfrom
fix/simple-markdown-react-19-npm

Conversation

@isekovanic

@isekovanic isekovanic commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Ticket: https://linear.app/stream/issue/RN-485

🎯 Goal

Fix a startup crash for integrators who install with npm on React 19: "Cannot read property 'useState' of null".

Since 9.10.0, core depends on @khanacademy/simple-markdown (^2.1.0) for the AI components' markdown. That package peer-depends on react/react-dom ^18.2.0. Core does not declare react itself, so npm satisfies the peer by nesting React 18.3.1 at node_modules/stream-chat-react-native-core/node_modules/react. Core's own import 'react' then resolves to that copy, and the app runs two Reacts.

Only npm does this. Yarn 1, Yarn 4, pnpm 10 and bun 1.3 all hand core the app's React 19, which is why the monorepo never showed it.

πŸ›  Implementation details

Replace @khanacademy/simple-markdown with the original simple-markdown ^0.7.3. The fork was cut from it at 0.8.0, and 0.7.3 has no peer dependencies. It is already in the tree through react-native-markdown-package, and core's main entry already imported it (renderText.tsx) without declaring it.

  • package/package.json / yarn.lock: swap the dependency (the fork and @khanacademy/perseus-utils drop out).
  • src/ai-components/markdown/*: import from simple-markdown. The API and type names are the same.
  • bin/validate-entry-isolation.js: drop the fork from the AI-only packages list, since the main entry already uses simple-markdown.
  • src/ai-components/README.md: point the rules link at the original repo.

The fork's newer releases don't help. Every version up to 4.0.3, and Perseus main, still declares react/react-dom ^18.2.0. Even though V10 can resolve ESM modules just fine, some changes still need to be made to the upstream library so that react-dom is no longer enforced. For the time being, we'll revert to the known library that enforces nothing and we'll take up the markdown topic more seriously in the near future.

🎨 UI Changes

None. Markdown output is identical.

πŸ§ͺ Testing

  • yarn lint, cd package && yarn test:typecheck, yarn build and yarn test:unit (272 suites, 3206 tests) all pass.
  • npm proof: npm install --package-lock-only next to react@19.3.0. Published 10.0.2 nests react/react-dom 18.3.1 under core; the packed fix resolves a single React 19.3.0, with no @khanacademy packages.
  • Parity between the fork (2.2.3) and 0.7.3, run through our AI markdown rules on 2,732 markdown files: the parse trees and rendered element trees are identical. A source diff confirms the parser and regex code is the same. The only differences are sanitizeUrl and the default hr output, and both are code we never run.
  • On device: ExpoChatGPTSample on an iOS 27 simulator against a live agent. Headings, nested and numbered lists, tables, syntax-highlighted code, bold, italic, inline code and links all render.

β˜‘οΈ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the develop branch
  • Documentation is updated
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

…ct 18 under core

@khanacademy/simple-markdown peer-depends on react/react-dom ^18.2.0. In a
React 19 app installed with npm, npm satisfies that peer by nesting React
18.3.1 under stream-chat-react-native-core, so core imports a second React and
the app crashes at startup ("Cannot read property 'useState' of null").

Switch to the original simple-markdown 0.7.3, which the fork was cut from and
which has no peer dependencies. It is already in the tree through
react-native-markdown-package. Parsing and rendering are identical.
@Stream-SDK-Bot

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 2033 KB 2033 KB 0 B 🟒
ai_components_size 344 KB 344 KB -26 B πŸš€
a2ui_size 103 KB 103 KB 0 B 🟒

isekovanic added a commit that referenced this pull request Nov 12, 2026
…ct 18 under core (#3989)

Ticket: https://linear.app/stream/issue/RN-485

## 🎯 Goal

Fix a startup crash for integrators who install with npm on React 19:
"Cannot read property 'useState' of null".

Since 9.10.0, core depends on `@khanacademy/simple-markdown` (`^2.1.0`)
for the AI components' markdown. That package peer-depends on
`react`/`react-dom` `^18.2.0`. Core does not declare `react` itself, so
npm satisfies the peer by nesting React 18.3.1 at
`node_modules/stream-chat-react-native-core/node_modules/react`. Core's
own `import 'react'` then resolves to that copy, and the app runs two
Reacts.

Only npm does this. Yarn 1, Yarn 4, pnpm 10 and bun 1.3 all hand core
the app's React 19, which is why the monorepo never showed it.

## πŸ›  Implementation details

Replace `@khanacademy/simple-markdown` with the original
`simple-markdown` `^0.7.3`. The fork was cut from it at 0.8.0, and 0.7.3
has no peer dependencies. It is already in the tree through
`react-native-markdown-package`, and core's main entry already imported
it (`renderText.tsx`) without declaring it.

- `package/package.json` / `yarn.lock`: swap the dependency (the fork
and `@khanacademy/perseus-utils` drop out).
- `src/ai-components/markdown/*`: import from `simple-markdown`. The API
and type names are the same.
- `bin/validate-entry-isolation.js`: drop the fork from the AI-only
packages list, since the main entry already uses `simple-markdown`.
- `src/ai-components/README.md`: point the `rules` link at the original
repo.

The fork's newer releases don't help. Every version up to 4.0.3, and
Perseus `main`, still declares `react`/`react-dom` `^18.2.0`. 4.x is
also ESM-only, so Metro on RN 0.76–0.78, which v9 still supports, can't
resolve it.

## 🎨 UI Changes

None. Markdown output is identical.

## πŸ§ͺ Testing

- `yarn lint`, `cd package && yarn test:typecheck`, `yarn build` and
`yarn test:unit` (220 suites, 2421 tests) all pass.
- npm proof: `npm install --package-lock-only` next to `react@19.3.0`.
Published 9.14.1 nests `react`/`react-dom` 18.3.1 under core; the packed
fix resolves a single React 19.3.0, with no `@khanacademy` packages.
- Parity between the fork (2.2.3) and 0.7.3, run through our AI markdown
rules on 2,732 markdown files: the parse trees and rendered element
trees are identical. A source diff confirms the parser and regex code is
the same. The only differences are `sanitizeUrl` and the default `hr`
output, and both are code we never run.
- On device: checked on the V10 PR,
#3988. The
markdown code differs slightly on v9, so the 2,732-file parity check was
re-run here too, with identical results.

## β˜‘οΈ Checklist

- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [x] PR targets the `v9-release` branch
- [x] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
  - [ ] SampleApp iOS and Android
  - [ ] Expo iOS and Android
@isekovanic
isekovanic merged commit 8924a36 into develop Nov 12, 2026
23 checks passed
@isekovanic
isekovanic deleted the fix/simple-markdown-react-19-npm branch November 12, 2026 20:50
@github-actions github-actions Bot mentioned this pull request Nov 12, 2026
6 tasks
@ghost

ghost commented Nov 12, 2026

Copy link
Copy Markdown

πŸŽ‰ This PR is included in version 10.1.0 πŸŽ‰

The release is available on:

Your semantic-release bot πŸ“¦πŸš€

@ghost ghost added the released label Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants