'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:
- https://projects.wojtekmaj.pl/react-pdf/
- https://github.com/wojtekmaj/react-pdf/wiki/Frequently-Asked-Questions (contains a test base64 string - great when troubleshooting!)
- https://www.geeksforgeeks.org/how-to-convert-blob-to-base64-encoding-using-javascript/
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 |