Enable Dark Mode!
how-to-build-a-pdf-upload-and-preview-feature-in-react.jpg
By: Alan Joy

How to Build a PDF Upload and Preview Feature in React

Technical odoo React

Uploading files is a common feature in most of the web applications. Whether we are building an invoice system, document management application, form, or admin dashboard, we need to let users upload their files and preview them before uploading.

In this tutorial, we will build a simple application with a PDF upload and preview feature using React and pdf.js. We will display a pdf thumbnail so the user can see its first page as a preview, and we will also add the option to remove the uploaded pdf.

1. Create a React Project

To create a new React project, run:

npm create vite@latest pdf-preview -- --template react
cd pdf-preview
npm install

Start the application with:

npm run dev

2. Install PDF.js

Now, we need a pdf library to handle the reading and rendering of pdf pages. We will use the pdf.js package for this. You can install pdf.js with this:

npm install pdfjs-dist

3. Create the Upload Component

Now, create a file named PdfUpload.jsx, and add this; it sets up the input field for uploading the pdf file.

import { useState } from "react";
export default function PdfUpload() {
 const [file, setFile] = useState(null);
 const handleFileChange = (event) => {
   const selectedFile = event.target.files?.[0];
   if (selectedFile) {
     setFile(selectedFile);
   }
 };
 return (
   <div>
     <input
       type="file"
       accept="application/pdf"
       onChange={handleFileChange}
     />
     {file && <p>{file.name}</p>}
   </div>
 );
}

4. Generate the PDF Preview

Now, we can create the pdf render using the PDF.js package. First, import the package.

import * as pdfjsLib from "pdfjs-dist";

Next, configure the pdf.js worker.

pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
 "pdfjs-dist/build/pdf.worker.min.mjs",
 import.meta.url
).toString();

Now, we can read and render the selected pdf first page.

const data = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data }).promise;
const page = await pdf.getPage(1);

Next, create a viewport to render the pdf page.

5. Convert the Page into an Image

When the pdf page is rendered into the canvas, we can convert the canvas to an image so we can display it.

const image = canvas.toDataURL("image/png");
const [preview, setPreview] = useState(null);
setPreview(image);
{preview && (
 <img
   src={preview}
   alt="PDF preview"
   className="max-h-60 w-auto rounded border"
 />
)}

6. Add Loading and Validation

The pdf rendering can take a little time, especially if the files are larger. So, we can add a loading state for the pdf rendering. We can also add some validations for the files, like only upload pdf type file and the file sizes smaller than 10mb.

const [loading, setLoading] = useState(false);
if (selectedFile.type !== "application/pdf") {
 setError("Please select a PDF file.");
 return;
}
if (selectedFile.size > 10 * 1024 * 1024) {
 setError("PDF must be smaller than 10 MB.");
 return;
}

7. Add a Remove Button

We can also add a button to remove the uploaded pdf file.

const removeFile = () => {
 setFile(null);
 setPreview(null);
 setError("");
};
<button
 onClick={removeFile}
 className="mt-3 text-sm text-red-600"
>
 Remove
</button>

8. Complete PDF Upload Code

Here is the full code of the PdfUpload.jsx component.

import { useEffect, useState } from "react";
import * as pdfjsLib from "pdfjs-dist";
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
 "pdfjs-dist/build/pdf.worker.min.mjs",
 import.meta.url
).toString();
export default function PdfUpload() {
 const [file, setFile] = useState(null);
 const [preview, setPreview] = useState(null);
 const [loading, setLoading] = useState(false);
 const [error, setError] = useState("");
 const handleFileChange = (event) => {
   const selectedFile = event.target.files?.[0];
   if (!selectedFile) return;
   setError("");
   setPreview(null);
   if (selectedFile.type !== "application/pdf") {
     setError("Please select a PDF file.");
     return;
   }
   if (selectedFile.size > 10 * 1024 * 1024) {
     setError("PDF must be smaller than 10 MB.");
     return;
   }
   setFile(selectedFile);
 };
 useEffect(() => {
   if (!file) return;
   let cancelled = false;
   const createPreview = async () => {
     setLoading(true);
     setError("");
     try {
       const data = await file.arrayBuffer();
       const pdf = await pdfjsLib.getDocument({ data }).promise;
       const page = await pdf.getPage(1);
       const viewport = page.getViewport({
         scale: 0.5,
       });
       const canvas = document.createElement("canvas");
       const context = canvas.getContext("2d");
       if (!context) {
         throw new Error("Could not create canvas context.");
       }
       canvas.width = viewport.width;
       canvas.height = viewport.height;
       await page.render({
         canvasContext: context,
         viewport,
       }).promise;
       if (!cancelled) {
         setPreview(canvas.toDataURL("image/png"));
       }
     } catch {
       if (!cancelled) {
         setError("Unable to preview this PDF.");
         setPreview(null);
       }
     } finally {
       if (!cancelled) {
         setLoading(false);
       }
     }
   };
   createPreview();
   return () => {
     cancelled = true;
   };
 }, [file]);
 const removeFile = () => {
   setFile(null);
   setPreview(null);
   setError("");
 };
 return (
   <div className="mx-auto mt-20 flex w-full max-w-md flex-col items-center rounded-xl border bg-white p-6 text-center">
     <h2 className="text-lg font-semibold">
       Upload PDF
     </h2>
     <p className="mt-1 text-sm text-gray-500">
       Select a PDF to preview its first page.
     </p>
     <label className="mt-5 flex w-full cursor-pointer justify-center rounded-lg border-2 border-dashed p-6 text-sm text-gray-600">
       Choose PDF file
       <input
         type="file"
         accept="application/pdf"
         onChange={handleFileChange}
         className="hidden"
       />
     </label>
     {error && (
       <p className="mt-3 text-sm text-red-600">
         {error}
       </p>
     )}
     {loading && (
       <p className="mt-4 text-sm text-gray-500">
         Creating preview...
       </p>
     )}
     {file && !loading && (
       <div className="mt-5 w-full rounded-lg border bg-gray-50 p-4">
         <div className="flex flex-col items-center">
           {preview && (
             <img
               src={preview}
               alt="PDF preview"
               className="max-h-60 w-auto rounded border bg-white"
             />
           )}
           <p className="mt-2 max-w-full truncate text-sm font-medium">
             {file.name}
           </p>
           <p className="mt-1 text-xs text-gray-500">
             {(file.size / 1024 / 1024).toFixed(2)} MB
           </p>
           <button
             onClick={removeFile}
             className="mt-3 text-sm text-red-600"
           >
             Remove
           </button>
         </div>
       </div>
     )}
   </div>
 );
}

9. Use the Component in Your App

To use our component, import the component into App.jsx.

import PdfUpload from "./PdfUpload";
function App() {
 return (
   <main className="min-h-screen bg-gray-100 p-8">
     <PdfUpload />
   </main>
 );
}
export default App;

To run the app

npm run dev

We have checked how to implement a simple pdf upload and preview feature in our React app. Upload a file, read and open the file with PDF.js, then render and convert the first page to an image and display the preview. The next step is to handle the option to upload the pdf file to a backend. The preview can be generated and handled completely in the browser, without waiting for the server upload, allowing the users to see their document immediately. You can also add other features to our component, like drag-and-drop uploads, upload progress, api integration, multiple-page previews, and other more advanced document handling features.

To read more about How to Handle Errors with Error Boundaries in React, refer to our blog How to Handle Errors with Error Boundaries in React.


Frequently Asked Questions

How can I preview a PDF in React without uploading it to a server?

You can use PDF.js to read the selected file in the browser, render its first page onto a canvas, and convert it into an image for preview.

How do I maintain the PDF preview's aspect ratio?

Use max-h-60 w-auto on the preview image. This limits its height while allowing its width to adjust automatically, preserving the PDF page's aspect ratio.

Can I upload the PDF to a backend after previewing it?

Yes. Store the selected file in React state and use FormData to send it to your backend API when the user submits the form.

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



WhatsApp