'Display a PDF in the browser in a modal from blob data using react-pdf (@react-pdf/renderer)

I am using the handy react-pdf library to render/display/download pdf's in my React site. I have my PDF's stored on the server. I have a call to the server that sends back the PDF in blob form. Now I want to display that PDF in the browser in a modal when the user clicks a link. I'm not finding and concrete examples on their site, https://react-pdf.org/ . I'm not sure if I should be using PDFViewer, https://react-pdf.org/components#pdfviewer or BlobProvider, https://react-pdf.org/components#blobprovider

I'm sure this is a fairly easy task, but I'm just not able to find any good examples anywhere.

I fee like the closest example would be here from their site:

import { BlobProvider, Document, Page } from '@react-pdf/renderer';

const MyDoc = (
  <Document>
    <Page>
      // My document data
    </Page>
  </Document>
);

const App = () => (
  <div>
    <BlobProvider document={MyDoc}>
      {({ blob, url, loading, error }) => {
        // Do whatever you need with blob here
        return <div>There's something going on on the fly</div>
      }}
    </BlobProvider>
  </div>
);


Solution 1:[1]

I was trying to achieve the same goal (via my own modal) and it took me a while to get it to work. I used react-pdf and pdfjsWorker (required to render the PDF). The main 'watch-outs': use a pdf worker, and include the pageNumber attribute in the return.

import { Document, Page, pdfjs } from 'react-pdf';

import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';

This line is required for the pdf worker (inside the React component):

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

Firstly I convert my blob into a base64String, removing the additional data at the beginning, and set it as state. I also use state for my page numbers:

  const [pdfString, setPdfString] = useState('');
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);

  const onDocumentLoadSuccess = ({ numPages }) => {
    setNumPages(numPages);
  };

  let base64String;

  let reader = new FileReader();
  reader.readAsDataURL(blob);
  reader.onloadend = () => {
    base64String = reader.result;
    setPdfString(base64String.substr(base64String.indexOf(',') + 1));
  };

Then the return looks something like this:

   <PreviewContainer>
      <Document
        file={`data:application/pdf;base64,${pdfString}`}
        onLoadSuccess={onDocumentLoadSuccess}
      >
        <Page pageNumber={pageNumber} />
      </Document>
    </PreviewContainer>

Additionally, the page number states are controlled by an handleOnClick function and I have added 'next' and 'previous' buttons in my PreviewContainer.

Some useful links:

Solution 2:[2]

I have bumped into this problem recently and here is how I solved it:

The <Document></Document> component expects a file from a given url, one way pass the file then would be to convert the blob content into a url object. You can use window.URL.createObjectURL(blobContent) and pass it to the Document component like this:

<Document 
   file={window.URL.createObjectURL(blobContent)}
>
   <Page pageNumber={pageNumber} />   
</Document>

In addition, you can also use the usePDF to render your pdf from the client side and generate a blob you can then pass to your modal and also update it case you need (https://react-pdf.org/advanced has an example using this hook).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jamie Cook
Solution 2 Gustavo Diniz Da Corte