OnedocLabs / react-print-pdf

Build and generate PDF using React 📄 UI kit for PDFs and print documents. Simple, reusable components and templates to create great invoices, docs, brochures. Use your favorite front-end framework React to build your next PDF.
https://docs.fileforge.com/react-print/welcome/getting-started
Apache License 2.0
2.18k stars 72 forks source link

React print not working when hosted on vercel #13

Open EbubeEvan opened 5 months ago

EbubeEvan commented 5 months ago

When I hosted my Nextjs 14 website on vercel, I was generating the pdf on a server component using tailwincss in the pdf template component.

It worked well on my computer but was throwing errors when hosted. I then changed it to generate on the backend using a route handler and I got an error saying that a particular tailwindcss module couldn't be found.

I changed the styling to plain CSS and I got a status 500 error. All this time, it worked perfectly on my computer so I wonder why it doesn't work when hosted on vercel.

Titou325 commented 5 months ago

Hey @EbubeEvan, could you confirm what version of react-print-pdf you are using? There have been improvements to running Tailwind server-side in the latest versions.

EbubeEvan commented 5 months ago

Hi @Titou325 I'm not sure as I had already uninstalled it before raising the issue. But I know it was the latest version as of March 10th when I installed it.

thebiltheory commented 4 months ago

Hey @Titou325,

I will make an assumption here, but it might be related to the fact that Vercel runs on Lambdas. Puppeteer relies on Chromium, which is way to large to run on Lambdas, but by using @sparticuz/chromium and puppeteer-core on production it will run smoothly.

I created a server action, but can also perfectly run on a route handler.

"use server";

import { Browser } from "puppeteer-core";

export async function makePDF(html) {
  let browser: Browser | undefined | null;

  if (process.env.NODE_ENV !== "development") {
    const chromium = require("@sparticuz/chromium");
    const puppeteer = require("puppeteer-core");

    browser = await puppeteer.launch({
      executablePath: await chromium.executablePath(),
      headless: chromium.headless,
      ignoreHTTPSErrors: true,
      defaultViewport: chromium.defaultViewport,
      args: [...chromium.args, "--hide-scrollbars", "--disable-web-security"],
    });
  } else {
    const puppeteer = require("puppeteer");
    browser = await puppeteer.launch({
      headless: "new",
    });
  }

  if (browser) {
    const page = await browser.newPage();
    await page.setContent(html);

    const pdfBuffer = await page.pdf({
      format: "a4",
      printBackground: true,
      margin: {
        top: 80,
        bottom: 80,
        left: 80,
        right: 80,
      },
    });

    await browser.close();

    return pdfBuffer;
  }

  return null;
}

Route handler:

import { compile } from "@onedoc/react-print";
import { PdfCv } from "../_components/pdf-cv";
import { makePDF } from "../actions/make-pdf";

export async function GET(request: Request) {
  try {
    const headers = new Headers();

    const html = await compile(<PdfCv />);
    const pdfBuffer = await makePDF(html);

    headers.append("Content-Type", "application/pdf");
    headers.append(
      "Content-Disposition",
      `attachment; filename="resume-${new Date().getFullYear()}.pdf"`
    );

    return new Response(pdfBuffer, { headers });
  } catch (error) {
    return new Response("Error downloading PDF", {
      status: 500,
      headers: {
        "Content-Type": "text/plain",
      },
    });
  }
}

You will also need to update your next.config.js file with the following.

experimental: {
    serverComponentsExternalPackages: ["puppeteer-core", "@sparticuz/chromium"],
  },
Titou325 commented 4 months ago

Hey @thebiltheory, thanks for these detailed examples! If using Onedoc as a backend, you don't actually need to run puppeteer as the payload is sent externally for rendering.

This issue originally came from our component that relied on the base Tailwind library, however Tailwind requires a full fledged file system to process just-in-time compilation. We switched to an earlier version of Tailwind patched for JIT a few weeks back to solve this issue, and haven't been able to reproduce it since. However, since there have been occasional reports I kept the thread open!

There is another consideration when running on Vercel regarding the functions timeout as PDF rendering can be slow, sometimes exceeding the default invocation time of 15s. This timeout can be changed for paid customers, and we are releasing speed improvements regularly.

Thanks!

thebiltheory commented 4 months ago

@Titou325 this is only if you use react-print to put the PDF together. I personally don't use Onedoc to render the PDF, also the Error 500 on Vercel doesn't say much, so I'm just assuming this could be one of the reasons if you only use react-print on the frontend.

Titou325 commented 4 months ago

Makes sense, do you still use the tailwind component or has it been switched to plain css as well?

thebiltheory commented 4 months ago

@Titou325, I use tailwind yes, also I believe Onedoc compiles everything to css?

I'm not even sure I need Tailwind. Will check.

EbubeEvan commented 4 months ago

I had to switch to react-to-print to download pdfs. which was a bummer cos I then had to display the document on the DOM which I didn't want. I also couldn't generate a pdf link to send via email. I really wish react print worked for me.

Titou325 commented 1 week ago

Note that the latest versions of react-print do work on Vercel, with the main caveat being that RSC (use client/use server) cannot be used. This is easy with the pages router, however with the app router, RSC is the default pattern. A common workaround is to expose 2 routes, one with the page and the other one with the call to Fileforge.

The API route calls the Page route to get the rendered HTML from React, and this is passed.

While non-ideal, this is currently hard to bypass as Next monkey patches React's rendering functions.

An issue is open with Next at https://github.com/vercel/next.js/issues/68150