diff --git a/.changeset/angry-stingrays-tease.md b/.changeset/angry-stingrays-tease.md
new file mode 100644
index 00000000..8b9bcda8
--- /dev/null
+++ b/.changeset/angry-stingrays-tease.md
@@ -0,0 +1,6 @@
+---
+"@preact/signals-react-transform": minor
+"@preact/signals-react": minor
+---
+
+Bump `peerDependency` on React to support 19.x
diff --git a/.changeset/sweet-glasses-thank.md b/.changeset/sweet-glasses-thank.md
new file mode 100644
index 00000000..c5d2dfa6
--- /dev/null
+++ b/.changeset/sweet-glasses-thank.md
@@ -0,0 +1,5 @@
+---
+"@preact/signals-react": patch
+---
+
+Fix the stubbed ReactElementType to use the newly added traditional element in v19
diff --git a/packages/react-transform/package.json b/packages/react-transform/package.json
index 5cdfbd20..0a8281d7 100644
--- a/packages/react-transform/package.json
+++ b/packages/react-transform/package.json
@@ -53,7 +53,7 @@
},
"peerDependencies": {
"@babel/core": "^7.0.0",
- "react": "^16.14.0 || 17.x || 18.x"
+ "react": "^16.14.0 || 17.x || 18.x || 19.x"
},
"devDependencies": {
"@babel/core": "^7.22.8",
diff --git a/packages/react/package.json b/packages/react/package.json
index 7650cfe4..fc2a7391 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -68,7 +68,7 @@
"use-sync-external-store": "^1.2.0"
},
"peerDependencies": {
- "react": "^16.14.0 || 17.x || 18.x"
+ "react": "^16.14.0 || 17.x || 18.x || 19.x"
},
"devDependencies": {
"@types/react": "^18.0.18",
diff --git a/packages/react/runtime/src/index.ts b/packages/react/runtime/src/index.ts
index d85ffb0b..a06d2831 100644
--- a/packages/react/runtime/src/index.ts
+++ b/packages/react/runtime/src/index.ts
@@ -5,14 +5,26 @@ import {
Signal,
ReadonlySignal,
} from "@preact/signals-core";
-import { useRef, useMemo, useEffect, useLayoutEffect } from "react";
+import {
+ useRef,
+ useMemo,
+ useEffect,
+ useLayoutEffect,
+ version as reactVersion,
+} from "react";
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
import { isAutoSignalTrackingInstalled } from "./auto";
export { installAutoSignalTracking } from "./auto";
+const [major] = reactVersion.split(".").map(Number);
const Empty = [] as const;
-const ReactElemType = Symbol.for("react.element"); // https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15
+// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15
+// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15
+const ReactElemType = Symbol.for(
+ major >= 19 ? "react.transitional.element" : "react.element"
+);
+
const noop = () => {};
export function wrapJsx(jsx: T): T {