Setting Up PDF Rendering in React Is More Painful Than It Should Be
You install react-pdf, you throw a Document and Page component in your JSX, and then you spend three hours debugging why nothing renders or why your webpack build explodes. This is the actual workflow. I learned this by burning through a weekend on a project that needed to generate and display PDFs client-side. The library you want is @react-pdf/renderer if you're generating PDFs programmatically, or react-pdf if you're rendering existing PDF files. They are two different things and I have seen people confuse them multiple times. Let me just walk through the most common use case: rendering a PDF file inside a React app. First, install the package. npm install react-pdf or yarn add react-pdf. That part is trivial. The trouble starts immediately after. The library relies on pdfjs-dist under the hood, and if you are using a version mismatch between the two, you will get cryptic errors like "Cannot read property 'getPage' of undefined" or the infamous Worker is not defined. I ran into this exact error on a Next.js project where pdfjs-dist was being hoisted to a different version by a transitive dependency. The fix was adding a resolutions field in package.json to lock pdfjs-dist to the version react-pdf expected, then running yarn install with the --force flag. Took about twenty minutes to track down.
Basic Implementation
Here is what a minimal setup looks like. You import the Document and Page components, pass a source URL or a loaded PDF data object, and render it inside a container with explicit dimensions. import { Document, Page } from 'react-pdf'; <Document file="/path/to/your.pdf" onLoadSuccess={(\*data\*) =\> console.log(data)}\>
<Page pageNumber={1} width={800} /\> </Document> That will render the first page at 800 pixels wide. If you do not specify width, the page will render at its default size which is usually too small to read on any modern screen. Always set explicit dimensions or wrap the Page component in a div with a fixed width and overflow auto so horizontal scrolling does not break your layout.
Get the Full Details
The Worker Configuration You Almost Skip
This is where most guides fail you. pdfjs-dist needs a Web Worker to parse PDF files, and by default react-pdf tries to load it from a CDN path that may be blocked in corporate environments or in SSR setups. You need to configure the GlobalWorkerOptions.workerSrc manually. Import it, set the worker source, and then render. import { Document, Page, globalWorkerOptions } from 'react-pdf'; globalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.js', import.meta.url).toString();
In Create React App the path is different because it uses process.env. Modern bundlers like Vite or Next.js use import.meta.url. If you are using an older CRA project, use the legacy require path instead. I wasted an afternoon on this because someone on Stack Overflow gave me a code snippet that assumed Webpack 4.
Server-Side Rendering Is A Problem
If you are using Next.js or any SSR framework, react-pdf will crash on the server because the browser APIs it depends on do not exist in Node.js. The workaround is straightforward but easy to forget: only render the Document and Page components client-side. Wrap them in a dynamic import with ssr: false, or use a useState hook to track whether the component has mounted and conditionally render the PDF only after mount. const [mounted, setMounted] = useState(false); useEffect(() \*{ setMounted(true); }, []);
{mounted \&\&
Performance Gotchas
Rendering large PDFs with many pages will make your page laggy if you are not careful. react-pdf renders each Page component as a canvas element, and canvases are expensive. If you need to display ten pages, do not render all ten at once without virtualization. Use a library like react-window or react-virtualized to only render the pages currently in the viewport. This cut my rendering time from roughly 4 seconds to under 600 milliseconds for a 50-page document on a mid-range laptop. Also, be aware that react-pdf caches loaded PDFs in memory by default. If your application loads several large PDFs throughout a session, you will see memory usage climb steadily. There is no built-in way to clear the cache, so if you are building something like a document viewer that users rotate through frequently, you may want to reload the Document component with a fresh key after navigating away from the PDF view. It forces a re-fetch and release of the previous instance.
When react-pdf Is The Wrong Tool
Sometimes you do not need this library at all. If your only goal is to let users download a generated PDF, use @react-pdf/renderer on the server side and stream the result. It produces far cleaner output and avoids all the canvas rendering overhead. If you need to display a PDF and your users are on mobile, consider embedding the native browser PDF viewer with an iframe pointing to the file instead. react-pdf on mobile is slow and the touch interactions are awkward. I learned this the hard way when a client complained that their PDF viewer felt unresponsive on iPhone. The library has limitations. It does not support all PDF features equally. Form fields, annotations, and complex layered content often render incorrectly or not at all. If your use case involves interactive PDFs with fillable forms, you are better off using a dedicated solution like pdf-lib for manipulation or routing the user to a full-featured viewer. react-pdf is meant for displaying the visual content of a PDF, not for treating it as a rich interactive document. There is also the ongoing maintenance situation. The library has had periods where updates were slow and issue response times stretched out. Before committing to it for a production app, check the recent commit history and the current open issues to gauge whether the project is active enough for your timeline. I have seen teams switch to alternative approaches mid-project because the library stalled during a critical release window.