Consume PDFViewer in multiple react apps

Product:

Product Version:

Please give a brief summary of your issue:
Unable to consume PDFViewer in React application from another React application

Please describe your issue and provide steps to reproduce it:
So I created a reusable PDFViewer component providing licensekey, assetspath, pdfurl, theme etc in a React application where the licensekey, assetspath, pdfurl, theme is user input.

I tested it in storybook react it works as expected. Now i try to consume the component in client applicatioon but i get error as below:

This is sample code :

The way i call here same way it works within my app in storybook as well, but when i build and consume it in client app i get the error as pdftron_webviewer_WEBPACK_IMPORTED_MODULR4 is not a function.

Can i get some suggestions on how to handle this

Please provide a link to a minimal sample where the issue is reproducible:

1 Like

Hello @neelamtalreja12,

Thank you for posting on our forum,

To diagnose further we require the following information.

  1. What does the viewer look like when this error occurs? Are you able to interact with the viewer at all?
  2. All WebViewer-related code, including the constructor with all options defined
  3. Are you able to reproduce the issue on our latest release of WebViewer (Version 11.1)?
  4. Screenshot of the project structure, including where the worker files are located
  5. Network panel showing all failed network traffic
  6. Can you reproduce the issue using our samples (especially on our site)? You can find our react sample project here: GitHub - ApryseSDK/webviewer-react-sample: Sample to demonstrate integrating WebViewer into React
  7. Does the issue only occur in certain browsers and/or devices? If so what are the details?
  8. Are you using WebViewer Server? If so, what version of WebViewer Server?
  9. SDK version number: Version-number | Apryse Documentation

Best Regards,
Jacob Romano Carlsen
Web Development Support Engineer
Apryse Software Inc.

1 Like

Quick Overview

We are building a React application that includes a reusable PDF viewer component using the @pdftron/webviewer library as well as other components like Button, switch etc. The goal is to package this component as an independent, reusable NPM package, which can be easily integrated into other React applications without requiring additional setup or redundant installation of dependencies like @pdftron/webviewer.

  1. What does the viewer look like when this error occurs? Are you able to interact with the viewer at all?

When this error occurs, the viewer is not rendered, and it is not possible to interact with it. The issue arises because the WebViewer from @pdftron/webviewer is not recognized in the consuming React application. The error message displayed is:


I am unable to interact with the viewer when it is consumed from one app into another. However, the viewer works perfectly and renders PDFs successfully within the original source app.

I tested this in a simple React app using both Webpack and Vite as the bundler, but the issue persists. My source app, where the viewer works fine, is a straightforward create-react-app setup with TypeScript configuration for the build. Despite trying different setups, the error remains consistent when consuming the component in an external app.

  1. All WebViewer-related code, including the constructor with all options defined
import React, { useEffect, useRef } from 'react';
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
import { ForwardRefComponent } from '@fluentui/react-components';
import { usePDFViewerStyles } from './PDFViewer.styles.js';

export interface PDFViewerProps {
  pdfUrl: string;
  assetsPath: string;
  licenseKey: string;
  theme: 'light' | 'dark';
}

const handleReadOnlyMode = (webViewerInstance: WebViewerInstance) => {
  const { annotationManager } = webViewerInstance.Core;
  const { UI } = webViewerInstance;

  if (annotationManager && UI) {
    annotationManager.disableRedaction();
    annotationManager.enableReadOnlyMode();
    UI.disableElements([
      'annotationToolGroup',
      'toolbarGroup-View',
      'notesPanelToggle',
    ]);
  }
};

export const PDFViewer: ForwardRefComponent<PDFViewerProps> = React.forwardRef(
  (props, ref) => {
    const { pdfUrl, assetsPath, licenseKey, theme = 'light' } = props;
    const viewerRef = useRef<HTMLDivElement | null>(null);
    const webViewerInstance = useRef<WebViewerInstance | null>(null);
    const pdfViewerStyles = usePDFViewerStyles();

    useEffect(() => {
      const refContainer = viewerRef?.current;
      const initializeViewer = async () => {
        if (refContainer) {
          if (webViewerInstance?.current) {
            webViewerInstance.current = null;
          }
          const instance = await WebViewer(
            {
              path: assetsPath,
              licenseKey: licenseKey,
            },
            refContainer
          );
          webViewerInstance.current = instance;
          const { UI } = instance;
          if (pdfUrl) {
            UI.loadDocument(pdfUrl);
          }
          UI.setTheme(theme);
          if (webViewerInstance?.current) {
            handleReadOnlyMode(webViewerInstance?.current);
          }
        }
      };

      initializeViewer();

      return () => {
        if (refContainer) {
          refContainer.innerHTML = '';
        }
        webViewerInstance.current = null;
      };
    }, [assetsPath, licenseKey, pdfUrl, theme]);

    return (
      <div ref={ref || viewerRef} className={pdfViewerStyles.viewerContainer} />
    );
  }
);

PDFViewer.displayName = 'PDFViewer';

This is the code to render pdf in my react app where i call the component by passing properties as:

    <PDFViewer
            theme = 'dark'  
            licenseKey ='my_license_key'
            pdfUrl = 'https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf'
            assetsPath = '/webviewer'
         />

It is rendering pdf within my react app as well in storybook in the app. But when i try to consume by building the component and consuming the package in another react app, i get the above error attached in screenshot.

  1. Are you able to reproduce the issue on our latest release of WebViewer (Version 11.1)?

Yes

  1. Screenshot of the project structure, including where the worker files are located

  1. Network panel showing all failed network traffic

I dont see anything in network panel, but assets are not loading when consuming in another app although i have the assets in consuming app as well with same structure inside public

  1. Can you reproduce the issue using our samples (especially on our site)? You can find our react sample project here: GitHub - ApryseSDK/webviewer-react-sample: Sample to demonstrate integrating WebViewer into React

We are encountering an issue with a reusable React component built using the WebViewer React Sample. The component works perfectly within the original app and during local development. However, when the component is exported as a library and consumed in another client application, it throws the following error during runtime:

Uncaught (in promise) TypeError: WebViewer is not a function

This issue seems related to how WebViewer is imported and initialized in the reusable component. The goal is to create a standalone, reusable React component where clients only need to pass the required props (such as pdfUrl, licenseKey, etc.) without needing to import or manage WebViewer themselves.

The problem occurs specifically during the build and integration phase in the client application, where it appears the import and initialization of WebViewer in the reusable component are not resolving correctly.

  1. Does the issue only occur in certain browsers and/or devices? If so what are the details?

It occurs in all browsers.

NOTE

I attempted to pass WebViewer from @pdftron/webviewer as a prop in another React application by installing the library there, adding the required assets, and using that as a reference in my reusable component. This approach renders successfully in the client app.

For example:

<PDFViewer
  theme="dark"
  licenseKey="my_license_key"
  pdfUrl="https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf"
  assetsPath="../public/webviewer"
  WebViewer={WebViewer}
/>

While this method works, it requires installing the @pdftron/webviewer library in every client application that uses the component, which is not ideal. The goal is to create a truly reusable PDF viewer component that clients can use without needing to manage additional installations, as the library is already a peer dependency in my source app.

I would prefer a solution where the client application does not need to pass or install WebViewer explicitly, ensuring a seamless and lightweight integration experience.

Also the assets path is correct as expected.

1 Like

When we set "type": "module" in our package.json, Node.js treated our files as ES modules (ECMAScript modules). This caused issues while receiving Webviewer in client applications as it was received as module instead of function. Removing this resolved the issue.

Thanks for the support!

2 Likes

Hello @neelamtalreja12,

No problem, glad to hear you were able to resolve your issue by modifying your package.json!

We appreciate posting your solution so that other users will be able to find it.

Best Regards,
Jacob Romano Carlsen
Web Development Support Engineer
Apryse Software Inc.

1 Like