Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 321

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 62k
Author
Asked: November 25, 20242024-11-25T09:48:04+00:00 2024-11-25T09:48:04+00:00

How to Capture Web Page Screenshots with Next.js and Puppeteer

  • 62k

Capturing screenshots of web pages programmatically can be incredibly useful for generating previews, creating image-based reports, and more. In this guide, we’ll build a Next.js API route that takes a URL and generates a PNG screenshot. Our setup uses Puppeteer and chrome-aws-lambda to leverage a headless Chrome browser, making it versatile and production-ready.

We’ll start by setting up a new Next.js project and walking through the code step-by-step to understand how the API captures screenshots.

Prerequisites

  • Setting up the Next.js app
  • Configuring the API route with Puppeteer
  • Creating the React component for the capture interface
  • Explanation of local vs. deployment configurations for Puppeteer

Getting Started with a New Next.js Project

  1. Create a new Next.js app:
npx create-next-app@latest capture-image-app cd capture-image-app 
Enter fullscreen mode Exit fullscreen mode

  1. Install the necessary dependencies:
npm install puppeteer puppeteer-core chrome-aws-lambda busboy 
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the API Route to Generate Screenshots

Now, we’ll set up an API endpoint to capture and return screenshots based on a provided URL.

In the pages/api folder, create a new file named generate-png.ts and add this code:

import { NextApiRequest, NextApiResponse } from "next"; import busboy, { Busboy } from "busboy"; // Use busboy for multipart parsing import chromium from "chrome-aws-lambda"; import puppeteerCore from "puppeteer-core"; // Import puppeteer-core directly import puppeteer from "puppeteer"; // Import puppeteer directly  // Conditional import for Puppeteer based on the environment const puppeteerModule = process.env.NODE_ENV === "production" ? puppeteerCore : puppeteer;  export const config = {   api: {     bodyParser: false, // Disable default body parsing to handle raw binary data (Blob)   }, };  const delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));  export default async function handler(   req: NextApiRequest,   res: NextApiResponse ): Promise<void> {   try {     if (req.method === "POST") {       const bb: Busboy = busboy({ headers: req.headers });       let width: number = 1920; // Default width       let height: number = 0; // Default height       let delayTime: number = 6000;       const buffers: Buffer[] = [];        bb.on("file", (_name: string, file: NodeJS.ReadableStream) => {         file.on("data", (data: Buffer) => buffers.push(data));       });        bb.on("field", (name: string, value: string) => {         if (name === "width") width = parseInt(value, 10) || 1920;         if (name === "height") height = parseInt(value, 10) || 0;         if (name === "delay") delayTime = parseInt(value, 10) || 6000;       });        bb.on("finish", async () => {         const blobBuffer: Buffer = Buffer.concat(buffers);         const htmlContent: string = blobBuffer.toString("utf-8");          const browser = await puppeteerModule.launch({           args: ["--start-maximized"],           executablePath: process.env.NODE_ENV === "production"             ? await chromium.executablePath || "/usr/bin/chromium-browser"             : undefined,  // No custom executable path needed for local           headless: true,         });          const page = await browser.newPage();          // Load the HTML content directly         await page.setContent(htmlContent, { waitUntil: "networkidle0" });          //@ts-expect-error todo         const bodyHeight = await page.evaluate(() => {           return document.body.scrollHeight; // Get the full scrollable height of the body         });          await page.setViewport({           width: Number(width),           height: height || bodyHeight, // Use the provided height or fallback to the full body height           deviceScaleFactor: 2,         });          await delay(delayTime);          const screenshotBuffer = await page.screenshot({           fullPage: !height,           type: "png",           omitBackground: false,         });          await browser.close();          res.setHeader("Content-Type", "image/png");         res.setHeader(           "Content-Disposition",           "attachment; filename=screenshot.png"         );         res.status(200).end(screenshotBuffer);       });        req.pipe(bb); // Pipe the request stream to busboy     } else {       res.setHeader("Allow", ["POST"]);       res.status(405).end(`Method ${req.method} Not Allowed`);     }   } catch (error) {     console.error("ERROR", error);     res.status(500).end("Internal Server Error");   } }   
Enter fullscreen mode Exit fullscreen mode

*Explanation: Choosing Puppeteer for Local vs. Production Environments
*

In this code, we’ve set up a dynamic import for puppeteer:

  • Local Development: If NODE_ENV is not production, it uses puppeteer, which is simpler to set up and doesn’t require chrome-aws-lambda.

  • Production: For serverless deployments, the environment will detect NODE_ENV as production and load puppeteer-core along with chrome-aws-lambda, which allows it to work in AWS Lambda and other similar environments. In this setup, chrome-aws-lambda provides the correct Chromium path, ensuring compatibility with serverless providers.

Step 3: Create a Simple React Component for the UI

Here, we’ll create a straightforward form that lets users input values for the webpage capture. This form will trigger the generate function to capture and download the screenshot in PDF format.

import { useState } from "react";  export default function ScreenCaptureComponent() {   const [isProcessing, setProcessing] = useState(false);   const [width, setWidth] = useState<string>("1920");   const [height, setHeight] = useState<string>("1000");   const [delay, setDelay] = useState<string>("6000");    // Function to clone HTML and prepare for capture   function takeScreenshot() {     const clonedElement = document.body.cloneNode(true) as HTMLElement;     const blob = new Blob([clonedElement.outerHTML], { type: "text/html" });     return blob;   }    // Function to capture screenshot by sending cloned HTML to API   async function generateCapture() {     setProcessing(true);      const htmlBlob = takeScreenshot();      if (!htmlBlob) {       setProcessing(false);       return;     }      try {       const formData = new FormData();       formData.append("file", htmlBlob);       formData.append("width", width);       formData.append("height", height);       formData.append("delay", delay);       const response = await fetch("/api/generate-png", {         method: "POST",         body: formData,       });        if (!response.ok) throw new Error("Capture failed");        const blob = await response.blob();       const downloadUrl = URL.createObjectURL(blob);       const link = document.createElement("a");       link.href = downloadUrl;       link.download = "capture.png";       link.click();       URL.revokeObjectURL(downloadUrl);     } catch (error) {       console.error("Failed to capture screenshot", error);     } finally {       setProcessing(false);     }   }    return (     <div       style={{         maxWidth: "400px",         margin: "50px auto",         padding: "24px",         backgroundColor: "white",         borderRadius: "8px",         width: "100%",         boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",       }}     >       <h2         style={{           fontSize: "24px",           fontWeight: "600",           textAlign: "center",           marginBottom: "16px",         }}       >         Webpage Screenshot Capture       </h2>       <form         onSubmit={(e) => {           e.preventDefault();           generateCapture();         }}         style={{           display: "flex",           flexDirection: "column",           alignItems: "center",           marginBottom: "16px",         }}       >         <label           style={{ marginBottom: "8px", fontWeight: "500" }}           htmlFor="width"         >           Width (px)         </label>         <select           id="width"           value={width}           onChange={(e) => setWidth(e.target.value)}           style={{             width: "100%",             padding: "8px",             marginBottom: "16px",             borderRadius: "4px",             border: "1px solid #ccc",             outline: "none",           }}         >           <option value="1920">1920 (Full HD)</option>           <option value="1366">1366 (Laptop)</option>           <option value="1280">1280 (Desktop)</option>           <option value="1024">1024 (Tablet Landscape)</option>           <option value="768">768 (Tablet Portrait)</option>           <option value="375">375 (Mobile)</option>         </select>          <label           style={{ marginBottom: "8px", fontWeight: "500" }}           htmlFor="height"         >           Height (px)         </label>         <input           type="number"           id="height"           value={height}           onChange={(e) => setHeight(e.target.value)}           required           style={{             width: "100%",             padding: "8px",             marginBottom: "16px",             borderRadius: "4px",             border: "1px solid #ccc",             outline: "none",           }}         />          <label           style={{ marginBottom: "8px", fontWeight: "500" }}           htmlFor="delay"         >           Delay (ms)         </label>         <input           type="number"           id="delay"           value={delay}           onChange={(e) => setDelay(e.target.value)}           required           style={{             width: "100%",             padding: "8px",             marginBottom: "16px",             borderRadius: "4px",             border: "1px solid #ccc",             outline: "none",           }}         />          <button           type="submit"           disabled={isProcessing}           style={{             padding: "8px 16px",             color: "white",             borderRadius: "4px",             transition: "background-color 0.3s",             backgroundColor: isProcessing ? "#b0bec5" : "#2196F3",             cursor: isProcessing ? "not-allowed" : "pointer",           }}         >           {isProcessing ? "Capturing..." : "Capture Screenshot"}         </button>       </form>        {/* Example HTML Element to Capture */}       <div id="capture-area" style={{ display: "none" }}>         <h3           style={{             fontSize: "20px",             fontWeight: "600",           }}         >           Content to Capture         </h3>         <p>This is an example of the HTML content that will be captured.</p>       </div>     </div>   ); } 
Enter fullscreen mode Exit fullscreen mode

Conclusion

This tutorial covers setting up a webpage capture tool in Next.js, handling screenshots with Puppeteer, and creating an interactive frontend component. Remember to use puppeteer locally and switch to puppeteer-core in production to reduce bundle size and optimize for serverless environments. Happy coding!

beginnersjavascripttutorialwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    ES6 - A beginners guide - Template Literals

    • 0 Answers
  • Author

    Understanding Higher Order Functions in JavaScript.

    • 0 Answers
  • Author

    Build a custom video chat app with Daily and Vue.js

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.