react-pdf icon indicating copy to clipboard operation
react-pdf copied to clipboard

Cloudflare worker compatibility

Open stephtr opened this issue 1 year ago • 8 comments

Is your feature request related to a problem? Please describe. When one tries to use react-pdf on Cloudflare Pages (for example in a NextJS project), generating PDFs fail with "Wasm code generation disallowed by embedder" due to security restrictions. This issue arises from yoga-layout loading a wasm script. yoga-layout is imported by @react-pdf/layout.

Describe the solution you'd like It would be nice, if one could specify which yoga-layout version @react-pdf/layout shall load. When manually patching @react-pdf/layout to load yoga-layout/asmjs-sync instead of just yoga-layout, the above issue can be circumvented.

Describe alternatives you've considered In case the above is out of scope, I at least wanted to write it down such that others having this issue can find a solution.

Additional context UPDATE: For a patch compatible with react-pdf v4/React 19, see this update. For reference, the patch I did: image (same for the cjs version)

stephtr avatar May 19 '24 23:05 stephtr

+1 to this feature

jacobgad avatar Sep 03 '24 02:09 jacobgad

I would like to add more context and report additional errors related to using react-pdf with Cloudflare Pages.

Errors encountered:

{"error":"renderToStream is a Node-specific API. You're either using this method in a browser, or your bundler is not loading react-pdf from the appropriate web build."}
{"error":"renderToBuffer is a Node-specific API. You're either using this method in a browser, or your bundler is not loading react-pdf from the appropriate web build."}

Root cause:

These errors arise because renderToStream and renderToBuffer are APIs designed exclusively for Node.js environments, which are not fully supported in the Edge environment of Cloudflare Pages.

Edge limitations:

The Edge runtime provided by Cloudflare does not allow the use of Node-specific APIs, making it impossible to leverage these methods in the current setup. This creates a blocker for using react-pdf in serverless and Edge-first environments without extensive patching or workarounds.

Potential solutions:

  1. Support for alternative yoga-layout versions: Allowing the user to specify yoga-layout/asmjs-sync would resolve the WASM restriction issue. However, this still doesn’t address the Node API usage.

  2. Web-compatible build: It would be great if react-pdf provided web-compatible versions of its APIs to render PDFs on Edge or serverless runtimes.

  3. Documentation Update: Adding a clear note in the documentation about the compatibility limitations when using react-pdf in serverless or Edge environments would be helpful for users encountering these errors.

Thank you for considering these enhancements. I hope this additional context helps prioritize Edge and serverless compatibility for future versions.

matbrgz avatar Nov 21 '24 00:11 matbrgz

Update: facebook has removed support for loading yoga via asm.js in v3. There's currently an open PR for fixing the primary issue (unsafe wasm execution). Once that's merged, this issue should be fixable with minimal changes. For using React 19, react-pdf v4 is used, which itself relies on yoga v3. I managed to get things running using the following patch, also downgrading the yoga requirement. WARNING: Even though it works fine for me, this might lead to issues.

diff --git a/lib/index.js b/lib/index.js
index b3187283ae2d1577b9fc8f08a6f2e501ad48ec0d..3139cef0deb009fd6954c55bc7b08b3e0ee786c1 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -2,8 +2,7 @@ import { upperFirst, capitalize, parseFloat as parseFloat$1, without, pick, comp
 import * as P from '@react-pdf/primitives';
 import resolveStyle, { transformColor, flatten } from '@react-pdf/stylesheet';
 import layoutEngine, { fontSubstitution, wordHyphenation, scriptItemizer, textDecoration, justification, linebreaker, bidi, fromFragments } from '@react-pdf/textkit';
-import * as Yoga from 'yoga-layout/load';
-import { loadYoga as loadYoga$1 } from 'yoga-layout/load';
+import Yoga from 'yoga-layout/asmjs-sync';
 import emojiRegex from 'emoji-regex';
 import resolveImage from '@react-pdf/image';
 
@@ -451,7 +450,7 @@ let instancePromise;
 const loadYoga = async () => {
     // Yoga WASM binaries must be asynchronously compiled and loaded
     // to prevent Event emitter memory leak warnings, Yoga must be loaded only once
-    const instance = await (instancePromise ??= loadYoga$1());
+    const instance = Yoga.default;
     const config = instance.Config.create();
     config.setPointScaleFactor(0);
     const node = { create: () => instance.Node.createWithConfig(config) };
diff --git a/package.json b/package.json
index b61ef4fae79d8ee79bd1d881a8fda7405ffa7650..2e1e30773f77635e8c0dde6761c38e554e916ffa 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "@react-pdf/types": "^2.9.0",
     "emoji-regex": "^10.3.0",
     "queue": "^6.0.1",
-    "yoga-layout": "^3.2.1"
+    "yoga-layout": "^2.0.1"
   },
   "files": [
     "lib"

Additionally, you'll probably need an override in you package.json like this for pnpm:

"pnpm": {
    "overrides": {
        "yoga-layout": "2.0.1"
    }
}

stephtr avatar Apr 14 '25 17:04 stephtr

had to do this to get it working:

  1. apply patch from @stephtr to @react-pdf/layout
  2. to overcome error from @matbrgz: patch @react-pdf/renderer and remove the "browser builds" from package.json, seems they get picked up by CF

tobimori avatar Apr 26 '25 14:04 tobimori

The proposed patches of @react-pdf/layout and @react-pdf/renderer did not work for me on a react-router v7 ssr (formerly remix) app built using Vite with @cloudflare/vite-plugin.

Annoying as hell, have to rewrite a PDF feature now.

firxworx avatar Jun 23 '25 18:06 firxworx

I switched to CF Browser rendering

tobimori avatar Jun 23 '25 19:06 tobimori

@tobimori I hugely appreciate that timely comment thank-you.

An absolute pain in the ass for an app that only needs to generate a single PDF in one specific feature (plus the whole rigamarole of running puppeteer). Your answer gives me confidence it won't be another waste of time like @react-pdf + Cloudflare though LOL. I appreciate it, have a good one!

firxworx avatar Jun 23 '25 19:06 firxworx

totally get it, alternative would be you wait until tomorrow and then spin up a node server in cf containers (releasing tomorrow).

tobimori avatar Jun 23 '25 19:06 tobimori