Next.js PDF Splinter

Eugene Musebe

Introduction

If you only need part of a long PDF, you can easily split it into individual chapters, separate pages, or remove them. That way, you're free to mark up, save, or send only what you need. If you only need part of that long PDF, you can easily split it into individual chapters, separate pages, or remove them. Here we demonstrate how Nextjs can be used to spread or separate a single pdf document into different PDF files.

Codesandbox

Check the sandbox demo on Codesandbox.

You can also get the project github repo using Github.

Prerequisites

Entry-level javascript and React/Nextjs knowledge.

Setting Up the Sample Project

In your respective folder, create a new nextjs app using npx create-next-app pdfsplinter in your terminal. Head to your project root directory cd pdfsplinter

Nextjs has its own serverside rendering backend which we will use for our media files upload. We will begin by setting up Cloudinary for our backend. Start by creating your own Cloudinary account using Link and log into it. Each Cloudinary user account will have a dashboard containing the environment variable keys necessary for the Cloudinary integration in our project.

In your project directory, start by including Cloudinary in your project dependencies npm install cloudinary create a new file named .env and paste the following code. Fill the blanks with your environment variables from the Cloudinary dashboard.

1CLOUDINARY_CLOUD_NAME =
2
3CLOUDINARY_API_KEY =
4
5CLOUDINARY_API_SECRET =

Restart your project: npm run dev.

In the pages/api folder, create a new file named upload.js. Start by configuring the environment keys and libraries.

1var cloudinary = require("cloudinary").v2;
2
3cloudinary.config({
4 cloud_name: process.env.CLOUDINARY_NAME,
5 api_key: process.env.CLOUDINARY_API_KEY,
6 api_secret: process.env.CLOUDINARY_API_SECRET,
7});

Create a handler function to execute the POST request. The function will receive media file data and post it to the Cloudinary website. It then captures the media file's Cloudinary link and sends it back as a response.

1export default async function handler(req, res) {
2 if (req.method === "POST") {
3 let url = ""
4 try {
5 let fileStr = req.body.data;
6 const uploadedResponse = await cloudinary.uploader.upload_large(
7 fileStr,
8 {
9 resource_type: "video",
10 chunk_size: 6000000,
11 }
12 );
13 url = uploadedResponse.url
14 } catch (error) {
15 res.status(500).json({ error: "Something wrong" });
16 }
17
18 res.status(200).json({data: url});
19 }
20}

The code above concludes our backend.

To achieve the pdf split. We will require a js library known as pdf-lib. Install it through your terminal using npm install pdf-lib.

In the home component, include the necessary imports

1"pages/index"
2
3
4import { useRef, useState, useEffect } from 'react';
5
6const PDFDocument = require('pdf-lib').PDFDocument;

Inside the home function, start by pasting the following

1export default function Home() {
2 const [link, setLink] = useState('');
3 const [response, setResponse] = useState([]);
4 const [processedpages, setProcessedPages] = useState(false)
5 const pdfRef = useRef(null);
6 const url = 'https://res.cloudinary.com/hackit-africa/image/upload/v1654235775/pdfsample.pdf'
7
8return(
9 <></>
10)

We will use the state hooks above as we move on. The url variable contains the original sample pdf for our project. Create a function splitPdf like below. The function will use the pdf-doc library to load the pdf variable and use a for loop to loop the pdf file pages and separate each page into a base64 string and pass it to another function uploadHandler which will be used to upload the pages to the backend.

1const splitPdf = async (props) => {
2 // Load your PDFDocument
3 const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer());
4 const pdfDoc = await PDFDocument.load(existingPdfBytes);
5
6 const numberOfPages = pdfDoc.getPages().length;
7
8 for (let i = 0; i < numberOfPages; i++) {
9 // Create a new "sub" document
10 const subDocument = await PDFDocument.create();
11 // copy the page at current index
12 const [copiedPage] = await subDocument.copyPages(pdfDoc, [i]);
13 subDocument.addPage(copiedPage);
14
15 const pdfBytes = await subDocument.saveAsBase64({ dataUri: true });
16 uploadHandler(pdfBytes)
17 }
18 }
19
20 const uploadHandler = (file) => {
21 // console.log(file)
22 try {
23 fetch('/api/upload', {
24 method: 'POST',
25 body: JSON.stringify({ data: file }),
26 headers: { 'Content-Type': 'application/json' },
27 })
28 .then((response) => response.json())
29 .then((data) => {
30 setProcessedPages(true)
31 let tempArray = response;
32 tempArray.push(data.data);
33 setResponse(tempArray);
34 console.log(response)
35 });
36 } catch (error) {
37 console.error(error);
38 }
39}

Use the following function in your return statement. The css files can be located in the Github repo

1return (
2 <div className="container">
3 <h2>Nextjs PDF splinter</h2>
4 <div className="row">
5 <div className="column">
6 <h2>Sample PDF</h2>
7 <object name="bane" width="400px" height="400px" data="https://res.cloudinary.com/hackit-africa/image/upload/v1654235775/pdfsample.pdf"></object><br />
8 </div>
9 <div className="column">
10 {processedpages ? ""
11 :
12 <button onClick={splitPdf}>Split</button>
13
14 }
15
16 {response.map((page,index) => {
17 return <a href={page} key={index}><p>separated sample page link</p></a>
18 })}
19 </div>
20 </div>
21 </div>
22 )

The code will display the original pdf file and provide links for each page once the split command is fired. The UI should look like the below:

Thats it! Ensure to go through the article to complete the experience.

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.