Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cloudflare): use React edge renderer for compatibility with React 19 #436

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

mlafeldt
Copy link

@mlafeldt mlafeldt commented Nov 6, 2024

Note: Until a proper fix gets merged, you can use this workaround.


React 19 requires MessageChannel from node:worker_threads, which isn't available in Cloudflare's workerd runtime. Switch to React's edge-optimized server renderer to avoid the MessageChannel dependency.

More background

Trying to use useActionState from React 19 as described here currently results in Uncaught ReferenceError: MessageChannel is not defined when workerd is involved.

❯ rg  --no-ignore 'new MessageChannel' node_modules/react-dom/
node_modules/react-dom/cjs/react-dom-server.browser.development.js
7095:      channel = new MessageChannel(),

node_modules/react-dom/cjs/react-dom-server.browser.production.js
126:var channel = new MessageChannel(),

Rather than polyfilling MessageChannel and its dependencies, e.g. via unenv, I found that switching the renderer is much easier.

The edge renderer probably makes more sense at the edge anyway (😄), though I don't know if the browser variant was initially chosen for a specific reason.

React 19 requires MessageChannel from node:worker_threads, which isn't
available in Cloudflare's workerd runtime. Switch to React's
edge-optimized server renderer to avoid MessageChannel dependency.
Copy link

changeset-bot bot commented Nov 6, 2024

🦋 Changeset detected

Latest commit: 86d1ac5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@astrojs/cloudflare Minor
@test/astro-cloudflare-astro-dev-platform Patch
@test/astro-cloudflare-astro-env Patch
@test/astro-cloudflare-compile-image-service Patch
@test/astro-cloudflare-external-image-service Patch
@test/astro-cloudflare-wasm Patch
@test/astro-cloudflare-no-output Patch
@test/astro-cloudflare-routes-json Patch
@test/astro-cloudflare-with-solid-js Patch
@test/astro-cloudflare-wrangler-preview-platform Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mlafeldt mlafeldt changed the title fix(cloudflare): use React 19's edge renderer for workers compatibility fix(cloudflare): use React edge renderer for compatibility with React 19 Nov 6, 2024
@mlafeldt
Copy link
Author

Can I interest @alexanderniebuhr or @bholmesdev in a review?

@bluwy
Copy link
Member

bluwy commented Nov 13, 2024

This breaks for React 18?

I think we could probably simply remove the alias altogether, it has this exports:

  "./server": {
      "deno": "./server.browser.js",
      "worker": "./server.browser.js",
      "browser": "./server.browser.js",
      "default": "./server.node.js"
   },

And the cloudflare vite config will respect worker or browser conditions already. So it should load the right entrypoint.

@mlafeldt
Copy link
Author

Removing the alias doesn't work, unfortunately. Here's what will happen.

React 18:

✘ [ERROR] TypeError: Cannot read properties of null (reading 'useState')

      at react_production_min.useState
  (file:///Users/mathias/devel/myproject/.wrangler/tmp/pages-PNPhoG/chunks/_@astro-renderers_BO7YS_f8.mjs:258:20)
      at null.<anonymous>
  (file:///Users/mathias/devel/myproject/.wrangler/tmp/pages-PNPhoG/chunks/App_4EsssyrD.mjs:6931:394)
      at renderWithHooks
  (file:///Users/mathias/devel/myproject/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5658:16)
      at renderForwardRef
  (file:///Users/mathias/devel/myproject/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5853:18)
      at renderElement

React 19:

✘ [ERROR] TypeError: Cannot read properties of null (reading 'useState')

      at react_production.useState
  (file:///Users/mathias/devel/myproject/.wrangler/tmp/pages-NLf5Nx/chunks/_@astro-renderers_sWJxeN6r.mjs:423:33)
      at Component
  (file:///Users/mathias/devel/myproject/.wrangler/tmp/pages-NLf5Nx/chunks/App_Cvyax1x8.mjs:430:386)
      at renderWithHooks
  (file:///Users/mathias/devel/myproject/node_modules/react-dom/cjs/react-dom-server.edge.development.js:4545:19)
      at renderElement
...

The correct files are loaded even without the alias (as you suggested), but something else breaks. I guess that's why the alias was added in the first place.

Also, you're right that my change actually breaks React 18. 😕

12:23:34 [ERROR] [vite] x Build failed in 48ms
[commonjs--resolver] Missing "./server.edge" specifier in "react-dom" package
  Location:
    /Users/mathias/devel/myproject/node_modules/vite/dist/node/chunks/dep-BWSbWtLw.js:46041:25
  Stack trace:
...

We need a better fix.

Copy link
Member

@alexanderniebuhr alexanderniebuhr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking, since this doesn't work for all versions, to avoid accidental merge.

@bluwy
Copy link
Member

bluwy commented Dec 6, 2024

Is this still happening in the latest version of the cloudflare integration? We've updated the resolve conditions recently so maybe it works again.

@mlafeldt
Copy link
Author

mlafeldt commented Dec 6, 2024

Is this still happening in the latest version of the cloudflare integration? We've updated the resolve conditions recently so maybe it works again.

Unfortunately, it's still a problem. Resolving works fine. The problem is that SSR will load react-dom/server.browser, which used to work but no longer does with React 19.

@mlafeldt
Copy link
Author

mlafeldt commented Dec 6, 2024

PS: Removing the alias altogether still results in TypeError: Cannot read properties of null (reading 'useState') as reported above.

@ducan-ne
Copy link

ducan-ne commented Dec 7, 2024

any temporary workaround for this? I'm getting same issue after upgraded to astro 5

@filiptibell
Copy link

any temporary workaround for this? I'm getting same issue after upgraded to astro 5

Running into this as well as a completely new user of Astro v5.
I set up a project while exactly following the Astro documentation with:

  • Astro v5.0.3
  • React v18.3.4, astro integration v4.0.0
  • Cloudflare wrangler v3.93.0, astro adapter v12.0.1

and I am unable to preview or deploy, with the error:

✘ [ERROR] core:user:app Uncaught ReferenceError: MessageChannel is not defined

    at null.<anonymous> (m62frbb6ha.js:9132:18) in requireReactDomServer_browser_production
    at null.<anonymous> (m62frbb6ha.js:13815:9) in requireServer_browser
    at null.<anonymous> (m62frbb6ha.js:14031:29) in
  .wrangler/tmp/pages-gAyqUr/chunks/_@astro-renderers_BTmXnlAZ.mjs
    at null.<anonymous> (m62frbb6ha.js:18:59) in __init
    at null.<anonymous> (m62frbb6ha.js:16729:1)

✘ [ERROR] The Workers runtime failed to start. There is likely additional logging output above.

None of the previously working workarounds I have found seem to have done anything.
More specifically, I have tried:

  • ["nodejs_compat"] in wrangler manifest
  • vite.ssr.external configs
  • platformProxy configs
  • react({ ... }) integration configs

Hopefully this can be resolved soon or if anyone else has another workaround to try 🙁

@mlafeldt
Copy link
Author

mlafeldt commented Dec 7, 2024

@ducan-ne @filiptibell As a workaround that doesn't require patching, you can add this to your Astro config:

diff --git astro.config.mjs astro.config.mjs
index 9a077e5..7d30410 100644
--- astro.config.mjs
+++ astro.config.mjs
@@ -34,6 +34,13 @@ export default defineConfig({
     build: {
       sourcemap: true,
     },
+    resolve: {
+      // Use react-dom/server.edge instead of react-dom/server.browser for React 19.
+      // Without this, MessageChannel from node:worker_threads needs to be polyfilled.
+      alias: import.meta.env.PROD && {
+        'react-dom/server': 'react-dom/server.edge',
+      },
+    },
     plugins: [
       // While @sentry/astro isn't compatible with CF Pages yet, we can still use the Vite plugin to upload source maps.
       // Based on https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts

Note: resolve is part of https://docs.astro.build/en/reference/configuration-reference/#vite

@mlafeldt
Copy link
Author

mlafeldt commented Dec 7, 2024

Running into this as well as a completely new user of Astro v5. I set up a project while exactly following the Astro documentation with:

  • Astro v5.0.3
  • React v18.3.4, astro integration v4.0.0
  • Cloudflare wrangler v3.93.0, astro adapter v12.0.1

@filiptibell Are you 100% sure you're using React 18? AFAIK, the MessageChannel is not defined problem was introduced in React 19.

Also, according to https://react.dev/versions, the latest 18 release is 18.3.1. The version you specified does not exist (?).

@filiptibell
Copy link

@filiptibell Are you 100% sure you're using React 18? AFAIK, the MessageChannel is not defined problem was introduced in React 19.

Also, according to https://react.dev/versions, the latest 18 release is 18.3.1. The version you specified does not exist (?).

Ah yep, you're actually right. For some reason I had my @types/react.. packages on v18 while the actual react version I got during setup was v19. Maybe something else to investigate. The patch in your previous reply worked great, thank you!!

@choumasamori
Copy link

@ducan-ne @filiptibell As a workaround that doesn't require patching, you can add this to your Astro config:

diff --git astro.config.mjs astro.config.mjs
index 9a077e5..7d30410 100644
--- astro.config.mjs
+++ astro.config.mjs
@@ -34,6 +34,13 @@ export default defineConfig({
     build: {
       sourcemap: true,
     },
+    resolve: {
+      // Use react-dom/server.edge instead of react-dom/server.browser for React 19.
+      // Without this, MessageChannel from node:worker_threads needs to be polyfilled.
+      alias: import.meta.env.PROD && {
+        'react-dom/server': 'react-dom/server.edge',
+      },
+    },
     plugins: [
       // While @sentry/astro isn't compatible with CF Pages yet, we can still use the Vite plugin to upload source maps.
       // Based on https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts

Didnt work me , any insight ?

Success: Assets published!

14:01:29.160 | Error: Failed to publish your Function. Got error: Uncaught ReferenceError: MessageChannel is not defined at chunks/@astro-renderers_3VCRMvak.mjs:6534:16 in requireReactDomServer_browser_production at chunks/@astro-renderers_3VCRMvak.mjs:12531:8 in requireServer_browser at chunks/_@astro-renderers_3VCRMvak.mjs:12543:29

@NomadicDeveloper22
Copy link

@ducan-ne @filiptibell As a workaround that doesn't require patching, you can add this to your Astro config:

diff --git astro.config.mjs astro.config.mjs
index 9a077e5..7d30410 100644
--- astro.config.mjs
+++ astro.config.mjs
@@ -34,6 +34,13 @@ export default defineConfig({
     build: {
       sourcemap: true,
     },
+    resolve: {
+      // Use react-dom/server.edge instead of react-dom/server.browser for React 19.
+      // Without this, MessageChannel from node:worker_threads needs to be polyfilled.
+      alias: import.meta.env.PROD && {
+        'react-dom/server': 'react-dom/server.edge',
+      },
+    },
     plugins: [
       // While @sentry/astro isn't compatible with CF Pages yet, we can still use the Vite plugin to upload source maps.
       // Based on https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts

Didnt work me , any insight ?

Success: Assets published!

14:01:29.160 | Error: Failed to publish your Function. Got error: Uncaught ReferenceError: MessageChannel is not defined at chunks/@astro-renderers_3VCRMvak.mjs:6534:16 in requireReactDomServer_browser_production at chunks/@astro-renderers_3VCRMvak.mjs:12531:8 in requireServer_browser at chunks/_@astro-renderers_3VCRMvak.mjs:12543:29

I also tried the suggested fix and no luck. How did you manage to get the assets published?

@NomadicDeveloper22
Copy link

I know it's not feasible for everyone. But I gave up on using react in my astro project and used svelte instead. I was only using react for tabler icons so the switch was quick. Solved deployment issues for me

@kziemski
Copy link

@ducan-ne @filiptibell As a workaround that doesn't require patching, you can add this to your Astro config:

diff --git astro.config.mjs astro.config.mjs
index 9a077e5..7d30410 100644
--- astro.config.mjs
+++ astro.config.mjs
@@ -34,6 +34,13 @@ export default defineConfig({
     build: {
       sourcemap: true,
     },
+    resolve: {
+      // Use react-dom/server.edge instead of react-dom/server.browser for React 19.
+      // Without this, MessageChannel from node:worker_threads needs to be polyfilled.
+      alias: import.meta.env.PROD && {
+        'react-dom/server': 'react-dom/server.edge',
+      },
+    },
     plugins: [
       // While @sentry/astro isn't compatible with CF Pages yet, we can still use the Vite plugin to upload source maps.
       // Based on https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts

Didnt work me , any insight ?

Success: Assets published!

14:01:29.160 | Error: Failed to publish your Function. Got error: Uncaught ReferenceError: MessageChannel is not defined at chunks/@astro-renderers_3VCRMvak.mjs:6534:16 in requireReactDomServer_browser_production at chunks/@astro-renderers_3VCRMvak.mjs:12531:8 in requireServer_browser at chunks/_@astro-renderers_3VCRMvak.mjs:12543:29

Import meta env doesn't work. i would use process env i forget which environment variable node_env maybe.

@hckhanh
Copy link

hckhanh commented Dec 21, 2024

@ducan-ne @filiptibell As a workaround that doesn't require patching, you can add this to your Astro config:

diff --git astro.config.mjs astro.config.mjs
index 9a077e5..7d30410 100644
--- astro.config.mjs
+++ astro.config.mjs
@@ -34,6 +34,13 @@ export default defineConfig({
     build: {
       sourcemap: true,
     },
+    resolve: {
+      // Use react-dom/server.edge instead of react-dom/server.browser for React 19.
+      // Without this, MessageChannel from node:worker_threads needs to be polyfilled.
+      alias: import.meta.env.PROD && {
+        'react-dom/server': 'react-dom/server.edge',
+      },
+    },
     plugins: [
       // While @sentry/astro isn't compatible with CF Pages yet, we can still use the Vite plugin to upload source maps.
       // Based on https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts

Didnt work me , any insight ?

Success: Assets published!

14:01:29.160 | Error: Failed to publish your Function. Got error: Uncaught ReferenceError: MessageChannel is not defined at chunks/@astro-renderers_3VCRMvak.mjs:6534:16 in requireReactDomServer_browser_production at chunks/@astro-renderers_3VCRMvak.mjs:12531:8 in requireServer_browser at chunks/_@astro-renderers_3VCRMvak.mjs:12543:29

I also tried the suggested fix and no luck. How did you manage to get the assets published?

I try this and it run smoothly. I hope this PR is resolved soon.

resolve: {
      // Use react-dom/server.edge instead of react-dom/server.browser for React 19.
      // Without this, MessageChannel from node:worker_threads needs to be polyfilled.
      // TODO: wait for this PR: https://github.com/withastro/adapters/pull/436#issuecomment-2525190557
      alias:
        process.env.NODE_ENV === 'production'
          ? {
              'react-dom/server': 'react-dom/server.edge',
            }
          : undefined,
    },

@hckhanh
Copy link

hckhanh commented Dec 21, 2024

@alexanderniebuhr Do we need to add anything to preview this PR? I can help.

@alexanderniebuhr
Copy link
Member

!preview react-19

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants