This issue seems to arise due to esModule option introduced in [email protected].

The fix for this was merged in (pre-release) [email protected]

You can fix this by either upgrading pdfjs-dist to v2.6.347 OR downgrading worker-loader to v2.0.0

Answer from Siddhesh on Stack Overflow
Top answer
1 of 6
14

This issue seems to arise due to esModule option introduced in [email protected].

The fix for this was merged in (pre-release) [email protected]

You can fix this by either upgrading pdfjs-dist to v2.6.347 OR downgrading worker-loader to v2.0.0

2 of 6
7

I just had to solve this issue myself...

This issue

Module not found: Error: Can't resolve 'module' in '/home/giampaolo/dev/KJ_import/KJ-JS/node_modules/webpack/lib/node'

Is caused by worker-loader loading NodeTargetPlugin, which in turn runs require("module") which I think (but I'm not 100%) is for native node modules, which when running Webpack targeted for web is not relevant

This issue can be mitigated with Webpack config

{
  node: {
    module: "empty"
  }
}

Afterwards, things move along farther, but I needed further mitigations:

import pdfjsLib from "pdfjs-dist/webpack";

This runs pdfjs-dist/webpack.js:27 which is

var PdfjsWorker = require("worker-loader!./build/pdf.worker.js");

Which is attempting to load pdf.worker.js (which worker-loader should be packaging) and then tries to instantiate the class:

pdfjs.GlobalWorkerOptions.workerPort = new PdfjsWorker();

The issue I had was that Webpack packaged pdf.worker.js as an esModule (the default for worker-loader), so the way it was require'd needs to be unwrapped with the default property on the imported esModule (said another way, the instantiation would have to be new PdfjsWorker.default()

I was able to mitigate this with the NormalModuleReplacementPlugin plugin, which is able to re-write the require statement based on a regex match/replace, which is matching the original require string and replacing it with one that sets the worker-loader option esModule=false, followed by the absolute path to the pdf.worker.js file on the local system:

new webpack.NormalModuleReplacementPlugin(
  /worker-loader!\.\/build\/pdf\.worker\.js$/,
  "worker-loader?esModule=false!" + path.join(__dirname, "../", "node_modules", "pdfjs-dist", "build", "pdf.worker.js")
)

It's important to match the complete original require string of /worker-loader!\.\/build\/pdf\.worker\.js$/ and not just the pdf.worker.js part, because you could end up in an infinite replace loop.

You need to fix the replacement string to be a proper path for your project, which would probably be

"worker-loader?esModule=false!" + path.join(__dirname, "node_modules", "pdfjs-dist", "build", "pdf.worker.js")

I have a ../ in my path because this code is being executed inside storybooks .storybook/ folder, so I have go up a directory to get to node_modules/

And with those two changes, everything for PDF.js seems to be working.

And lastly, if you want to ignore the warnings about the missing FetchCompileWasmPlugin and FetchCompileAsyncWasmPlugin modules, you can setup the webpack IgnorePlugin to just ignore these imports, my assumption is they're WASM based and not actually needed

plugins: [
  new webpack.IgnorePlugin({ resourceRegExp: /FetchCompileWasmPlugin$/ }),
  new webpack.IgnorePlugin({ resourceRegExp: /FetchCompileAsyncWasmPlugin$/ })
]

I'm guessing there might be some out-of-date mismatch of worker-loader and the modules in the currently installed Webpack version, but these WASM modules don't seem to be necessary for our purposes

🌐
npm
npmjs.com › package › pdfjs-dist
pdfjs-dist - npm
npm i pdfjs-dist · github.com/mozilla/pdf.js · mozilla.github.io/pdf.js/ 6,183,977 · 5.4.449 · Apache-2.0 · 37.2 MB · 388 · 12 days ago · yurydelendik · pdfjsbot · brendandahl · calixteman · pdfjs-release-automation · Try on RunKit ·
      » npm install pdfjs-dist
    
Published   Nov 29, 2025
Version   5.4.449
🌐
GitHub
github.com › mozilla › pdf.js › wiki › setup-pdf.js-in-a-website
Setup PDF.js in a website
The worker shall be built into ... PDFJS.workerSrc shall be set to point to this file. You can use the pdfjs-dist/webpack module for PDF.js autoconfiguration....
Author   mozilla
🌐
GitHub
github.com › mozilla › pdf.js › issues › 15302
Correct way to use pdf.js with NPM/Yarn and Webpack 5 · Issue #15302 · mozilla/pdf.js
June 4, 2022 - GlobalWorkerOptions.workerPort = new Worker(new URL('pdfjs-dist/build/pdf.worker', import.meta.url)); This follows webpack's documentation on how to use web workers ·
Published   Aug 11, 2022
🌐
GitHub
github.com › mozilla › pdf.js › issues › 10940
Document pdfjs-dist/webpack · Issue #10940 · mozilla/pdf.js
March 28, 2019 - Document pdfjs-dist/webpack#10940 · #11081 · Copy link · Labels · other · maclockard · opened · on Jul 2, 2019 · Issue body actions · The current webpack example makes no mention of this as an option or how to correctly use it with worker loader. Instead, I only found it by looking at this comment: #7612 (comment) 👍React with 👍5bryanph, agilgur5, Joozty, bobotangpy and Jakedalus ·
Published   Jul 02, 2019
🌐
GitHub
github.com › mozilla › pdfjs-dist › blob › master › webpack.js
pdfjs-dist/webpack.js at master · mozilla/pdfjs-dist
Generic build of PDF.js library. . Contribute to mozilla/pdfjs-dist development by creating an account on GitHub.
Author   mozilla
🌐
Ember.JS
discuss.emberjs.com › questions › build issues
(pdf.js) Build Error (Bundler) webpack returned errors to ember-auto-import - Build Issues - Ember.JS
December 30, 2022 - Hi there, I’ve been trying to import the PDF.js library and by extenstion pdfjs-dist into my ember app however I receive this error: ERROR in ./node_modules/pdfjs-dist/build/pdf.js 1361:17 Module parse failed: Unexpected character '#' (1361:17) File was processed with these loaders: * ./node_modules/babel-loader/lib/index.js You may need an additional loader to handle the result of these loaders.
Find elsewhere
🌐
GitHub
github.com › mozilla › pdf.js › issues › 17319
`import * as pdfjsLib from 'pdfjs-dist/webpack';` needs `.mjs` · Issue #17319 · mozilla/pdf.js
October 11, 2023 - -pdfjsLib.GlobalWorkerOptions.workerSrc = - "../../build/webpack/pdf.worker.bundle.js"; - // Loading a document. const loadingTask = pdfjsLib.getDocument(pdfPath); const pdfDocument = await loadingTask.promise; diff --git a/examples/webpack/webpack.config.js b/examples/webpack/webpack.config.js index 47301843c..b5c049340 100644 --- a/examples/webpack/webpack.config.js +++ b/examples/webpack/webpack.config.js @@ -6,8 +6,7 @@ const path = require("path"); module.exports = { context: __dirname, entry: { - main: "./main.mjs", - "pdf.worker": "pdfjs-dist/build/pdf.worker.mjs", + main: "./main.mjs" }, mode: "none", output: {
Published   Nov 22, 2023
🌐
GitHub
github.com › mozilla › pdf.js › issues › 8647
Using pdfjs-dist/webpack results in pdf.worker.js imported twice · Issue #8647 · mozilla/pdf.js
February 6, 2017 - I've analyzed the result using webpack JSON stats. From that I realized that pdf.worker.js is imported twice. One is 8d94cec765cddcb66c2d.worker.js file. This is the one that is correctly loaded and used. What you can see though is a second, 0.bundle.js file. This bundle contains a second copy of pdf.worker.js, as pdfjs-dist/build/pdf.js requests for it, but this time without 'worker-loader!' prefix.
Published   Jul 13, 2017
Top answer
1 of 1
2

Here is a full example - complete single-file Vue component:

<template>
  <div class="pdfjs">
    <div id="outerContainer">

      <div id="sidebarContainer">
        <div id="toolbarSidebar">
          <div class="splitToolbarButton toggled">
            <button id="viewThumbnail" class="toolbarButton group toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
              <span data-l10n-id="thumbs_label">Thumbnails</span>
            </button>
            <button id="viewOutline" class="toolbarButton group" title="Show Document Outline (double-click to expand/collapse all items)" tabindex="3" data-l10n-id="document_outline">
              <span data-l10n-id="document_outline_label">Document Outline</span>
            </button>
            <button id="viewAttachments" class="toolbarButton group" title="Show Attachments" tabindex="4" data-l10n-id="attachments">
              <span data-l10n-id="attachments_label">Attachments</span>
            </button>
          </div>
        </div>
        <div id="sidebarContent">
          <div id="thumbnailView">
          </div>
          <div id="outlineView" class="hidden">
          </div>
          <div id="attachmentsView" class="hidden">
          </div>
        </div>
      </div>  <!-- sidebarContainer -->

      <div id="mainContainer">
        <div class="findbar hidden doorHanger hiddenSmallView" id="findbar">
          <label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
          <input id="findInput" class="toolbarField" tabindex="91">
          <div class="splitToolbarButton">
            <button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="92" data-l10n-id="find_previous">
              <span data-l10n-id="find_previous_label">Previous</span>
            </button>
            <div class="splitToolbarButtonSeparator"></div>
            <button class="toolbarButton findNext" title="" id="findNext" tabindex="93" data-l10n-id="find_next">
              <span data-l10n-id="find_next_label">Next</span>
            </button>
          </div>
          <input type="checkbox" id="findHighlightAll" class="toolbarField" tabindex="94">
          <label for="findHighlightAll" class="toolbarLabel" data-l10n-id="find_highlight">Highlight all</label>
          <input type="checkbox" id="findMatchCase" class="toolbarField" tabindex="95">
          <label for="findMatchCase" class="toolbarLabel" data-l10n-id="find_match_case_label">Match case</label>
          <span id="findResultsCount" class="toolbarLabel hidden"></span>
          <span id="findMsg" class="toolbarLabel"></span>
        </div>  <!-- findbar -->

        <div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
          <div id="secondaryToolbarButtonContainer">
            <button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="51" data-l10n-id="presentation_mode">
              <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
            </button>

            <button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="52" data-l10n-id="open_file">
              <span data-l10n-id="open_file_label">Open</span>
            </button>

            <button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="53" data-l10n-id="print">
              <span data-l10n-id="print_label">Print</span>
            </button>

            <button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="54" data-l10n-id="download">
              <span data-l10n-id="download_label">Download</span>
            </button>

            <a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="55" data-l10n-id="bookmark">
              <span data-l10n-id="bookmark_label">Current View</span>
            </a>

            <div class="horizontalToolbarSeparator visibleLargeView"></div>

            <button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="56" data-l10n-id="first_page">
              <span data-l10n-id="first_page_label">Go to First Page</span>
            </button>
            <button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="57" data-l10n-id="last_page">
              <span data-l10n-id="last_page_label">Go to Last Page</span>
            </button>

            <div class="horizontalToolbarSeparator"></div>

            <button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="58" data-l10n-id="page_rotate_cw">
              <span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
            </button>
            <button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="59" data-l10n-id="page_rotate_ccw">
              <span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
            </button>

            <div class="horizontalToolbarSeparator"></div>

            <button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="60" data-l10n-id="hand_tool_enable">
              <span data-l10n-id="hand_tool_enable_label">Enable hand tool</span>
            </button>

            <div class="horizontalToolbarSeparator"></div>

            <button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="61" data-l10n-id="document_properties">
              <span data-l10n-id="document_properties_label">Document Properties…</span>
            </button>
          </div>
        </div>  <!-- secondaryToolbar -->

        <div class="toolbar">
          <div id="toolbarContainer">
            <div id="toolbarViewer">
              <div id="toolbarViewerLeft">
                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="11" data-l10n-id="toggle_sidebar">
                  <span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
                </button>
                <div class="toolbarButtonSpacer"></div>
                <button id="viewFind" class="toolbarButton group hiddenSmallView" title="Find in Document" tabindex="12" data-l10n-id="findbar">
                  <span data-l10n-id="findbar_label">Find</span>
                </button>
                <div class="splitToolbarButton">
                  <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="13" data-l10n-id="previous">
                    <span data-l10n-id="previous_label">Previous</span>
                  </button>
                  <div class="splitToolbarButtonSeparator"></div>
                  <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="14" data-l10n-id="next">
                    <span data-l10n-id="next_label">Next</span>
                  </button>
                </div>
                <input type="number" id="pageNumber" class="toolbarField pageNumber" title="Page" value="1" size="4" min="1" tabindex="15" data-l10n-id="page">
                <span id="numPages" class="toolbarLabel"></span>
              </div>
              <div id="toolbarViewerRight">
                <button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="31" data-l10n-id="presentation_mode">
                  <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
                </button>

                <button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="32" data-l10n-id="open_file">
                  <span data-l10n-id="open_file_label">Open</span>
                </button>

                <button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
                  <span data-l10n-id="print_label">Print</span>
                </button>

                <button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="34" data-l10n-id="download">
                  <span data-l10n-id="download_label">Download</span>
                </button>
                <a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="35" data-l10n-id="bookmark">
                  <span data-l10n-id="bookmark_label">Current View</span>
                </a>

                <div class="verticalToolbarSeparator hiddenSmallView"></div>

                <button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="36" data-l10n-id="tools">
                  <span data-l10n-id="tools_label">Tools</span>
                </button>
              </div>
              <div id="toolbarViewerMiddle">
                <div class="splitToolbarButton">
                  <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="21" data-l10n-id="zoom_out">
                    <span data-l10n-id="zoom_out_label">Zoom Out</span>
                  </button>
                  <div class="splitToolbarButtonSeparator"></div>
                  <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
                    <span data-l10n-id="zoom_in_label">Zoom In</span>
                  </button>
                </div>
                <span id="scaleSelectContainer" class="dropdownToolbarButton">
                  <select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
                    <option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
                    <option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
                    <option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
                    <option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
                    <option id="customScaleOption" title="" value="custom" disabled="disabled" hidden="true"></option>
                    <option title="" value="0.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 50 }'>50%</option>
                    <option title="" value="0.75" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 75 }'>75%</option>
                    <option title="" value="1" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 100 }'>100%</option>
                    <option title="" value="1.25" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 125 }'>125%</option>
                    <option title="" value="1.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 150 }'>150%</option>
                    <option title="" value="2" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 200 }'>200%</option>
                    <option title="" value="3" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 300 }'>300%</option>
                    <option title="" value="4" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 400 }'>400%</option>
                  </select>
                </span>
              </div>
            </div>
            <div id="loadingBar">
              <div class="progress">
                <div class="glimmer">
                </div>
              </div>
            </div>
          </div>
        </div>

        <div type="context" id="viewerContextMenu">
          <div id="contextFirstPage" label="First Page"
                    data-l10n-id="first_page"></div>
          <div id="contextLastPage" label="Last Page"
                    data-l10n-id="last_page"></div>
          <div id="contextPageRotateCw" label="Rotate Clockwise"
                    data-l10n-id="page_rotate_cw"></div>
          <div id="contextPageRotateCcw" label="Rotate Counter-Clockwise"
                    data-l10n-id="page_rotate_ccw"></div>
        </div>

        <div id="viewerContainer" tabindex="0" v-loading="loading">
          <div id="viewer" class="pdfViewer"></div>
        </div>

        <div id="errorWrapper" hidden='true'>
          <div id="errorMessageLeft">
            <span id="errorMessage"></span>
            <button id="errorShowMore" data-l10n-id="error_more_info">
              More Information
            </button>
            <button id="errorShowLess" data-l10n-id="error_less_info" hidden='true'>
              Less Information
            </button>
          </div>
          <div id="errorMessageRight">
            <button id="errorClose" data-l10n-id="error_close">
              Close
            </button>
          </div>
          <div class="clearBoth"></div>
          <textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
        </div>
      </div> <!-- mainContainer -->

      <div id="overlayContainer" class="hidden">
        <div id="passwordOverlay" class="container hidden">
          <div class="dialog">
            <div class="row">
              <p id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</p>
            </div>
            <div class="row">
              <!-- The type="password" attribute is set via script, to prevent warnings in Firefox for all http:// documents. -->
              <input id="password" class="toolbarField">
            </div>
            <div class="buttonRow">
              <button id="passwordCancel" class="overlayButton"><span data-l10n-id="password_cancel">Cancel</span></button>
              <button id="passwordSubmit" class="overlayButton"><span data-l10n-id="password_ok">OK</span></button>
            </div>
          </div>
        </div>
        <div id="documentPropertiesOverlay" class="container hidden">
          <div class="dialog">
            <div class="row">
              <span data-l10n-id="document_properties_file_name">File name:</span> <p id="fileNameField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_file_size">File size:</span> <p id="fileSizeField">-</p>
            </div>
            <div class="separator"></div>
            <div class="row">
              <span data-l10n-id="document_properties_title">Title:</span> <p id="titleField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_author">Author:</span> <p id="authorField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_subject">Subject:</span> <p id="subjectField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_keywords">Keywords:</span> <p id="keywordsField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_creation_date">Creation Date:</span> <p id="creationDateField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_modification_date">Modification Date:</span> <p id="modificationDateField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_creator">Creator:</span> <p id="creatorField">-</p>
            </div>
            <div class="separator"></div>
            <div class="row">
              <span data-l10n-id="document_properties_producer">PDF Producer:</span> <p id="producerField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_version">PDF Version:</span> <p id="versionField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
            </div>
            <div class="buttonRow">
              <button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
            </div>
          </div>
        </div>
        <div id="printServiceOverlay" class="container hidden">
          <div class="dialog">
            <div class="row">
              <span data-l10n-id="print_progress_message">Preparing document for printing…</span>
            </div>
            <div class="row">
              <progress value="0" max="100"></progress>
              <span data-l10n-id="print_progress_percent" data-l10n-args='{ "progress": 0 }' class="relative-progress">0%</span>
            </div>
            <div class="buttonRow">
              <button id="printCancel" class="overlayButton"><span data-l10n-id="print_progress_close">Cancel</span></button>
            </div>
          </div>
        </div>
      </div>  <!-- overlayContainer -->

    </div> <!-- outerContainer -->
    <div id="printContainer"></div>
  </div>
</template>

<script>
require('pdf.js-viewer/pdf');
require('pdf.js-viewer/viewer.css');

export default
{
  props:
    {
      src:
        {
          type: String,
          required: true
        },
      filename:
        {
          type: String,
          required: true
        }
    },
  data ()
  {
    let a =
      {
        loading: false,
        timer: null
      };
    return a;
  },
  created ()
  {
    document.addEventListener('pagerendered', this.pageRendered);
  },
  beforeDestroy ()
  {
    document.removeEventListener('pagerendered', this.pageRendered);
    if (this.timer) clearInterval(this.timer);
  },
  mounted ()
  {
    this.loadFile();
  },
  watch:
    {
      src: 'loadFile'
    },
  methods:
    {
      loadFile ()
      {
        this.loading = true;
        this.timer = setInterval(this.pollPDF, 100);
        window.PDFJS.workerSrc = '/pdf.worker.js';
        if (window.PDFView.pdfDocument) window.PDFView.pdfDocument.destroy();
        window.PDFJS.webViewerLoad(this.src);
        window.PDFView.setScale('page-width', true);
      },
      pageRendered (evt)
      {
        this.loading = false;
        clearInterval(this.timer);
        this.timer = null;
      },
      pollPDF ()
      {
        if (!window.PDFView.loading) this.pageRendered();
      }
    }
}
</script>

<style>
  .pdfjs
  {
    width: 100%;
    height: 100%;
  }

  .pdfjs #scaleSelectContainer
  {
    min-width: auto !important;
    max-width: none !important;
  }

  #scaleSelect
  {
    min-width: initial !important;
  }

  .pdfjs .dropdownToolbarButton
  {
    background-image: none !important;
  }

  #toolbarViewerLeft
  {
    position: static !important;
  }

  #toolbarViewerRight
  {
    display: none !important;
  }

  #sidebarToggle
  {
    display: none;
  }

  .pdfjs #thumbnailView
  {
    width: 100% !important;
  }

  .pdfjs #errorWrapper
  {
    margin-top: 3px;
  }

  .pdfjs #errorWrapper button
  {
    color: #000;
    padding-left: 6px;
    padding-right: 6px;
  }
</style>
Top answer
1 of 6
9

Here's a super ugly workaround for client-components, until something better is proposed (I hate this too, but it works):

npm i pdfjs-dist

Edit 1: A hook might be better for reusability:

Create Your Hook

"use client";
import {useEffect} from "react";
import * as PDFJS from "pdfjs-dist/types/src/pdf";

export const usePDFJS = (onLoad: (pdfjs: typeof PDFJS) => Promise<void>, deps: (string | number | boolean | undefined | null)[] = []) => {
  
  const [pdfjs, setPDFJS] = useState<typeof PDFJS>(null);
  
  // load the library once on mount (the webpack import automatically sets-up the worker)
  useEffect(() => {
    import("pdfjs-dist/webpack.mjs").then(setPDFJS)
  }, []);

  // execute the callback function whenever PDFJS loads (or a custom dependency-array updates)
  useEffect(() => {
    if(!pdfjs) return;
    (async () => await onLoad(pdfjs))();
  }, [ pdfjs, ...deps ]);
}

Typescript Fix

Your compiler might complain about a typescript issue; if so, I just added an index.d.ts (note the .d.ts) at the same level as my hook:

declare module 'pdfjs-dist/webpack.mjs' { export * from 'pdfjs-dist' }

Use Your Hook

"use client";
import {usePDFJS} from "...";


export default function Page() {
  usePDFJS(async (pdfjs) => {
    console.log(pdfjs)
  })
}
2 of 6
4

There's a pretty lengthy discussion over on Github issues where people have managed to get it working either on client:

I decided to follow a simple path, I downloaded the stable version from the official website. I put all the files in the public folder. Then I added this tag to my component:

<script src="/pdfjs/pdf.mjs" type="module" />

then adding code in useEffect:

  const pdfjs = window.pdfjsLib as typeof import('pdfjs-dist/types/src/pdf')
  const pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.min.mjs');
  pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

  const pdfDocument = pdfjs.getDocument('http://localhost:3000/pdf-files/myFile.pdf')

  console.log('pdfDocument', pdfDocument);

Or server-side, via appDir (e.g. app/api/pdf/route.js)

import * as pdfjs from 'pdfjs-dist/build/pdf.min.mjs';
await import('pdfjs-dist/build/pdf.worker.min.mjs');

export async function POST(req, res) {
  const pdf = await pdfjs.getDocument(
    'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf'
  ).promise;
  const page = await pdf.getPage(1);
  const textContent = await page.getTextContent();
  return NextResponse.json({ message: textContent }, { status: 200 });
}

I've personally just tested this last API-one on Next.js 14.1.1 and it works just fine after a npm install pdfjs-dist

🌐
GitHub
github.com › mozilla › pdf.js › issues › 10838
Webpack should handle loading worker instead of setting workerSrc · Issue #10838 · mozilla/pdf.js
February 13, 2019 - When following the Webpack example and importing pdfjs-dist with import * as pdfjsLib from 'pdfjs-dist'; Webpack will create a pdfjsWorker.js and also automatically load it in the browser. The file may be named differently(hashed names, ...
Published   May 20, 2019
🌐
Stack Overflow
stackoverflow.com › questions › tagged › pdfjs-dist
Highest scored 'pdfjs-dist' questions - Stack Overflow
Trying to use pdfjs-dist in node js app Steps: created new node js app (module) then:- npm i pdfjs-dist package.json is:- "name": "testpdf", "version": "1.0.0&...
🌐
npm
npmjs.com › package › embed-pdfjs-dist
embed-pdfjs-dist - npm
Generic build of Mozilla's PDF.js library. An embedded viewer provided.. Latest version: 2.0.89, last published: 8 years ago. Start using embed-pdfjs-dist in your project by running `npm i embed-pdfjs-dist`. There are no other projects in the npm registry using embed-pdfjs-dist.
      » npm install embed-pdfjs-dist
    
Published   Nov 03, 2017
Version   2.0.89
🌐
UNPKG
unpkg.com › browse › [email protected] › webpack.mjs
pdfjs-dist/webpack.mjs
Version: 1.0.8131.0.8161.0.8181.0.8201.0.8221.0.8241.0.8261.0.8281.0.8301.0.8331.0.8351.0.8371.0.8491.0.8511.0.8531.0.8551.0.8581.0.8611.0.8631.0.8641.0.8671.0.8691.0.8721.0.8741.0.8761.0.8781.0.8801.0.8821.0.8841.0.8871.0.8891.0.8921.0.8941.0.8961.0.8981.0.9001.0.9031.0.9051.0.9071.0.9091...
🌐
Lightrun
lightrun.com › answers › mozilla-pdf-js-pdfjs-dist-npm-update-from-20489-to-20943-causes-webpack-error-cant-resolve-babel-runtimereg
pdfjs-dist npm update from 2.0.489 to 2.0.943 causes webpack error: Can't resolve 'babel-runtime/regenerator'
As such, I consider all currently released versions as faulty in that matter, and I suggest to add @babel/runtime as a dependency of pdfjs-dist. Another idea is to get rid of @babel/runtime dependency (probably the best approach). May you consider reopening this? ... The versions at https://github.com/mozilla/pdfjs-dist/tree/master/build and https://github.com/mozilla/pdfjs-dist/tree/master/web are still version 2.0.489 at the time of this writing and this version is devoid from the issue I was reporting here.