Closed Kasui92 closed 5 months ago
downgrading to 8.0.2 solved the issue
Also seeing this issue on Next 14 during the build process.
8.0.2 has a high security vulnerability due to referencing pdfjs-dist@3.11.174, so it's not a great workaround.
Also experiencing the same issue.
I was able to resolve the issue by setting the dynamic component to load without server-side rendering (SSR).
const PdfViewerComponent = dynamic(() => import("./PdfViewer"), { ssr: false, });
and make sure in next.config.js enable this
config.resolve.alias.canvas = false;
I was able to resolve the issue by setting the dynamic component to load without server-side rendering (SSR).
const PdfViewerComponent = dynamic(() => import("./PdfViewer"), { ssr: false, });
and make sure in next.config.js enable this
config.resolve.alias.canvas = false;
Which version of Next/React-PDF are you using?
Would you be able to paste your full component please?
All, please kindly see the updated samples:
and see updated upgrade guide for workarounds:
@wojtekmaj - I think the upgrade guide should read: experimental.esmExternals: 'loose'
, not experiments
.
Regarding the workarounds... I'm unsure how to use the polyfill for Promise.withResolvers. Is there a guide somewhere on how to do this for react-pdf?
I'm also experiencing this on Remix v2.9.1
. Trying to polyfill Promise.withResolvers now
Edit: It seems that the error is coming from within the worker itself, making this difficult to polyfill. I have tried to place a polyfill both in the root as well as in the client component where I have included the worker, but neither worked for me.
Perhaps using a legacy worker would help? π€
Perhaps using a legacy worker would help? π€
I tried using a legacy worker, but sadly that did not help. That being said, the following polyfill worked like a charm:
// @ts-expect-error This does not exist outside of polyfill which this is doing
if (typeof Promise.withResolvers === 'undefined') {
if (window)
// @ts-expect-error This does not exist outside of polyfill which this is doing
window.Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/legacy/build/pdf.worker.min.mjs',
import.meta.url
).toString();
Once I added this, it worked fantastically! π
I referred to this commit and used a polyfill from core-js. Commit that added the polyfill
As a result, I was able to make it work.
If core-js is available, this method might also work.
Perhaps using a legacy worker would help? π€
I tried using a legacy worker, but sadly that did not help. That being said, the following polyfill worked like a charm:
// @ts-expect-error This does not exist outside of polyfill which this is doing if (typeof Promise.withResolvers === 'undefined') { if (window) // @ts-expect-error This does not exist outside of polyfill which this is doing window.Promise.withResolvers = function () { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; } pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url ).toString();
Once I added this, it worked fantastically! π
@justinfarrelldev Where did you add this specifically? I assumed adding to entry.client would be the place, but that didn't resolve it for me.
Perhaps using a legacy worker would help? π€
I tried using a legacy worker, but sadly that did not help. That being said, the following polyfill worked like a charm:
// @ts-expect-error This does not exist outside of polyfill which this is doing if (typeof Promise.withResolvers === 'undefined') { if (window) // @ts-expect-error This does not exist outside of polyfill which this is doing window.Promise.withResolvers = function () { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; } pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url ).toString();
Once I added this, it worked fantastically! π
@justinfarrelldev Where did you add this specifically? I assumed adding to entry.client would be the place, but that didn't resolve it for me.
Sorry, I could have been more clear - I put this snippet in the component where the Document and Page tags are (IE, the component used to show the PDF - I called it pdfViewer.client.tsx).
For more context, I also imported the TextLayer.css and AnnotationLayer.css within this file (at the top, as imports) as well as the worker src.
Basically, almost all react-pdf logic is isolated within this client component. I'll post the source code shortly (and edit this comment when I do).
Edit:
Here's the source that works for me (this file is pdfViewer.client.tsx
). Yes, my JSDoc is badly out of date (it was generated with Hygen boilerplate and I haven't updated it). Note that the memoization is to prevent re-renders of the PDF Viewer (causing it to load the PDF again):
/*
Description: The viewer for pdfs. Needs a buffer to render
*/
import React, { FC, useMemo } from 'react';
import { pdfjs, Document, Page } from 'react-pdf';
import 'react-pdf/dist/Page/TextLayer.css';
import 'react-pdf/dist/Page/AnnotationLayer.css';
// @ts-expect-error This does not exist outside of polyfill which this is doing
if (typeof Promise.withResolvers === 'undefined') {
if (window)
// @ts-expect-error This does not exist outside of polyfill which this is doing
window.Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/legacy/build/pdf.worker.min.mjs',
import.meta.url
).toString();
type Props = {
buffer: { data: Buffer };
};
/**
* The viewer for pdfs. Needs a buffer to render
*
* @param {Props} param0
* @param {string} param0.children The children of this component.
* @returns {React.ReactElement}
*/
export const PdfViewer: FC<Props> = ({ buffer }: Props): React.ReactElement => {
const memoizedFile = useMemo(() => buffer.data, [buffer.data]); // Depend on buffer.data assuming buffer.data is stable and only changes if actual data changes
// Memoize the file object to prevent unnecessary re-renders
const fileProp = useMemo(() => ({ data: memoizedFile }), [memoizedFile]);
return (
<div>
<Document
file={fileProp}
onLoadError={(err) =>
console.error(`Loading error from PDF viewer: ${err}`)
}
onLoadStart={() => console.log('Started loading pdf')}
onLoadSuccess={(pdf) =>
console.log('Successfully loaded pdf:', pdf)
}
>
<Page pageIndex={0} />
</Document>
</div>
);
};
The issue is still present.
After running the example app https://github.com/wojtekmaj/react-pdf/tree/main/sample/next-app
I'm getting error in the console
TypeError: Promise.withResolvers is not a function
Using the proposed polyfills doesn't seem to solve it.
The issue is still present.
After running the example app https://github.com/wojtekmaj/react-pdf/tree/main/sample/next-app
I'm getting error in the console
TypeError: Promise.withResolvers is not a function
Using the proposed polyfills doesn't seem to solve it.
Try adding an "else" to the polyfill I posted above and then using "global" instead of window within that else statement. That should help to handle SSR
if (typeof Promise.withResolvers === "undefined") {
if (window) {
// @ts-expect-error This does not exist outside of polyfill which this is doing
window.Promise.withResolvers = function () {
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve, reject }
}
} else {
// @ts-expect-error This does not exist outside of polyfill which this is doing
global.Promise.withResolvers = function () {
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve, reject }
}
}
}
Nothing changes, the same error occurs.
I also tried https://github.com/wojtekmaj/react-pdf/commit/2ba89d8cb968af6e522e688329cbf2e412b80462 with the same result
I referred to this commit and used a polyfill from core-js. Commit that added the polyfill
As a result, I was able to make it work.
If core-js is available, this method might also work.
- Node.js 19.7.0
- "core-js": "^3.37.1"
- "react-pdf": "^9.0.0"
Thank you, It works for me with the same configuration.
Any update on this?, none of above worked for me
Loading a Polyfill and putting import './polyfills.mjs';
at the top of my next.config.mjs
worked for me. This made Promise.withResolvers
available in both browser and server environments.
Here's the code I used:
import 'core-js/full/promise/with-resolvers.js';
// Polyfill for environments where window is not available (e.g., server-side rendering)
if (typeof Promise.withResolvers === 'undefined') {
if (typeof window !== 'undefined') {
window.Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
} else {
global.Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
}
Updated to latest one 9.1.0 and now getting this error, downgraded and still same :| Basically every time when I update version I getting some errors...
Super hacky, but this is how we added the polyfill to the pdfjs worker itself, in vite.config.ts
:
function transformPdfJsWorker(): Plugin {
return {
name: 'transform-pdf-js-worker',
generateBundle(options, bundle) {
for (const [fileName, chunkOrAsset] of Object.entries(bundle)) {
if (!fileName.includes('pdf.worker') || chunkOrAsset.type !== 'asset') {
continue
}
const prepend = Buffer.from(
`if (typeof Promise.withResolvers === "undefined") {
Promise.withResolvers = function () {
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve, reject }
}
}
`,
'utf-8'
)
const sourceBuffer = Buffer.isBuffer(chunkOrAsset.source)
? chunkOrAsset.source
: Buffer.from(chunkOrAsset.source)
chunkOrAsset.source = Buffer.concat([prepend, sourceBuffer])
}
},
}
}
export default defineConfig({
plugins: [
transformPdfJsWorker(),
],
})
if (typeof Promise.withResolvers === "undefined") { if (window) { // @ts-expect-error This does not exist outside of polyfill which this is doing window.Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } else { // @ts-expect-error This does not exist outside of polyfill which this is doing global.Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } }
Nothing changes, the same error occurs.
I also tried 2ba89d8 with the same result
This works for me if I put the polyfill in _app.tsx. It works in both local and production.
For anyone still struggling with this. Don't bother loading polyfills yourself, it will not work.
You have to load the Legacy version of PdjJS worker which is already polyfilled. See https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support
Like so:
import { pdfjs } from "react-pdf";
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/legacy/build/pdf.worker.min.mjs',
import.meta.url,
).toString();
Okay, so after a few tests, you still have to polyfill Promise.withResolvers because react-pdf uses non-legacy pdf.mjs even if you choose the legacy pdf worker. So legacy pdf worker + polyfilled Promise.withResolvers + react-pdf works as intended.
Getting the same error:
β Compiling / ...
warn - No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.
warn - https://tailwindcss.com/docs/content-configuration
β Compiled / in 673ms (567 modules)
β¨― TypeError: Promise.withResolvers is not a function
at __webpack_require__ (/Users/al/Documents/GitHub/test/frontend/.next/server/webpack-runtime.js:33:42)
at eval (./app/page.tsx:8:67)
at (ssr)/./app/page.tsx (/Users/al/Documents/GitHub/test/frontend/.next/server/app/page.js:173:1)
at Object.__webpack_require__ [as require] (/Users/al/Documents/GitHub/test/frontend/.next/server/webpack-runtime.js:33:42)
at JSON.parse (<anonymous>)
digest: "1833689360"
For anyone still struggling with this. Don't bother loading polyfills yourself, it will not work.
You have to load the Legacy version of PdjJS worker which is already polyfilled. See https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support
Like so:
import { pdfjs } from "react-pdf"; pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url, ).toString();
@wojtekmaj Hey, sorry, is there any proper solution for this error?
pdfjs.GlobalWorkerOptions.workerSrc =
//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs;
and this
import 'core-js/actual/promise';
Doesnt help. The latest version of Chrome and nextjs 14.2.5 and this error.
Hi @wojtekmaj ,
First of all, well done for all the work done on this project.
I do not understand why this issue is closed. I am agree with @DonikaV .
I'm using this
export const pdfjsOptions = {
workerSrc: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`, // use legacy worker for compatibility
standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/standard_fonts/`, // enable standard fonts
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`, // enable character maps for non-latin languages
cMapPacked: true,
};
// use external worker
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsOptions.workerSrc;
And I get the error on MANY recent browsers (tested on browserstack). If I add this piece of code just before then it works :
// @ts-expect-error This does not exist outside of polyfill which this is doing
if (typeof Promise.withResolvers === 'undefined') {
if (window)
// @ts-expect-error This does not exist outside of polyfill which this is doing
window.Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
In my package.json I'm using : "react-pdf": "^9.1.0",
On my point of view the problem exists and is not solved. The problem should be fix properly of the documentation should be updated to alert the users to add the piece of code.
Okay, so after a few tests, you still have to polyfill Promise.withResolvers because react-pdf uses non-legacy pdf.mjs even if you choose the legacy pdf worker. So legacy pdf worker + polyfilled Promise.withResolvers + react-pdf works as intended.
This is working for me, thanks for sharing the solution.
I think i fixed finally
getPdfWorker.js
import pfdWorkerPath from 'pdfjs-dist/legacy/build/pdf.worker.mjs';
import pfdWorkerMinPath from 'pdfjs-dist/legacy/build/pdf.worker.min.mjs';
export default process.env.NODE_ENV === 'production'
? pfdWorkerMinPath
: pfdWorkerPath;
And then in PdfComponent
import workerSrc from '../utils/getPdfWorker';
pdfjs.GlobalWorkerOptions.workerSrc = `/${workerSrc}`;
But I have warnings
Attempted import error: 'pdfjs-dist/legacy/build/pdf.worker.mjs' does not contain a default export (imported as 'pfdWorkerPath').
Import trace for requested module:
./src/utils/getPdfWorker.ts
./src/components/PDFViewer.tsx
Anyone found a working fix for this? Tried Pollyfills and the legacy pdf worker and none of them worked.
This works for me
put this in the component where you use the Document component
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/legacy/build/pdf.worker.min.mjs',
import.meta.url,
).toString();
Add this on the page.tsx of the route.
if (typeof Promise.withResolvers === "undefined") {
if (typeof window !== 'undefined') {
// @ts-expect-error This does not exist outside of polyfill which this is doing
window.Promise.withResolvers = function () {
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve, reject }
}
} else {
// @ts-expect-error This does not exist outside of polyfill which this is doing
global.Promise.withResolvers = function () {
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve, reject }
}
}
}
Loading a Polyfill and putting
import './polyfills.mjs';
at the top of mynext.config.mjs
worked for me. This madePromise.withResolvers
available in both browser and server environments.Here's the code I used:
import 'core-js/full/promise/with-resolvers.js'; // Polyfill for environments where window is not available (e.g., server-side rendering) if (typeof Promise.withResolvers === 'undefined') { if (typeof window !== 'undefined') { window.Promise.withResolvers = function () { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; } else { global.Promise.withResolvers = function () { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; } }
For anyone else facing this, the above solution + using the cdn is the only thing which worked for me of all solutions in this thread.
pdfjs.GlobalWorkerOptions.workerSrc = //unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs;
I'd rather prefer to self-host this but nothing else worked here.
I have polyfills.mjs
in src
and did import '../polyfills.mjs'
in _app.tsx
.
Seems like there's 5 different working solutions for people regarding this issue, I hope it grabs the maintainers attention again since it seems like it needs a deeper dive.
I am running node v22.6.0 and the upgrade to the latest react-pdf caused this issue in our test suite. If I'm running the latest node version, why am I getting this error?
For anyone struggling with remix (v2.10.2
and nodejs 20 LTS
).
This worked for me: I created a promisePolyfill.ts
util function then imported it at the very top of the file that imports everything related to react-pdf
or pdfjs-dist
.
//promisePolyfill.ts
if (typeof Promise.withResolvers !== "function") {
Promise.withResolvers = function <T>() {
let resolve!: (value: T | PromiseLike<T>) => void;
let reject!: (reason?: any) => void;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
export {};
//yourfile.client.tsx
import "~/utils/promisePolyfill";
import { useCallback, useRef, useState } from "react";
import { useResizeObserver } from "@wojtekmaj/react-hooks";
import { pdfjs, Document, Page } from "react-pdf";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import "react-pdf/dist/esm/Page/TextLayer.css";
import { PDFToolbar } from "./pdfToolbar";
import { LoaderCircle } from "lucide-react";
import { IPDFViewerProps } from "~/types/pdfViewerTypes";
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
"/pdf.worker.min.mjs",
import.meta.url,
).toString();
Do make sure that that your file is only rendering on the client side, so rename your file to : file.client.tsx
Also if you have issues with the typing for the promise polyfill remember to create your global.d.ts for it:
interface PromiseConstructor {
withResolvers<T>(): {
promise: Promise<T>;
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
};
}
page.tsx:
const PDFViewer = dynamic(
() => import('./pdf-viewer').then(mod => mod.PDFViewer),
{ ssr: false }
)
pdf-viewer.tsx
"use client"
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url
).toString()
export function PDFViewer() {
return (
<Document>
<Page />
</Document>
)
It fixed my tests execution which was failing since I added react-pdf to my project.
I've tried all solutions above nothing worked until I found your solution, thank you!
@wojtekmaj The issue is still reproducible in some cases Chrome: 127 React: 18.2.0
The issue is related to pdf.js dependency. https://github.com/mozilla/pdf.js/issues?q=withResolvers
@wojtekmaj The issue is still reproducible in some cases Chrome: 127 React: 18.2.0
The issue is related to pdf.js dependency. https://github.com/mozilla/pdf.js/issues?q=withResolvers
UPDATE:
Tuned out Promise.withResolvers was overwritten by Zone.js (angular) (ZoneAwarePromise) Promise class whose old implementation does Not have withResolvers
method.
Solution would be updating conflicting dependancies, especially if you're using Angular
This works for me
put this in the component where you use the Document component
pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url, ).toString();
Add this on the page.tsx of the route.
if (typeof Promise.withResolvers === "undefined") { if (typeof window !== 'undefined') { // @ts-expect-error This does not exist outside of polyfill which this is doing window.Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } else { // @ts-expect-error This does not exist outside of polyfill which this is doing global.Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } }
Worked for me as well. Thanks!
Just wanted to throw some observations I discovered while testing this, For those still facing this issue while using react + Next JS Pages + Promise.withResolvers bypass
If you are planning to host your project on Vercel or any other hosting that doesn't have node 22.x support yet, below is the working solution.
@Kasui92 this error occurs because Vercel currently defaults to Node.js 20.x, while Promise.withResolvers()
is available only in Node.js 22.x or later.
My project config uses the below packages:
18.3.1
14.2.5
v20.x
9.1.0
3.38.0
Hereβs a polyfill solution for Promise.withResolvers()
in both TypeScript and JavaScript:
// utils/polyfilsResolver.tsx
/**
* Polyfill for Promise.withResolvers if it's not available.
*/
export type PromiseWithResolvers<T> = {
promise: Promise<T>;
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
};
export function polyfillPromiseWithResolvers() {
if (!Promise.withResolvers) {
Promise.withResolvers = function <T>(): PromiseWithResolvers<T> {
let resolve: (value: T | PromiseLike<T>) => void;
let reject: (reason?: any) => void;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve: resolve!, reject: reject! };
};
}
}
// utils/polyfilsResolver.js
/**
* Polyfill for Promise.withResolvers if it's not available.
*/
export function polyfillPromiseWithResolvers() {
if (!Promise.withResolvers) {
Promise.withResolvers = function () {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
}
Now You can call polyfillPromiseWithResolvers()
from _app.js
or _app.jsx
like below
// pages/_app.js or pages/_app.jsx
import { polyfillPromiseWithResolvers } from "@/utils/polyfilsResolver";
import 'core-js/full/promise/with-resolvers.js';
polyfillPromiseWithResolvers();
Now lets use the legacy version of react-pdf
as the final touch
// Pdfviewer component
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
Finally, lets build it for production to test
npm run build
Note: This is a working solution that i use on my active projects. It works 100% on vercel and other hostings that doesn't support node 20.x
Enjoy XD :100:
@Bfaschat Oh, sounds like it's good for my case!
Thank you for the comments and the working solution!
@Bfaschat Oh, sounds like it's good for my case!
Thank you for the comments and the working solution!
Merci <3
Just wanted to throw some observations I discovered while testing this, For those still facing this issue while using react + Next JS Pages + Promise.withResolvers bypass
- [ ] Typescript Demo
- [ ] Javascript Demo
If you are planning to host your project on Vercel or any other hosting that doesn't have node 22.x support yet, below is the working solution.
@Kasui92 this error occurs because Vercel currently defaults to Node.js 20.x, while
Promise.withResolvers()
is available only in Node.js 22.x or later.My project config uses the below packages:
- React version:
18.3.1
- Next.js version:
14.2.5
- Node.js version:
v20.x
- react-pdf version:
9.1.0
- core-js version:
3.38.0
Hereβs a polyfill solution for
Promise.withResolvers()
in both TypeScript and JavaScript:TypeScript:
// utils/polyfilsResolver.tsx /** * Polyfill for Promise.withResolvers if it's not available. */ export type PromiseWithResolvers<T> = { promise: Promise<T>; resolve: (value: T | PromiseLike<T>) => void; reject: (reason?: any) => void; }; export function polyfillPromiseWithResolvers() { if (!Promise.withResolvers) { Promise.withResolvers = function <T>(): PromiseWithResolvers<T> { let resolve: (value: T | PromiseLike<T>) => void; let reject: (reason?: any) => void; const promise = new Promise<T>((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve: resolve!, reject: reject! }; }; } }
Javascript
// utils/polyfilsResolver.js /** * Polyfill for Promise.withResolvers if it's not available. */ export function polyfillPromiseWithResolvers() { if (!Promise.withResolvers) { Promise.withResolvers = function () { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; } }
Now You can call
polyfillPromiseWithResolvers()
from_app.js
or_app.jsx
like below// pages/_app.js or pages/_app.jsx import { polyfillPromiseWithResolvers } from "@/utils/polyfilsResolver"; import 'core-js/full/promise/with-resolvers.js'; polyfillPromiseWithResolvers();
Now lets use the legacy version of
react-pdf
as the final touch// Pdfviewer component pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
Finally, lets build it for production to test
npm run build
Note: This is a working solution that i use on my active projects. It works 100% on vercel and other hostings that doesn't support node 20.x
Enjoy XD π―
Hey everyone :) Make sure you are running npm run build
or yarn run build
using node 20.x
not 22.x
Trying to use Node 22.x gives a new error, i didn't have time to debug it so i defaulted back to 20.x
I hope my solution above works as expected
Happy coding
Perhaps using a legacy worker would help? π€
I tried using a legacy worker, but sadly that did not help. That being said, the following polyfill worked like a charm:
// @ts-expect-error This does not exist outside of polyfill which this is doing if (typeof Promise.withResolvers === 'undefined') { if (window) // @ts-expect-error This does not exist outside of polyfill which this is doing window.Promise.withResolvers = function () { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; } pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url ).toString();
Once I added this, it worked fantastically! π
This worked for me.
Super hacky, but this is how we added the polyfill to the pdfjs worker itself, in
vite.config.ts
:function transformPdfJsWorker(): Plugin { return { name: 'transform-pdf-js-worker', generateBundle(options, bundle) { for (const [fileName, chunkOrAsset] of Object.entries(bundle)) { if (!fileName.includes('pdf.worker') || chunkOrAsset.type !== 'asset') { continue } const prepend = Buffer.from( `if (typeof Promise.withResolvers === "undefined") { Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } `, 'utf-8' ) const sourceBuffer = Buffer.isBuffer(chunkOrAsset.source) ? chunkOrAsset.source : Buffer.from(chunkOrAsset.source) chunkOrAsset.source = Buffer.concat([prepend, sourceBuffer]) } }, } } export default defineConfig({ plugins: [ transformPdfJsWorker(), ], })
Excellent!!! It worked for me.
I was able to resolve the issue by setting the dynamic component to load without server-side rendering (SSR).
const PdfViewerComponent = dynamic(() => import("./PdfViewer"), { ssr: false, });
and make sure in next.config.js enable this
config.resolve.alias.canvas = false;
Thankyouuu soo muchhh you saved me!!!
page.tsx:
const PDFViewer = dynamic( () => import('./pdf-viewer').then(mod => mod.PDFViewer), { ssr: false } )
pdf-viewer.tsx
"use client" import { Document, Page, pdfjs } from 'react-pdf' pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url ).toString() export function PDFViewer() { return ( <Document> <Page /> </Document> )
This still doesn't work for me when I use Nextjs 14.2.11 :(
When I run npm run build I get a craaazy long error with lots of whitespace and this on the bottom: `----
Caused by: 0: failed to parse input file 1: Syntax Error
Build failed because of webpack errors
νμ΄μ§.tsx:
const PDFViewer = dynamic( () => import('./pdf-viewer').then(mod => mod.PDFViewer), { ssr: false } )
pdf-λ·°μ΄.tsx
"use client" import { Document, Page, pdfjs } from 'react-pdf' pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url ).toString() export function PDFViewer() { return ( <Document> <Page /> </Document> )
Nextjs 14.2.11μ μ¬μ©ν΄λ μ¬μ ν μλνμ§ μμμ :(
npm run buildλ₯Ό μ€ννλ©΄ κ³΅λ°±μ΄ λ§μ μμ²λκ² κΈ΄ μ€λ₯κ° λ°μνκ³ νλ¨μ λ€μκ³Ό κ°μ λ΄μ©μ΄ νμλ©λλ€. `----
μμΈ: 0: μ λ ₯ νμΌ κ΅¬λ¬Έ λΆμ μ€ν¨ 1: ꡬ문 μ€λ₯
webpack μ€λ₯λ‘ μΈν΄ λΉλκ° μ€ν¨νμ΅λλ€.
add this code to next.config.mjs
webpack: (config) => {
config.optimization.minimize = false;
return config;
},
if (typeof Promise.withResolvers === "undefined") { if (typeof window !== 'undefined') { // @ts-expect-error This does not exist outside of polyfill which this is doing window.Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } else { // @ts-expect-error This does not exist outside of polyfill which this is doing global.Promise.withResolvers = function () { let resolve, reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject } } } }
After many tried so hard with the previous answers and the error still remaning, @armanmasangkay solution works for me just well, thanks a lot bro!
Before you start - checklist
Description
I'm trying to implement the library in a Next.js 14.2.3 (App Router) project without turbopack, however when I go to use the proposed examples it reports the error in the title directly in the console during development or when it's launched the build command.
Having to use Node 20 because Vercel doesn't support 22, I immediately adopted legacy mode to solve the problem... but with poor results.
I also installed
core-js
and imported directly into the layout in root, but although it solves the error during development, it gives me an error when I build the app.Can I ask if I'm missing any steps?
Steps to reproduce
Expected behavior
The build command should work.
Actual behavior
The build command reports an error "failed to parse input file" or "Promise.withResolvers" if core-js is not installed.
Additional information
/src/app/layout.js
/src/app/page.js
/src/app/PdfViewer.js
The example PDF is the same one used in the sample, placed in /public.
Environment