Using Puppeteer in NextJs
Umut Tufanoglu / March 22, 2021
6 min read
How to use Puppeteer on Vercel using NextJs
Introduction
We are going to built a NextJs application which is going to generate PDF files using the Headless Chrome API Puppeteer. Puppeteer is going to be used as a serverless function. The app is going to be deployed on vercel.
Table of Contents
- Project Setup
- Puppeteer
- Deployment
Project Setup
First of all we are going to create a new NextJs application. To simplify things we are going to use JavaScript. Open up your terminal and run following command.
npx create-next-app@latest
You should have a similiar output to this name, in my case I named the project next-puppeteer-demo-app
npx create-next-app@latest
Need to install the following packages:
create-next-app@latest
Ok to proceed? (y) y
What is your project named? ... next-puppeteer-demo-app
Go back to your terminal and run
npm run dev
This is going to start the application, now you should be able to see a view in your browser using the given address localhost:3000.
We are going to remove the Home.module.css file located inside the styles folder.
Furthermore we are going to remove some content inside pages/index.js. So that it looks like this:
export default function Home() {
return (
<div>
<main>{/* content will be placed here */}</main>
</div>
);
}
We are not going to focus on styling the webapplication in this tutorial. I'll leave that ask to you.
Now go back to your project and rename the hello.js file located in pages/api to generatePdf.js.
So files inside pages/api contain your backend so to say, each file is basically a serverless function. Later on we are going to call that function from our frontend which will trigger a serverless functions, which is going to generate a pdf file using puppeteer.
Your generatePdf.js should look like this for now:
export default function handler(req, res) {
res.status(200).json({ message: "Generate PDF!" });
}
Puppeteer
We are going to install puppeteer via npm, but we are not going to install the full puppeteer package since that won't work. The maximum size for a Serverless Function is 50 MB, by using the puppeteer package we exceed that size which causes an error.
We are going to install chrome-aws-lambda which is a Chromium Binary for AWS Lambda and Google Cloud Functions
npm install chrome-aws-lambda --save-prod
and playwright-core
npm install playwright-core
Next we are going to update our generatePdf.js file. If you want to test the application locally you need to install chromium on your local dev machine aswell and pass the location of it to the ternary operation.
brew install --cask chromium
import chromium from "chrome-aws-lambda"
import playwright from "playwright-core"
export default async function generatePdf(req, res) {
try {
const browser = await playwright.chromium.launch({
args: [...chromium.args, '--font-render-hinting=none'], // This way fix rendering issues with specific fonts
executablePath: process.env.NODE_ENV === "production" ? await chromium.executablePath : '/usr/local/bin/chromium',
headless: process.env.NODE_ENV === "production" ? chromium.headless : true,
});
const context = await browser.newContext();
const page = await context.newPage();
// This is the path of the url which shall be converted to a pdf file
const pdfUrl = process.env.NODE_ENV === "production"
? "https://your.app/pdf"
: "http://localhost:3000/pdf";
await page.goto(pdfUrl, {
waitUntil: "load",
})
const pdf = await page.pdf({
path: "/tmp/awesome.pdf", // we need to move the pdf to the tmp folder otherwise it won't work properly
printBackground: true,
format: "a4",
});
await browser.close();
return res.status(200).json({ pdf })
} catch (error) {
return res.status(error.statusCode || 500).json({ error: error.message });
}
}
The method above basically starts chromium with a few argument. Once it is started we are telling him to go to the given url which is defined as const pdfUrl. Chromium then goes to that URL and waits until its fully loaded.
Next the page is converted to a pdf file, in order to return the file we need to save it in a temporary file location which is here /tmp/yourfilename.pdf. Finally we are closing the browser and returning the generated pdf file.
Now we still need to update our frontend. Go to pages and create a new file with the name pdf.js.
Inside that file you can create a custom view, in our case we are just going to return a string, but you can style it however you like to.
const Pdf = () => {
return (
<div>
<h1>This page is going to be converted to a pdf, wow!</h1>
</div>
);
};
export default Pdf;
Next we open the index.js file and add a button which is going to call our backend method.
We are going to call the api using fetch
Our index.js should look similar to this.
export default function Home() {
const generatePdf = async () => {
const res = await fetch("/api/generatePdf", {
headers: {
"Content-Type": "application/json",
},
method: "POST", // You probably can use get aswell, you can use a post request with a custom body to generate dynamic data in your pdf view, I am going to cover that in a different post :)
});
return res.json();
};
return (
<div>
<main>
<button type="button" onClick={generatePdf}>
Create PDF
</button>
</main>
</div>
);
}
That's it, now you can open your application locally and click on the button to generate a pdf file. The file is automatically going to be downloaded by your browser.
Deployment
We are going to deploy the application on vercel. For that visit vercel.com and register an account or sign in to an existing account. Next create a git repository and push your project to that repository, you can use github or gitlab for this.
Once you pushed your code go to vercel.com and click on New Project, now search for your project and import it.
Skip the Create a Team section and click on deploy.
The deployment / build process should take only take a couple minutes. Once the project is successfully deployed, you can test it on the given production url given by vercel. yourawesomeapp.vercel.app.
Cheers!