Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

If by “PDF string” you mean HTML produced from a React component, render the component to an HTML string, load it into a Puppeteer page, and call page.pdf(). The result is PDF bytes, not another string. If you already have a PDF encoded as Base64 or text, that is a different task: decode it into bytes rather than passing it to Puppeteer as HTML.

What “PDF string” means in this workflow

React can render a component tree to static HTML markup with renderToStaticMarkup. That markup is an HTML string; it is not a PDF and does not contain PDF bytes. Puppeteer’s page.setContent() accepts that HTML, and page.pdf() generates the PDF as a Uint8Array. In a Node.js application, wrap the returned bytes with Buffer.from() when you need a Buffer.

This distinction matters because there are two very different inputs people may call a “PDF string”:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • React-generated HTML: use the workflow below to render it as a PDF.
  • Existing PDF data encoded as Base64 or text: decode that data to bytes. Do not load it with page.setContent(); Puppeteer expects HTML markup there.

The example uses an invoice, but the same pipeline works for other static documents. React’s static renderer creates non-interactive markup, so prepare data before rendering and do not rely on browser event handlers or client-side hydration to fill in the PDF.

#1 Best Overall
Epson EcoTank ET-2800 Wireless Color All-in-One Supertank Printer - Black
  • INNOVATIVE CARTRIDGE-FREE PRINTING — No more dealing with lots of tiny ink cartridges; With this wireless document and photo printer each ink bottle set is equivalent to about 90 individual cartridges²
  • LESS FREQUENT INK REPLACEMENT — Replacement ink bottles don't have to be changed nearly as often as ink cartridges¹; When you choose this combination printer, scanner and copier you can print up to 4,500 pages black/7,500 color³
  • COLOR PRINTING — Up to 2 years of ink in the box4 (and with every replacement ink set) for fewer out-of-ink frustrations
  • ZERO CARTRIDGE WASTE — By using an Epson EcoTank printer you can help reduce the amount of cartridge waste ending up in landfills
  • HOME PRINTER DESIGNED FOR RELIABILITY — The Epson EcoTank ET-2800 All-in-One Supertank Color Printer creates vivid, detailed prints and documents thanks to Micro Piezo Heat-Free Technology; Fire off 10 ISO pages per minute1 to easily finish large jobs

Install the packages and prepare a component

In a Node.js project, install React, React DOM, and Puppeteer:

npm install react react-dom puppeteer

The following component uses plain JavaScript and React.createElement, so it does not depend on a JSX transform. Keep document data in the component props; React will escape text values inserted as children.

// Invoice.js
import React from 'react';

const h = React.createElement;

export function Invoice({ invoice }) {
  return h('main', { className: 'invoice' },
    h('h1', null, 'Invoice ', invoice.number),
    h('p', null, `Bill to: ${invoice.customer}`),
    h('table', null,
      h('thead', null,
        h('tr', null,
          h('th', null, 'Description'),
          h('th', null, 'Amount')
        )
      ),
      h('tbody', null,
        ...invoice.items.map((item, index) =>
          h('tr', { key: index },
            h('td', null, item.description),
            h('td', null, item.amount)
          )
        )
      )
    )
  );
}

For a JSX-based application, you can use the same approach with your existing component and server-side build setup. The important boundary is that the server renderer receives a React node and returns markup; the browser page then lays out that markup for printing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Render the React markup and create the PDF

This complete function wraps the component HTML in a document shell, loads it into Puppeteer, creates A4 output with background graphics, and returns the generated bytes as a Node Buffer. The browser is closed in a finally block so an exception during page setup or PDF generation does not leave that browser process open.

// pdf.js
import puppeteer from 'puppeteer';
import { renderToStaticMarkup } from 'react-dom/server';
import { Invoice } from './Invoice.js';

export async function createInvoicePdf(invoice) {
  const markup = renderToStaticMarkup(Invoice({ invoice }));
  const html = `<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Invoice ${invoice.number}</title>
    <style>
      @page { size: A4; margin: 18mm; }
      body { font: 12pt Arial, sans-serif; color: #222; }
      h1 { font-size: 22pt; }
      table { width: 100%; border-collapse: collapse; }
      th, td { padding: 8px; border-bottom: 1px solid #ccc; text-align: left; }
      @media print { .invoice { break-inside: avoid; } }
    </style>
  </head>
  <body>${markup}</body>
</html>`;

  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.setContent(html);
    const pdfBytes = await page.pdf({
      format: 'A4',
      printBackground: true,
      margin: { top: '18mm', right: '18mm', bottom: '18mm', left: '18mm' },
    });
    return Buffer.from(pdfBytes);
  } finally {
    await browser.close();
  }
}

In this example, Invoice({ invoice }) creates the React element and renderToStaticMarkup() turns it into static markup. Alternatively, if your code already has a React element such as <Invoice invoice={invoice} />, pass that element directly to renderToStaticMarkup().

Rank #2
Sale
Epson EcoTank Photo ET-8550 Wireless Wide-Format All-in-One Tank Printer
  • CARTRIDGE-FREE PRINTING — Print lab-quality photos, graphics and creative projects; Get vibrant colors and sharp text with Epson's high-accuracy printhead and Claria ET Premium 6-color inks
  • INK BOTTLES — Save on photos1 and creative projects with affordable in-house printing; All-in-one printer allows you to print 4" x 6" photos for about 4 cents each vs. 40 cents with traditional ink cartridges1
  • LESS FREQUENT INK REPLACEMENT — Replacement ink bottles don't have to be changed nearly as often as ink cartridges¹; Printer, scanner and copier lets you print up to 6,200 color pages³
  • PRINT FOR LONGER — Up to 2 years of ink in the box² (and with every replacement ink set) for fewer out-of-ink frustrations with this wireless printer
  • ZERO CARTRIDGE WASTE — Epson EcoTank printer helps reduce the amount of cartridge waste ending up in landfills; Cartridge-free printer uses high-yield ink bottles; Each replacement ink bottle set is equivalent to about 100 individual ink cartridges⁴

The HTML includes a document type, character encoding, viewport, title, and styles. The @page rule sets print dimensions and margins in CSS; the PDF options set them as well. Choose one deliberate source of layout settings for your document and keep them consistent. The sample sets A4 and 18 mm margins in both places to make the intended result explicit.

Return the bytes from an HTTP endpoint

createInvoicePdf() returns bytes; it does not trigger a browser download by itself. An HTTP route can send those bytes with a PDF content type. For example, in an Express handler:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
app.get('/invoices/:id.pdf', async (req, res, next) => {
  try {
    const invoice = await loadInvoice(req.params.id);
    const pdf = await createInvoicePdf(invoice);
    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader('Content-Disposition', 'attachment; filename="invoice.pdf"');
    res.send(pdf);
  } catch (error) {
    next(error);
  }
});

Use inline instead of attachment in Content-Disposition if the intended behavior is to display the PDF in the browser rather than prompt for a download. The route example assumes your application already provides loadInvoice() and Express setup; those are application-specific, not Puppeteer APIs.

Choose print layout and rendering options

Puppeteer’s page.pdf() uses print CSS media by default. That means print-specific styles and @page rules can affect output even if the page looks different on screen. If the component’s layout is designed for screen styles instead, call await page.emulateMediaType('screen') before page.pdf(). This changes the media type; it does not remove the need to choose page dimensions and inspect page breaks.

Useful PDFOptions settings include:

  • format, or explicit width and height, for paper dimensions. The documented default format is Letter, so choose a format appropriate for the intended audience rather than assuming A4 is universal.
  • landscape for orientation; its documented default is false.
  • margin for top, right, bottom, and left page margins.
  • printBackground to include background graphics; the documented default is false.
  • preferCSSPageSize to give CSS page sizing preference when you define size through print CSS.
  • pageRanges to output selected page ranges, and scale to adjust rendered content size.
  • displayHeaderFooter, headerTemplate, and footerTemplate for page headers or footers.
  • path to save the PDF to a file. Without it, page.pdf() returns the PDF bytes without writing a file.
  • timeout and waitForFonts to control PDF-generation waiting behavior. The documented default for waitForFonts is true.

Puppeteer’s PDF documentation notes that printing may modify colors. For exact color adjustment, the documentation points to the CSS property -webkit-print-color-adjust; background graphics are a separate concern controlled by printBackground. Include only the visual options the document needs rather than treating one configuration as a universal preset.

Rank #3
HP Smart Tank 5000 Wireless All-in-One Ink Tank Printer, Scanner, Copier with 2 Years of Ink Included, Best-for-Home, Cartridge-Free, Refillable and AI-Enabled. (5D1B6A)
  • SET IT UP ONCE AND PRINT WITH CONFIDENCE. No complicated maintenance. Just easy, reliable printing you can count on.
  • INK FOR YEARS. NOT MONTHS. Up to 2 years of ink included. Get thousands of pages of cartridge-free printing. More pages, less hassle
  • KEEPS PRINTING WELL AFTER COMPETITORS HAVE QUIT. No complex maintenance. Sharper text, richer colors.[2] Only with HP Smart Tank
  • PREMIUM SUPPORT - Strong technical expertise to solve issues faster
  • THE LAST PRINTER YOU'LL EVER NEED. Enjoy years of refillable, cartridge-free printing.

Handle fonts, images, data, and page breaks

The output depends on what the page can load at render time. Puppeteer’s PDF guide says PDF generation waits for fonts by default, but the runtime still needs access to the font files. External fonts, stylesheets, and images can fail if the rendering environment has no network access, authentication is required, or the asset URL is incorrect. Where predictable output matters, use assets available to the process and verify that they load before producing the PDF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Static rendering does not wait for client-side React effects to fetch data or update the page. Fetch and validate the invoice or other document data before calling renderToStaticMarkup(). React also documents limited Suspense support for renderToStaticMarkup: if a component suspends, it immediately emits its fallback. Resolve required data before the render or ensure the output does not depend on a suspended component.

Long documents need print-specific layout decisions. Use CSS page-break rules and table styles suited to printing, then inspect actual output for clipped content, awkward row splits, unexpected blank pages, and headers or footers that overlap the body. These checks are especially important when content length varies from one request to another.

Troubleshooting common failures

  • The PDF contains the literal text or broken layout instead of a component. Confirm that you passed HTML markup generated from a React element, not a Base64 PDF string, a JSON string, or an unrendered component function.
  • Only a fallback or incomplete data appears. Load application data before static rendering. A static render does not run client-side effects, and a component that suspends can produce its fallback immediately.
  • Styles or images are missing. Check that stylesheets and asset URLs are included in the HTML or otherwise available to the page, and that the browser process can load them. For styles intended only for the screen, confirm whether print media is hiding or changing them.
  • Background colors or images disappear. Set printBackground: true. Color adjustment can also affect the printed appearance; use -webkit-print-color-adjust when exact color handling is required.
  • The printed design differs from the browser preview. Remember that PDF generation uses print media by default. Use print CSS for the document, or explicitly emulate screen media before generating the PDF if screen styling is intended.
  • The process cannot launch Chromium in deployment. Check the deployment environment’s browser availability and launch requirements, then configure Puppeteer for that runtime. Do not assume a local development setup and a restricted serverless or container runtime have identical browser dependencies.
  • The response is empty, corrupt, or downloads as text. Send the returned bytes without converting them to a string, and set Content-Type: application/pdf. Check that errors are not being swallowed before the response is sent.
  • Pages are clipped or have unexpected breaks. Revisit paper size, margins, content width, and print-specific break rules. Check the generated PDF using representative short and long documents.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Generating a PDF requires launching and driving a browser, so it is more operationally involved than returning a pre-existing file. For a small application, the straightforward launch-use-close lifecycle above is easy to reason about. If generation volume grows, measure the browser startup and render time in your own deployment before choosing a process-reuse or queueing strategy; the cited React and Puppeteer API documentation does not establish a universal throughput figure.

Keep the browser cleanup path, handle errors at the request boundary, and avoid starting a new PDF job for repeated requests when your application can safely reuse a previously generated result. Set a suitable request-level policy for long documents and slow external assets. Puppeteer exposes PDF timeout options, but a timeout value should reflect the runtime and document rather than be copied as a universal number.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
NDYIN Portable Printers Wireless for Travel, N80 Bluetooth Thermal Printer
  • Wireless Bluetooth Printer: Portable thermal printer compatible with iPhone, Android phones, iPad and tablet computers via Bluetooth. For smartphones, please download the "Nada Print" App. You can also connect to laptops and computers for printing using a USB-C cable. (Note: Laptops and computers can only be connected via USB and require the installation of a driver first. Bluetooth connection is not supported.)
  • No-ink printing: Only supports US Letter and A4 size thermal paper.(Doesn't support regular paper) The no-ink portable thermal printer uses direct thermal technology, requiring no ink, toner or ribbons, making it environmentally friendly, cost-effective and time-saving. The thermal printer package comes with a roll of US Letter thermal printing paper. Note: When installing the paper, remember to switch the paper size switch on APP
  • Clear Print: NDYIN N80 portable thermal printer adopts high-definition printing technology, with a 203DPI resolution to provide you with clear printing results. This mobile printer is compatible with roll paper, folded paper and tattoo transfer paper, supporting printing from your mobile phone PDF, Word, pictures and web pages anytime and anywhere. It is recommended to use our NDYIN thermal paper to achieve good printing quality
  • Portable wireless printer for travel: The thermal printer is equipped with a built-in 1500mAh rechargeable battery, which can print 160 sheets of 8.5" x 11" thermal paper after being fully charged. It weighs only 1.5 pounds and is compact in size. This ink-free portable printer can be easily carried in a backpack or briefcase! It is perfect for business travel, cars, small offices, construction sites, schools and homes. You can print documents, contracts, invoices and boarding passes anytime and anywhere
  • The N80 thermal printer has a wide range of uses. The package includes the N80 printer, a roll of US Letter paper(7m/roll), a user manual, a guide card, a type-C soft cable and a type C adapter. Note: The charging adapter is not included. Special thermal paper is required for use; ordinary paper cannot be used. This ink-free portable thermal printer is suitable for various scenarios such as home, school, travel, office, and outdoor, meeting the printing needs of different groups of people. This tattoo template printer is also compatible with tattoo transfer paper, making it an ideal choice for tattoo art

There is no per-PDF price in this self-hosted example from the cited APIs. The practical costs depend on the compute environment, browser runtime, and your operations. A hosted rendering option can avoid some browser-management work, but assess it against your security, data handling, layout, and service requirements.

Or skip the browser setup

If your content is available at a public URL and you need a screenshot or PDF of that page rather than a PDF made from an in-memory React component, ScreenshotNeo is a website screenshot API with an MCP server for AI agents. The one-call example below saves a WebP screenshot of a URL; it does not convert an arbitrary React HTML string into a PDF. See the ScreenshotNeo API documentation for API details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Before capture, ScreenshotNeo can accept consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. Its MCP tools—take_screenshot, get_page_info, and capture_pdf—let AI agents including Claude and Cursor use the service through an MCP client.

The Free plan includes 1,000 screenshots per month with no card required. Paid plans start at $5 for 3,000 shots; higher plans are Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing gives two months free, and every feature is available on every plan. Sign up free for 1,000 screenshots a month, with no card required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I use this method when my React component contains buttons or forms?

You can render their visual markup, but static HTML does not preserve interactive behavior or make the resulting PDF interactive.

Does this example save a file automatically?

No. It returns bytes; writing to disk or sending a download response is a separate application step.

Quick Recap

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.