← dompdf.js Studio

Generate Receipt PDF in Browser: A Complete Guide

Receipts are the most common proof of transaction across e-commerce, retail stores, property management, training institutions, and software services. After a payment succeeds, customers expect a clean, standardized receipt they can save, print, or forward to their finance team. Many systems still generate receipts on the backend, which makes template changes painful, introduces Chinese font problems, and consumes server resources on every single receipt issued. dompdf.js moves receipt generation entirely to the browser: transaction data is assembled into HTML locally, rendered to vector PDF through addPage, and downloaded with save, with zero backend participation. This article covers the full receipt PDF workflow, including template design, amount formatting, multi-page receipts, batch printing considerations, and common pitfalls, so you can ship an online receipt feature in minutes. We will also cover refund receipts, narrow-format templates for thermal printers, timezone handling, and the data hygiene rules that keep receipts legally sound. The patterns here apply equally to a small store and a large platform, because the rendering work happens on the customer's device either way. After reading, you will be able to add an online receipt feature to almost any payment flow with a modest amount of code, and your finance team will thank you for the consistently formatted output.

Receipt Scenarios and Format Requirements

Receipts appear in e-commerce platforms, retail stores, property management, training programs, and software billing. A proper receipt contains the merchant name, transaction date, receipt number, line items, amounts in both digits and Chinese uppercase, and the payment method. It must satisfy accounting requirements while remaining instantly readable for the customer.

Electronic receipts have clear advantages over paper: they never get lost, are easy to archive, and can be re-downloaded anytime. Many platforms automatically generate a receipt PDF after a successful payment and email it to the customer, saving printing and postage costs while dramatically improving the experience.

Receipt generation has strict timing requirements. Customers check receipts immediately after paying, so speed matters. Frontend generation renders everything in the user's browser with no network round trip and no server queue, giving near-zero latency, compared to the hundreds of milliseconds or even seconds a backend render typically takes.

Refund receipts deserve special attention. In a refund scenario the receipt must clearly state the original transaction number, the refunded amount, and the reason, with the amount shown as negative or in parentheses rather than relying on color alone, since many receipts are printed in black and white. Terms such as refund-only and no-return should be spelled out to prevent later disputes, and the template can reuse the standard receipt with a refund-specific configuration.

Receipt formats vary by industry, but a few fields are universal: merchant name, receipt number, transaction date and time, line items, totals, and payment method. Standardizing on these fields across your organization makes reconciliation dramatically easier, because finance can compare receipts from different channels using the same keys instead of mapping half a dozen proprietary layouts by hand.

Think also about multi-currency receipts. A platform serving international customers may need to show the amount in the settlement currency and the customer's currency, with the exchange rate printed for transparency. Adding a small exchange-rate line to the template is trivial, and it preempts a whole category of customer questions about why the charged amount differs from the displayed amount.

For recurring billing, the receipt should reference the subscription or invoice number so customers can connect the payment to the cycle it belongs to. Consistent cross-referencing between receipts, invoices, and subscriptions is what makes a customer's financial records coherent over time.

What dompdf.js Brings to the Receipt Use Case

Receipts have a fixed, well-structured layout, which makes them a perfect fit for HTML templates. dompdf.js renders the DOM directly to PDF, so the template is the page. Developers do not need to learn a drawing API; if you can write HTML and CSS, you can produce compliant receipts quickly, and maintenance stays cheap.

Chinese typography is where many solutions stumble. dompdf.js bundles Source Han Sans SC, so Chinese punctuation, uppercase amount characters, and special symbols all render correctly without installing fonts on a server or maintaining font mapping tables. Garbled characters and tofu blocks simply disappear.

On security, receipt data is transaction data. With a frontend approach the data never leaves the browser and never passes through a third-party rendering service, which reduces the risk of leakage. Vector PDFs are small, sharp, and searchable, making later verification and reconciliation easier for both customers and finance teams.

Cross-platform consistency is another strong reason to go frontend. dompdf.js renders the same layout in every mainstream modern browser because it does not depend on the operating system's font library; a receipt generated on Windows, macOS, or Linux looks identical. Server-side solutions, by contrast, often require per-distribution font packages, and a missing CJK font on one node can silently corrupt a batch of receipts.

Operational simplicity compounds over time. There is no rendering service to monitor, no queue to tune, and no template cache to invalidate. The receipt feature degrades gracefully with the rest of the frontend, and new receipt types can be shipped as pure frontend changes, which most teams can release several times a day rather than once a week.

Receipt Template Design Essentials

A receipt template has three zones: the header with the merchant logo, receipt title, and receipt number; the middle section with the line-item table showing product, unit price, quantity, and amount; and the footer with the total, the amount in Chinese uppercase, payment method, and issue date. Together they form a complete visual loop.

Use border-collapse: collapse for uniform borders, right-align the amount column, and keep two decimal places. Make the total row bold or give it a light background so it stands out. Keep the receipt width within the readable page area so printing never truncates the content.

The amount in Chinese uppercase is a required element on Chinese receipts. Implement a small utility function that converts numbers to uppercase Chinese characters and fill it into the template before rendering. Use the YYYY-MM-DD date format, and design receipt numbers that include the date plus a sequence so they are easy to reconcile.

Keep the template styles in a style tag rather than scattering inline styles, so brand colors and fonts can be adjusted centrally. It is also worth producing two layouts from the same data: a narrow version modeled on 75mm or 80mm thermal paper for in-store printing, and an A4 version for electronic delivery. One data source, two templates, and both the store and the online channel are covered.

Consider accessibility and readability: use sufficient contrast between text and background, keep body font sizes above 9pt, and avoid decorative fonts for amounts. A receipt that is easy to scan at a glance reduces customer service questions, and a consistent typographic style quietly reinforces the merchant's brand with every transaction.

Code Example: Generate an Electronic Receipt in One Click

This example shows how to generate a standard electronic receipt with dompdf.js. The code builds the receipt HTML with merchant information, transaction line items, and the total, then creates a DomPDF instance, renders, and saves. The core logic is remarkably short.

In production, receipt data usually comes from a payment callback or an order API. Wrap the template in a buildReceiptHtml(data) function that takes an order object and returns the HTML string, then call a separate generate function to render and save. Keeping business logic and templates apart makes the code easier to maintain and test.

The two-column table in the example is the most common structure for small receipts. For itemized receipts with many lines, use a dedicated table with columns for product, quantity, unit price, and amount, stacked above the totals area. When adding columns, keep the total width at 100 percent and let the product column take the remaining space, so content never overflows the page.

Although dompdf.js bundles Source Han Sans SC, declaring the font family explicitly is still good practice: it ensures a clean fallback in custom or English-only environments and makes the template self-documenting. For amounts, a monospaced or half-width font keeps digits visually aligned across rows, which matters a surprising amount when finance staff scan columns of numbers for discrepancies.

import { DomPDF } from 'dompdf.js';

const receiptHtml = `
  <div style='font-family:Source Han Sans SC,sans-serif;width:100%'>
    <div style='text-align:center;border-bottom:2px solid #333;padding-bottom:10px'>
      <h1 style='margin:0;font-size:18pt'>Electronic Receipt</h1>
      <p style='margin:4px 0;font-size:10pt'>Acme Technology Co., Ltd.</p>
      <p style='margin:0;font-size:10pt'>Receipt No: R20260818001</p>
    </div>
    <table style='width:100%;border-collapse:collapse;margin-top:12px'>
      <tr>
        <th style='border:1px solid #333;padding:8px;text-align:left'>Item</th>
        <th style='border:1px solid #333;padding:8px;text-align:right'>Amount</th>
      </tr>
      <tr>
        <td style='border:1px solid #333;padding:8px'>Software subscription (Aug 2026)</td>
        <td style='border:1px solid #333;padding:8px;text-align:right'>$299.00</td>
      </tr>
      <tr>
        <td style='border:1px solid #333;padding:8px;text-align:right;font-weight:bold'>Total</td>
        <td style='border:1px solid #333;padding:8px;text-align:right;font-weight:bold'>$299.00</td>
      </tr>
    </table>
    <p style='font-size:10pt'>Payment method: WeChat Pay    Paid at: 2026-08-18 10:23:45</p>
  </div>
`;

const pdf = new DomPDF();
pdf.addPage(receiptHtml, { format: 'A4', margin: '20mm' });
pdf.save('receipt-R20260818001.pdf');

Multi-Page Receipts, Batch Printing, and Pagination Control

When a single transaction has many line items, the receipt exceeds one page. dompdf.js paginates automatically, splitting the content by page height, and the @page media properties control margins and page size so printed output keeps consistent margins and nothing is clipped.

For batch scenarios, such as a store issuing dozens of receipts per day, loop through the transactions, call addPage for each receipt, and save once to produce a single PDF containing all receipts. Start each receipt on a new page and use pagination control so individual receipts never bleed into one another.

Receipt number uniqueness deserves attention. Ideally the backend allocates the number before the frontend renders, avoiding duplicates under concurrency. If you must work without a backend, generate numbers from a timestamp plus a random component and keep a local record of issued numbers for duplicate checking.

For printed receipts, put the receipt number and page number in the footer of every page so finance can file multi-page receipts without confusion. If receipts must be archived, save all pages as a single PDF named after the receipt number and date, such as R20260818-001.pdf, and retrieval becomes a simple lookup by number.

Batch issuance also benefits from a small UX touch: show progress while generating, then offer both a combined PDF and individual files. The combined file is convenient for printing a shift's receipts at once, while individual files are easier to email to customers who request a copy, so offering both covers the full range of workflows.

Frequently Asked Questions and Best Practices

Problem one: the logo or seal image does not show. Convert images to base64 and embed them inline, and always set an explicit width. Avoid very large images that slow down rendering or bloat the file size.

Problem two: floating point errors in amounts. Do arithmetic in integer cents, or format with toFixed(2) before rendering. Never accumulate raw floats and then render, or you will see artifacts like 299.00000000000006 in the PDF.

Problem three: the download fails in some browsers. The save method relies on browser download behavior, so confirm the page allows downloads before calling it, wrap the call in error handling, and fall back to opening the PDF in a new window if the download fails.

Time and timezone handling deserve attention: the transaction time on the receipt should be the merchant's local time, not the user's device time. For cross-timezone SaaS products, have the backend return a canonical timestamp in the payment callback and let the frontend render it directly, avoiding receipts whose timestamps disagree with the payment provider's records.

Finally, think about data retention. Decide how long receipt data and generated files are kept, where they are stored, and who can access them. Documenting this policy matters for compliance, and the frontend approach keeps the footprint small because rendering never creates server-side copies unless you explicitly choose to archive them.

Error handling deserves a paragraph of its own. Rendering is fast, but a malformed template or an unexpected data shape can still throw. Wrap the render call in try-catch, log the failure with the receipt context, and show the user a friendly retry message instead of a blank page or a cryptic console error.

And do not forget localization. Currency symbols, date formats, and decimal separators differ by locale; render them from locale data rather than hard-coding, so a receipt generated for a customer in Germany looks right to them, not like an English template with symbols swapped in.

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。