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
.
- 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.
- 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.
- Are you able to reproduce the issue on our latest release of WebViewer (Version 11.1)?
Yes
- Screenshot of the project structure, including where the worker files are located
- 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
- 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.
- 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.