Issues when attempting to access the WebViewer instance Next.js + React

WebViewer Version: “@pdftron/webviewer”: “^8.12.0”

Do you have an issue with a specific file(s)? No
Can you reproduce using one of our samples or online demos? No
Are you using the WebViewer server? No
Does the issue only happen on certain browsers? No
Is your issue related to a front-end framework? Not sure, but I’m testing on Next.js + React
Is your issue related to annotations? No

Please give a brief summary of your issue:
Issues when attempting to access the WebViewer instance Next.js + React, in order to load a blob.

Please describe your issue and provide steps to reproduce it:
Hello!

I’m attempting to load a blob file via PDFTron WebViewer. To load the WebViewer, I added the following logic:

   const viewer = useRef(null);

  React.useEffect(() => {
    if (!viewer || !viewer.current) return;

    import('@pdftron/webviewer').then(() => {
      console.log('opening');

      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      WebViewer(
        {
          path: ''
        },
        viewer.current
      ).then((instance) => {
        console.log('instance??? ', instance);

        const { docViewer } = instance;
        // you can now call WebViewer APIs here...
      });
    });
  }, [viewer]);

return (
<Flex className="webviewer" ref={viewer} height="100vh"></Flex>
)

As you might notice, I added a log, to log the instance value. However, nothing is loaded.

WebViewer is loaded in the client which I find it confusing. This is necessary for me to load the blob file, which it is the next step in the implementation process. However, I cannot even access the instance

  instance.UI.loadDocument(
    myBlob,
    {
      filename: 'sample-1.pdf'
    }
  );

I tried running the Next.js sample found here: Apryse Documentation | Documentation. I was not able to your issue where the instance if not found.

I suspect there is another factor that is causing the instance to be null. Do you see any errors in the console or any failed network requests? Perhaps you could try comparing your project to the Next.js sample.

Hi @Andy_Huang ,

Thanks for checking!

If you noticed, the example found in Apryse Documentation | Documentation takes as a reference a file saved locally.

In my example, I tried to use the instance.UI.loadDocument function to load a Blob file.

With that being said, there are a couple things that are happening:

  1. WebViewer is requiring a /public/ui/index.html file to load the iframe.
  2. WebViewer is NOT loading the Blob file I provided via instance.UI.[loadDocument](https://docs.apryse.com/api/web/UI.html#.loadDocument).

Is there any way you could guide me in the right direction? I feel this should be simple, but clearly it is not working unless I load a file stored in the app.

Another “way” (but it is actually the same way, just using async/awaits) I was testing was this:

  React.useEffect(() => {
    if (!viewer || !viewer.current || !file) return;

    import('@pdftron/webviewer').then(async () => {
      console.log('opening');

      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      const instance = await WebViewer(
        {
          path: ''
        },
        viewer.current
      );

      console.log('instance??? ', instance);
    });
  }, [viewer, file]);

The funny part is, I can see the log “opening”, but there is not “instance???” log at all.

Additionally, If i don’t have an index.html file in the path /public/ui/index.html, I will get an error.

Screen Shot 2023-02-27 at 5.40.33 PM

This doesn’t make sense as:

  1. I haven’t defined the initialDoc key when calling the WebViewer function.
  2. Once again, I’m attempting to load a Blob file. (I make sure the Blob file exists!)

To provide more context.

I’m planning to download the file from our API. This file will be received in the client side in the shape of a Blob.

Whenever the Blob is received, I’m planning to load it using WebViewer.

Finally, as a recommendation, it seems there is not an example that gets the WebViewer types. To access the types of the WebViewer, once imported, I had to use the default value returned from the import. Meaning:

    import('@pdftron/webviewer').then(async (WebViewer) => {
      const pdfTronWebViewer = WebViewer.default;
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      pdfTronWebViewer(
        {
          path: ''
        },
        viewer.current
      ).then((instance) => {
     //  typescript types accessible for "instance"

        instance.UI.loadDocument(file, {
          filename: 'sample-1.pdf',
        });
      });
    });

The examples I see from your documentation assume the access of WebViewer which is imported globally:

 import('@pdftron/webviewer').then(() => {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      WebViewer(
        {
          path: ''
        },
        viewer.current
      );
      .then((instance) => {
     // No typescript types

        instance.UI.loadDocument(file, {
          filename: 'sample-1.pdf'
        });
      });
    });

Thanks for the clarification!

I was testing with a console log to ensure the instance was there as I thought it wasn’t getting logged on your end. However, you mentioned it was a loading issue. I think this may not be too unrelated to what I was thinking.

From your code, I wasn’t sure why you left the path option in the constructor as blank and I sort of assumed it was due to some sort of sensitive information. However, you have just provided the error message that might lend to supporting my suspicion. You need to provide that path so that WebViewer can load the UI as well as the workers. It might be failing before you even get to your loadDocument call.

image

Can you confirm that you have the lib files served and the path is pointing to the correct path?

I updated the path to:

 path: '/webviewer/lib'

Now, I’m getting the following screen. (Not loading anything in the Iframe)

The error that shows in the logs is.

Based on that, that forces me to include "http:///webviewer/lib/ui/index.html in the “frame-src” configuration inside the next.config.js file.

After doing so, I am able to load not the blob file, but the index.html file that is looking for from the path : "webviewer/lib/ui/index.html"

@Andy_Huang as you see, it doesn’t matter what path I configure. It is still not loading the Blob.

It would be great if you could provide an example in Next.js loading a Blob. Maybe that could guide me in the right direction.

That index file does not look like the one that comes from the WebViewer lib/ui folder.

image

The lib folder should have been statically served by Next.js after being copied out from the npm module folder.

I also want to mention as well that there is nothing wrong with how you are loading the document, assuming the file is a blob. The type of the file will be inferred by the filename.

However, what it seems to me right now, is that there is something incorrectly set up in your environment right now. Specifically, the WebViewer lib folder that should be statically served is not there.

Oh you are right! I completely missed copying the assets from WebViewer to my applications. I’m going to try that.

Thanks!

I was able to load it correctly. Thank you @Andy_Huang !