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

react-pdf is not working when build with nextjs

Open nyamba opened this issue 6 years ago • 30 comments

It's working fine on development mode with nextjs. However when I build my nextjs app, it gives me an error that worker.js is not found(404). It looks that *.worker.js is existed in .next director. next bundle Can you point me to a way to resolve it.

Thanks for your time.

404 not found

nyamba avatar Jan 23 '18 10:01 nyamba

Hello @nyamba, I'm not actively supporting nextjs as I don't have all the necessary knowledge. I can only give you one hint:

You can manually set workerSrc path to whatever path you like, so if you define where the worker file should be copied to (hint 2: it does not need building, it can be safely just copied from pdfjs-dist node_module) and how it's called, then your problem is solved entirely.

You could also do the opposite thing, meaning - looking where React-PDF looks for worker file and ensure your bundling process puts it there. But the first option is a safer bet :)

wojtekmaj avatar Jan 23 '18 23:01 wojtekmaj

FYI for others using Next.js they have a helper that we used to work around this issue.

Our problem was that rendering anything which depends on PDF.js server-side will throw errors, as the library itself contains browser API calls which are not safe in Node. By using the above helper to import the component that handles PDF rendering we were able to defer parsing of the library code to the browser, where it ran without error:

// This is our wrapper component which contains the PDF viewer
const PDFViewer = dynamic(import('../../components/PDFViewer'), { ssr: false });

class ExampleClass extends Component {
  render() {
    return (
      <div>
        <PDFViewer />
      </div>
    );
  }
}

felizuno avatar Jul 16 '18 21:07 felizuno

I was facing this problem for a long time in a nextJs production environment: __next[hash].worker.js not found on custom server.

Both provided solutions with custom setOptions method or dynamic import didn't work for me. So I dedided to work without worker.

/* next.config.js : webpack extension */
 webpack: (config) => {

 /* PdfViewer : override defaults aliases */

 config.resolve.alias = Object.assign({}, config.resolve.alias, {
   "react-pdf": "react-pdf/dist/entry.noworker.js"
 });

 return config;
}

I don't know if it's recommended, but it now works in production.

arnaud-brun avatar Dec 28 '18 13:12 arnaud-brun

I see nothing wrong with this code, apart from the fact it won't work with React-PDF 4.x, since there's no longer an option to run React-PDF without a Worker. All browsers should be capable of running the worker though, so you should aim to enable it if possible.

wojtekmaj avatar Jan 02 '19 16:01 wojtekmaj

I am also using next.js and have the same issue, I tried many suggestions and workarounds but failed.

I ended up using this module without worker.

johnking avatar Jan 18 '19 02:01 johnking

I cannot reproduce the issue with:

    "next": "9.1.4",
    "react": "16.12.0",

This works just fine for me:

import React from "react";
import { Document, Page } from "react-pdf";


class Test extends React.Component {
  render() {
    return (
      <Document
        file={{
          url: "./my.pdf"
        }}
        onLoadError={e =>
          console.log("Error while loading document! " + e.message)
        }
        onSourceError={e =>
          console.log("Error while loading document! " + e.message)
        }
      >
        <Page pageNumber={1} />
      </Document>
    );
  }
}

const index = () => {
  return (
    <div>
      <Test></Test>
    </div>
  );
};

export default index;

feluxe avatar Dec 06 '19 02:12 feluxe

@feluxe

the issue happens when trying to use the service worker implementation, which is supposed to be imported like this: import { Document } from 'react-pdf/dist/esm/entry.webpack';

somewhat related... @wojtekmaj , I'm not seeing the esm or umd directories. I'm using version 4.1.0. I tried importing from react-pdf/dist/entry.webpack, which does exist. I'm getting the error about not finding __next/[hash].worker.js so I'm going to try using version 3.x and the entry.noworker.js file

RyanDriscoll avatar Dec 11 '19 20:12 RyanDriscoll

@RyanDriscoll You're looking at docs for the newer version of React-PDF than you're currently using. Read the docs for 4.1.0 here: https://github.com/wojtekmaj/react-pdf/tree/v4.1.0

wojtekmaj avatar Feb 19 '20 12:02 wojtekmaj

Have tried this on Next v9 stack and React-PDF v4.1 alongside @felizuno's Dynamic Import patch.

import { Document, Page } from 'react-pdf/dist/entry.webpack'

However, the PDF gets stuck in loading forever. How to get the webpack working with NextJS?

Have also tried to install Next Workers to no avail. (Still loading)

clodal avatar May 15 '20 07:05 clodal

@wojtekmaj , I'm not seeing the esm or umd directories. I'm using version 4.1.0.

You're looking at React-PDF 5.0 docs.

wojtekmaj avatar May 15 '20 09:05 wojtekmaj

@wojtekmaj thanks for this.. I looking for pdf to image. can this library will help me out or anything ?

rajbir123 avatar Jul 07 '20 14:07 rajbir123

In case anyone comes here looking for NextJs implementation...I got it to work just fine with the below (an amalgamation of all the comments above; thanks all):

Install as per documentation: npm install react-pdf

NB: ensure you don't already have pdfjs-dist installed, as the version you installed may mismatch this package.

Create a wrapped component, as per documentation:

import React from 'react';

import {
  Document, Page,
} from 'react-pdf/dist/esm/entry.webpack';

const PdfViewer = ({
  url, width, pageNumber
}) => (
  <Document file={url}>
    <Page
      pageNumber={pageNumber}
      width={width}
    />
  </Document>
);

export default PdfViewer;

Import dynamically, as SSR FALSE:

import React from 'react';

import dynamic from 'next/dynamic';

const PdfViewer = dynamic(
  () => import('./PdfViewer'),
  { ssr: false }
);

This will ensure that the correct version of pdf.js is pulled in from this react-pdf package, and Next/webpack does the rest.

redgumnfp avatar Oct 26 '20 15:10 redgumnfp

@redgumnfp this is no longer working, error returned is

./node_modules/react-pdf/dist/esm/pdf.worker.entry.js (./node_modules/worker-loader/dist/cjs.js!./node_modules/react-pdf/dist/esm/pdf.worker.entry.js)
TypeError: Cannot read property 'tapAsync' of undefined

Resulting from a recent-ish change in webpack. Repro with code here: https://github.com/zhzhang/next-react-pdf-broken

Pulling from the CDN seems to work however.

// PDFViewer.jsx
import { Document, Page } from "react-pdf";
import { pdfjs } from "react-pdf";
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

const PdfViewer = ({ file, width, pageNumber }) => (
  <Document file={file}>
    <Page pageNumber={pageNumber} width={width} />
  </Document>
);

export default PdfViewer;

zhzhang avatar Feb 15 '21 08:02 zhzhang

I have created a minimal next-js + react-pdf codesandbox and everything works fine with current version, look

https://codesandbox.io/s/react-pdf-next-js-y4ev2

jeetiss avatar Apr 01 '21 18:04 jeetiss

@jeetiss Thanks! Actually, I think you don't even need to import it dynamically when using it this way.

paescuj avatar Apr 02 '21 19:04 paescuj

I think you don't even need to import it dynamically when using it this way.

@paescuj yes you right! react-pdf works fine with webpack 5, but it still is "future" feature, so I keep dynamic import

with webpack 4 react-pdf throws some warnings about window and file access

jeetiss avatar Apr 03 '21 08:04 jeetiss

Hey guys, for me helped to define JS worker _app.js from CDN.

import { pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

adderpositive avatar Apr 13 '21 13:04 adderpositive

import { pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

@adderpositive thanks for that! Solved loading issues when deploying to Vercel. Not my favorite thing to do but when in a pinch.

nathansearles avatar May 27 '21 18:05 nathansearles

Unfortunately, these solutions fail with target: "serverless", which is required by next-on-netlify, for example.

To reproduce the issue, use https://github.com/phbernard/nextjs-react-pdf-sandbox . This sandbox has been forked from @jeetiss's one above (thank you by the way, great resource!), with a change in next.config.js:

module.exports = {
  target: "serverless",
  ...
}

With this project, npm run dev works as expected. However, npm run build fails with:

Failed to compile.

ModuleNotFoundError: Module not found: Error: Can't resolve 'canvas' in '/somewhere/nextjs-react-pdf-sandbox/node_modules/pdfjs-dist/build'

I managed to make it work using webpack externals, still in next.config.js:

module.exports = {
  webpack: (config) => {
    config.externals = {
      ...config.externals,
      canvas: 'canvas',
      critters: 'critters'
    };
   ...
  }
}

Now the project builds and works as expected. However, I get warnings at build time such as:

./node_modules/next/dist/next-server/server/require.js
Critical dependency: the request of a dependency is an expression

This might be harmless, see https://github.com/vercel/next.js/issues/10633 .

I consider this solution as work-in-progress and don't advise to follow it blindly. I put it here because I couldn't find this anywhere.

phbernard avatar Jul 07 '21 12:07 phbernard

If I try to import components from react-pdf/dist/esm/entry.webpack, I've got the error

Cannot find module 'file-loader!pdfjs-dist/build/pdf.worker'

It's appear even if I add file-loader to next.config.js

Now I'm using worker file from CDN to resolve this

oceandrama avatar Jul 07 '21 16:07 oceandrama

I have created a minimal next-js + react-pdf codesandbox and everything works fine with current version, look https://codesandbox.io/s/react-pdf-next-js-y4ev2

@jeetiss Thanks a lot for your example! I updated it, so that it no longer relies on the deprecated file-loader plugin but uses Asset Modules. I only adapted next.config.js and removed the no longer needed dependency from package.json.

https://codesandbox.io/s/react-pdf-next-js-forked-x12d2

faberchri avatar Jul 22 '21 10:07 faberchri

Unfortunately, these solutions fail with target: "serverless", which is required by next-on-netlify, for example.

To reproduce the issue, use https://github.com/phbernard/nextjs-react-pdf-sandbox . This sandbox has been forked from @jeetiss's one above (thank you by the way, great resource!), with a change in next.config.js:

module.exports = {
  target: "serverless",
  ...
}

With this project, npm run dev works as expected. However, npm run build fails with:

Failed to compile.

ModuleNotFoundError: Module not found: Error: Can't resolve 'canvas' in '/somewhere/nextjs-react-pdf-sandbox/node_modules/pdfjs-dist/build'

I managed to make it work using webpack externals, still in next.config.js:

module.exports = {
  webpack: (config) => {
    config.externals = {
      ...config.externals,
      canvas: 'canvas',
      critters: 'critters'
    };
   ...
  }
}

Now the project builds and works as expected. However, I get warnings at build time such as:

./node_modules/next/dist/next-server/server/require.js
Critical dependency: the request of a dependency is an expression

This might be harmless, see vercel/next.js#10633 .

I consider this solution as work-in-progress and don't advise to follow it blindly. I put it here because I couldn't find this anywhere.

@phbernard I agree, I have tried those solutions but it seems it will still fail with target: serverless. However, when I tried your solution the error changed to:

ModuleNotFoundError: Module not found: Error: Can't resolve 'jimp' in '/path_to_file/node_modules/@ampproject/toolbox-optimizer/lib/transformers'

And when I tried to solve the jimp issue, another error came up:

ModuleNotFoundError: Module not found: Error: Can't resolve 'probe-image-size' in '/path_to_file/node_modules/@ampproject/toolbox-optimizer/lib/transformers'

And lastly, when I tried to put it inside externals the build will give a result error memory heap.

I also have tried to only add externals by "pushing" the canvas with config.externals['canvas'] = 'commonjs canvas' but it seems it doesn't work.

asharimh97 avatar Jul 27 '21 07:07 asharimh97

@asharimh97 did you solve this issues by any chance?

arnars avatar Oct 08 '21 10:10 arnars

I was able to get this working without needing to manually copy the pdf worker file into the public folder by using the copy-webpack-plugin. This will automatically copy the pdf worker file into your public folder when the application builds.

Add something like this to your custom webpack config:

config.plugins.push(
  new CopyPlugin({
    patterns: [
      {
        from: path.join(__dirname, 'node_modules/pdfjs-dist/build/pdf.worker.min.js'),
        to: path.join(__dirname, 'public'),
      },
    ],
  })
);

And update the workerSrc so it checks at the root:

pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js';

diakonos avatar Oct 12 '21 01:10 diakonos

@arnars @asharimh97 @phbernard I was able to successfully deploy to Netlify using this webpack solution: https://github.com/wojtekmaj/react-pdf/issues/799#issuecomment-864887752. Hope that helps!

metanivek avatar Oct 15 '21 17:10 metanivek

@jeetiss - Your shared solution stopped the pain for me. Thanks Good team effort all Much appreciation

GaddMaster avatar Oct 20 '21 17:10 GaddMaster

Hey guys, for me helped to define JS worker _app.js from CDN.

import { pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

I used the same method with create-react-app and it works great with Next too. Thanks @adderpositive !

MaximeConan avatar Nov 29 '21 09:11 MaximeConan

Hey guys, for me helped to define JS worker _app.js from CDN.

import { pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

I used the same method with create-react-app and it works great with Next too. Thanks @adderpositive !

with nextjs 12.0.4 this solution worked perfectly for me, and i dont even have to use the dynamic import

ghostneneji avatar Dec 11 '21 15:12 ghostneneji

$ npm i -D copy-webpack-plugin
// next.config.js

const path = require('path')
const CopyPlugin = require("copy-webpack-plugin");

const pdfWorkerPath = require.resolve(
  `pdfjs-dist/build/pdf.worker${
    process.env.NODE_ENV === "development" ? ".min" : ""
  }.js`
);

/** @type {import('next').NextConfig} */
const nextConfig = {
  // ...
  webpack: (config) => {
    config.plugins.push(
      new CopyPlugin({
        patterns: [
          {
            from: pdfWorkerPath,
            to: path.join(__dirname, 'public'),
          },
        ],
      })
    );

    return config;
  },
};

module.exports = nextConfig;

abernier avatar May 25 '22 10:05 abernier

FYI for others using Next.js they have a helper that we used to work around this issue.

Our problem was that rendering anything which depends on PDF.js server-side will throw errors, as the library itself contains browser API calls which are not safe in Node. By using the above helper to import the component that handles PDF rendering we were able to defer parsing of the library code to the browser, where it ran without error:

// This is our wrapper component which contains the PDF viewer
const PDFViewer = dynamic(import('../../components/PDFViewer'), { ssr: false });

class ExampleClass extends Component {
  render() {
    return (
      <div>
        <PDFViewer />
      </div>
    );
  }
}

Thank you, this works for me!

Genaro-CoronelG avatar May 26 '22 22:05 Genaro-CoronelG