Free email template
Receipt email template
A payment receipt with line items, tax, total and a link to the full invoice. Free and MIT-licensed, in React Email, MJML, HTML and plain text, with light and dark mode built in.
- MIT license
- Works with any email provider
- Light and dark mode
What every receipt email needs.
This template does all six. The HTML version has one line item; for several, render the React component or repeat the row in the MJML.
Line items people recognise
What they paid for, in the words they'd use: plan name and period, add-ons, seats. A total on its own makes people check their bank statement.
Subtotal, tax and total
On separate lines. Finance teams need the tax figure, and the total should match the charge on the card to the cent.
Receipt number, date and card
The receipt number is what support searches for. Show the card as brand and last four digits, never the full number.
A link to the full invoice
The email is a summary. Link to the invoice page with billing address, tax IDs and a PDF, which is what accounting tools ask for.
Your legal name and address
Your company's legal name and registered address, plus the name on the card statement if it's different. The template's footer carries the name and address from theme.ts.
Who to ask about the charge
A reply line or support address for billing questions. A question answered by email is often a chargeback that never happens.
The code. Copy the format your stack uses.
Change the brand name, logo and colors once in theme.ts and every template follows.
import * as React from 'react'
import { Button, Details, Layout, P, Small, Title } from '../../components'
import type { TemplateMeta } from '../../components/meta'
import { plainText, textDetails } from '../../components/text'
import { theme } from '../../theme'
export type ReceiptItem = { description: string; amount: string }
export type ReceiptProps = {
name: string
receiptNumber: string
paidAt: string
paymentMethod: string
items: ReceiptItem[]
subtotal: string
tax: string
total: string
receiptUrl: string
}
export const meta: TemplateMeta = {
name: 'Receipt',
description: 'A payment receipt with line items, tax, total and a link to the full invoice.',
group: 'Billing',
subject: `Your ${theme.brand.name} receipt #{{receipt_number}}`,
}
/**
* Account templates only fill flat `{{variables}}`, so the hosted version has one
* line item (`item_description`, `item_amount`). For several items, render this
* component in your app and send the HTML, or send `items` with the React element.
*/
export const placeholders: Partial<ReceiptProps> = {
items: [{ description: '{{item_description}}', amount: '{{item_amount}}' }],
}
export default function Receipt(props: ReceiptProps) {
const { name, receiptNumber, paidAt, paymentMethod, items, subtotal, tax, total, receiptUrl } = props
return (
<Layout
preview={`We received your payment of ${total}. Receipt #${receiptNumber}.`}
footerNote="You're receiving this because you made a payment. Keep it for your records."
>
<Title>Thanks for your payment</Title>
<P>
Hi {name}, we received your payment of <strong>{total}</strong>. Here's your receipt.
</P>
<Details
rows={[
['Receipt number', receiptNumber],
['Date paid', paidAt],
['Payment method', paymentMethod],
]}
/>
<Details
rows={[...items.map((item): [string, string] => [item.description, item.amount]), ['Subtotal', subtotal], ['Tax', tax]]}
total={['Total paid', total]}
/>
<Button href={receiptUrl}>View invoice</Button>
<Small style={{ margin: 0 }}>Questions about this charge? Reply to this email and we'll help.</Small>
</Layout>
)
}
Receipt.PreviewProps = {
name: 'Ada',
receiptNumber: '2041-7730',
paidAt: 'October 16, 2026',
paymentMethod: 'Visa ending in 4242',
items: [
{ description: 'Pro plan (monthly)', amount: '$40.00' },
{ description: 'Extra seats × 2', amount: '$10.00' },
],
subtotal: '$50.00',
tax: '$4.13',
total: '$54.13',
receiptUrl: 'https://example.com/billing/invoices/2041-7730',
} satisfies ReceiptProps
export function text(props: ReceiptProps) {
const { name, receiptNumber, paidAt, paymentMethod, items, subtotal, tax, total, receiptUrl } = props
return plainText(
'Thanks for your payment',
`Hi ${name}, we received your payment of ${total}. Here's your receipt.`,
textDetails([
['Receipt number', receiptNumber],
['Date paid', paidAt],
['Payment method', paymentMethod],
]),
textDetails([
...items.map((item): [string, string] => [item.description, item.amount]),
['Subtotal', subtotal],
['Tax', tax],
['Total paid', total],
]),
`View invoice: ${receiptUrl}`,
"Questions about this charge? Reply to this email and we'll help.",
)
}
<mjml lang="en" dir="ltr">
<mj-head>
<mj-title>Thanks for your payment</mj-title>
<mj-preview>We received your payment of {{total}}. Receipt #{{receipt_number}}.</mj-preview>
<!-- theme:start -->
<!-- Generated from theme.ts by `npm run build`. Edit theme.ts, not this block. -->
<mj-raw>
<meta name="color-scheme" content="light dark" />
<meta name="supported-color-schemes" content="light dark" />
</mj-raw>
<mj-attributes>
<mj-all font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" />
<mj-body background-color="#f4f4f5" width="560px" />
<mj-wrapper padding="40px 16px" />
<mj-section padding="0" />
<mj-column padding="0" />
<mj-text padding="0 0 16px" font-size="15px" line-height="24px" color="#18181b" css-class="em-text" />
<mj-button align="left" background-color="#2f5bd3" color="#ffffff" font-size="15px" font-weight="600" line-height="20px" border-radius="8px" inner-padding="12px 22px" padding="8px 0 24px" css-class="em-button" />
<mj-divider border-width="1px" border-color="#e4e4e7" padding="12px 0 28px" css-class="em-rule" />
<mj-table padding="0 0 24px" font-size="14px" line-height="20px" color="#18181b" />
<mj-class name="card" background-color="#ffffff" border="1px solid #e4e4e7" border-radius="12px" padding="40px" css-class="em-card" />
<mj-class name="h1" font-size="24px" line-height="32px" font-weight="650" letter-spacing="-0.3px" css-class="em-text em-h1" />
<mj-class name="small" font-size="13px" line-height="20px" color="#52525b" padding="0 0 12px" css-class="em-muted" />
<mj-class name="danger" background-color="#b42318" css-class="em-button-danger" />
<mj-class name="footer" font-size="12px" line-height="18px" color="#52525b" align="center" padding="0 0 6px" css-class="em-muted" />
</mj-attributes>
<mj-style inline="inline">
.em-label { padding: 10px 0; color: #52525b; width: 45%; vertical-align: top; }
.em-value { padding: 10px 0; color: #18181b; text-align: right; vertical-align: top; }
.em-row-border { border-top: 1px solid #e4e4e7; }
.em-total { padding: 12px 0 10px; color: #18181b; font-weight: 650; border-top: 2px solid #18181b; vertical-align: top; }
.em-total-value { padding: 12px 0 10px; color: #18181b; font-weight: 650; border-top: 2px solid #18181b; text-align: right; vertical-align: top; }
.em-codebox { margin: 8px 0 8px; padding: 18px 12px; background-color: #f4f4f5; border: 1px solid #e4e4e7; border-radius: 8px; text-align: center; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace; font-size: 32px; line-height: 40px; font-weight: 700; letter-spacing: 8px; color: #18181b; }
.em-callout { padding: 14px 16px; background-color: #f4f4f5; border: 1px solid #e4e4e7; border-radius: 8px; font-size: 14px; line-height: 22px; color: #18181b; }
.em-track { height: 8px; font-size: 1px; line-height: 8px; border-radius: 999px; background-color: #f4f4f5; }
.em-fill { height: 8px; font-size: 1px; line-height: 8px; border-radius: 999px; background-color: #2f5bd3; }
.em-link { color: #2f5bd3; text-decoration: underline; }
.em-step { color: #52525b; }
</mj-style>
<mj-style>
body { -webkit-font-smoothing: antialiased; }
:root { color-scheme: light dark; supported-color-schemes: light dark; }
.em-logo-dark { display: none; }
@media (prefers-color-scheme: dark) {
body, .em-page { background-color: #0e0e10 !important; }
.em-card, .em-card > table, .em-card > table > tbody > tr > td { background-color: #18181b !important; border-color: #2e2e33 !important; }
.em-text, .em-text > div { color: #f4f4f5 !important; }
.em-muted, .em-muted > div, .em-muted a { color: #a1a1aa !important; }
.em-link, .em-text a { color: #7a9bff !important; }
.em-wordmark, .em-wordmark > div, .em-wordmark a { color: #f4f4f5 !important; }
.em-subtle { background-color: #232327 !important; border-color: #2e2e33 !important; }
.em-border { border-color: #2e2e33 !important; }
.em-strong-border { border-color: #f4f4f5 !important; }
.em-rule, .em-rule p { border-top-color: #2e2e33 !important; }
.em-track { background-color: #232327 !important; }
.em-fill { background-color: #7a9bff !important; }
a.em-button, .em-button a, .em-button td[bgcolor] { background-color: #7a9bff !important; color: #0b0d14 !important; border-color: #7a9bff !important; }
a.em-button-danger, .em-button-danger a, .em-button-danger td[bgcolor] { background-color: #f97066 !important; color: #0b0d14 !important; border-color: #f97066 !important; }
.em-logo-light { display: none !important; }
.em-logo-dark { display: block !important; }
}
[data-ogsc] .em-card, [data-ogsc] .em-card > table, [data-ogsc] .em-card > table > tbody > tr > td { border-color: #2e2e33 !important; }
[data-ogsc] .em-text, [data-ogsc] .em-text > div { color: #f4f4f5 !important; }
[data-ogsc] .em-muted, [data-ogsc] .em-muted > div, [data-ogsc] .em-muted a { color: #a1a1aa !important; }
[data-ogsc] .em-link, [data-ogsc] .em-text a { color: #7a9bff !important; }
[data-ogsc] .em-wordmark, [data-ogsc] .em-wordmark > div, [data-ogsc] .em-wordmark a { color: #f4f4f5 !important; }
[data-ogsc] .em-subtle { border-color: #2e2e33 !important; }
[data-ogsc] .em-border { border-color: #2e2e33 !important; }
[data-ogsc] .em-strong-border { border-color: #f4f4f5 !important; }
[data-ogsc] .em-rule, [data-ogsc] .em-rule p { border-top-color: #2e2e33 !important; }
[data-ogsc] a.em-button, [data-ogsc] .em-button a, [data-ogsc] .em-button td[bgcolor] { color: #0b0d14 !important; border-color: #7a9bff !important; }
[data-ogsc] a.em-button-danger, [data-ogsc] .em-button-danger a, [data-ogsc] .em-button-danger td[bgcolor] { color: #0b0d14 !important; border-color: #f97066 !important; }
[data-ogsb] body, [data-ogsb] .em-page { background-color: #0e0e10 !important; }
[data-ogsb] .em-card, [data-ogsb] .em-card > table, [data-ogsb] .em-card > table > tbody > tr > td { background-color: #18181b !important; border-color: #2e2e33 !important; }
[data-ogsb] .em-subtle { background-color: #232327 !important; border-color: #2e2e33 !important; }
[data-ogsb] .em-border { border-color: #2e2e33 !important; }
[data-ogsb] .em-strong-border { border-color: #f4f4f5 !important; }
[data-ogsb] .em-rule, [data-ogsb] .em-rule p { border-top-color: #2e2e33 !important; }
[data-ogsb] .em-track { background-color: #232327 !important; }
[data-ogsb] .em-fill { background-color: #7a9bff !important; }
[data-ogsb] a.em-button, [data-ogsb] .em-button a, [data-ogsb] .em-button td[bgcolor] { background-color: #7a9bff !important; border-color: #7a9bff !important; }
[data-ogsb] a.em-button-danger, [data-ogsb] .em-button-danger a, [data-ogsb] .em-button-danger td[bgcolor] { background-color: #f97066 !important; border-color: #f97066 !important; }
@media only screen and (max-width: 600px) {
.em-card > table > tbody > tr > td { padding: 32px 24px !important; }
.em-h1, .em-h1 > div { font-size: 22px !important; line-height: 30px !important; }
}
</mj-style>
<!-- theme:end -->
</mj-head>
<mj-body>
<!-- header:start -->
<!-- Generated from theme.ts by `npm run build`. Edit theme.ts, not this block. -->
<mj-wrapper css-class="em-page">
<mj-section padding="0 8px 24px">
<mj-column>
<mj-text padding="0" font-size="18px" line-height="28px" font-weight="700" letter-spacing="-0.2px" css-class="em-wordmark"><a href="https://example.com" style="color:#18181b;text-decoration:none;">Acme</a></mj-text>
</mj-column>
</mj-section>
<!-- header:end -->
<mj-section mj-class="card">
<mj-column>
<mj-text mj-class="h1">Thanks for your payment</mj-text>
<mj-text>Hi {{name}}, we received your payment of <strong>{{total}}</strong>. Here's your receipt.</mj-text>
<mj-table>
<tr><td class="em-label em-muted">Receipt number</td><td class="em-value em-text">{{receipt_number}}</td></tr>
<tr><td class="em-label em-muted em-row-border em-border">Date paid</td><td class="em-value em-text em-row-border em-border">{{paid_at}}</td></tr>
<tr><td class="em-label em-muted em-row-border em-border">Payment method</td><td class="em-value em-text em-row-border em-border">{{payment_method}}</td></tr>
</mj-table>
<!-- Repeat the first row once per line item. -->
<mj-table>
<tr><td class="em-label em-muted">{{item_description}}</td><td class="em-value em-text">{{item_amount}}</td></tr>
<tr><td class="em-label em-muted em-row-border em-border">Subtotal</td><td class="em-value em-text em-row-border em-border">{{subtotal}}</td></tr>
<tr><td class="em-label em-muted em-row-border em-border">Tax</td><td class="em-value em-text em-row-border em-border">{{tax}}</td></tr>
<tr><td class="em-total em-text em-strong-border">Total paid</td><td class="em-total-value em-text em-strong-border">{{total}}</td></tr>
</mj-table>
<mj-button href="{{receipt_url}}">View invoice</mj-button>
<mj-text mj-class="small" padding="0">Questions about this charge? Reply to this email and we'll help.</mj-text>
</mj-column>
</mj-section>
<mj-section padding="24px 8px 0">
<mj-column>
<mj-text mj-class="footer">You're receiving this because you made a payment. Keep it for your records.</mj-text>
</mj-column>
</mj-section>
<!-- footer:start -->
<!-- Generated from theme.ts by `npm run build`. Edit theme.ts, not this block. -->
<mj-section padding="0 8px">
<mj-column>
<mj-text mj-class="footer">Acme Inc. · 100 Market Street · San Francisco, CA 94105</mj-text>
</mj-column>
</mj-section>
</mj-wrapper>
<!-- footer:end -->
</mj-body>
</mjml>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<meta name="color-scheme" content="light dark" />
<meta name="supported-color-schemes" content="light dark" />
<title>
We received your payment of {{total}}. Receipt #{{receipt_number}}.
</title>
<style>
:root { color-scheme: light dark; supported-color-schemes: light dark; }
.em-logo-dark { display: none; }
@media (prefers-color-scheme: dark) {
.em-page, .em-page > table > tbody > tr > td { background-color: #0e0e10 !important; }
.em-card { background-color: #18181b !important; border-color: #2e2e33 !important; }
.em-text, .em-text > div { color: #f4f4f5 !important; }
.em-muted, .em-muted > div, .em-muted a { color: #a1a1aa !important; }
.em-link, .em-text a { color: #7a9bff !important; }
.em-wordmark, .em-wordmark > div, .em-wordmark a { color: #f4f4f5 !important; }
.em-subtle { background-color: #232327 !important; border-color: #2e2e33 !important; }
.em-border { border-color: #2e2e33 !important; }
.em-strong-border { border-color: #f4f4f5 !important; }
.em-rule, .em-rule p { border-top-color: #2e2e33 !important; }
.em-track { background-color: #232327 !important; }
.em-fill { background-color: #7a9bff !important; }
a.em-button, .em-button a, .em-button td[bgcolor] { background-color: #7a9bff !important; color: #0b0d14 !important; border-color: #7a9bff !important; }
a.em-button-danger, .em-button-danger a, .em-button-danger td[bgcolor] { background-color: #f97066 !important; color: #0b0d14 !important; border-color: #f97066 !important; }
.em-logo-light { display: none !important; }
.em-logo-dark { display: block !important; }
}
[data-ogsc] .em-card { border-color: #2e2e33 !important; }
[data-ogsc] .em-text, [data-ogsc] .em-text > div { color: #f4f4f5 !important; }
[data-ogsc] .em-muted, [data-ogsc] .em-muted > div, [data-ogsc] .em-muted a { color: #a1a1aa !important; }
[data-ogsc] .em-link, [data-ogsc] .em-text a { color: #7a9bff !important; }
[data-ogsc] .em-wordmark, [data-ogsc] .em-wordmark > div, [data-ogsc] .em-wordmark a { color: #f4f4f5 !important; }
[data-ogsc] .em-subtle { border-color: #2e2e33 !important; }
[data-ogsc] .em-border { border-color: #2e2e33 !important; }
[data-ogsc] .em-strong-border { border-color: #f4f4f5 !important; }
[data-ogsc] .em-rule, [data-ogsc] .em-rule p { border-top-color: #2e2e33 !important; }
[data-ogsc] a.em-button, [data-ogsc] .em-button a, [data-ogsc] .em-button td[bgcolor] { color: #0b0d14 !important; border-color: #7a9bff !important; }
[data-ogsc] a.em-button-danger, [data-ogsc] .em-button-danger a, [data-ogsc] .em-button-danger td[bgcolor] { color: #0b0d14 !important; border-color: #f97066 !important; }
[data-ogsb] .em-page, [data-ogsb] .em-page > table > tbody > tr > td { background-color: #0e0e10 !important; }
[data-ogsb] .em-card { background-color: #18181b !important; border-color: #2e2e33 !important; }
[data-ogsb] .em-subtle { background-color: #232327 !important; border-color: #2e2e33 !important; }
[data-ogsb] .em-border { border-color: #2e2e33 !important; }
[data-ogsb] .em-strong-border { border-color: #f4f4f5 !important; }
[data-ogsb] .em-rule, [data-ogsb] .em-rule p { border-top-color: #2e2e33 !important; }
[data-ogsb] .em-track { background-color: #232327 !important; }
[data-ogsb] .em-fill { background-color: #7a9bff !important; }
[data-ogsb] a.em-button, [data-ogsb] .em-button a, [data-ogsb] .em-button td[bgcolor] { background-color: #7a9bff !important; border-color: #7a9bff !important; }
[data-ogsb] a.em-button-danger, [data-ogsb] .em-button-danger a, [data-ogsb] .em-button-danger td[bgcolor] { background-color: #f97066 !important; border-color: #f97066 !important; }
@media only screen and (max-width: 600px) {
.em-card-body > tbody > tr > td { padding: 32px 24px !important; }
.em-h1, .em-h1 > div { font-size: 22px !important; line-height: 30px !important; }
}
</style>
</head>
<body
class="em-page"
dir="ltr"
lang="en"
style="background-color:#f4f4f5;margin:0;padding:0">
<div
style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0"
data-skip-in-text="true">
We received your payment of {{total}}. Receipt #{{receipt_number}}.
<div>
</div>
</div>
<table
border="0"
width="100%"
cellpadding="0"
cellspacing="0"
role="presentation"
align="center">
<tbody>
<tr>
<td
dir="ltr"
lang="en"
style="margin:0;padding:0;background-color:#f4f4f5;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;-webkit-font-smoothing:antialiased">
<table
align="center"
width="100%"
class="em-page"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="max-width:560px;width:100%;margin:0 auto">
<tbody>
<tr style="width:100%">
<td style="padding:40px 16px">
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation">
<tbody>
<tr>
<td style="padding:0 8px 24px">
<a
href="https://example.com"
style="color:#067df7;text-decoration-line:none;text-decoration:none"
target="_blank"
><p
class="em-wordmark"
style="font-size:18px;line-height:28px;margin:0;font-weight:700;letter-spacing:-0.2px;color:#18181b;margin-top:0;margin-bottom:0;margin-left:0;margin-right:0">
Acme
</p></a
>
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-card"
style="background-color:#ffffff;border:1px solid #e4e4e7;border-radius:12px">
<tbody>
<tr>
<td>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-card-body">
<tbody>
<tr>
<td style="padding:40px 40px">
<h1
class="em-text em-h1"
style="margin:0 0 16px;font-size:24px;line-height:32px;font-weight:650;letter-spacing:-0.3px;color:#18181b">
Thanks for your payment
</h1>
<p
class="em-text"
style="font-size:15px;line-height:24px;margin:0 0 16px;color:#18181b;margin-top:0;margin-right:0;margin-bottom:16px;margin-left:0">
Hi {{name}}, we received your payment of
<strong>{{total}}</strong>. Here's
your receipt.
</p>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="margin:0 0 24px">
<tbody>
<tr>
<td>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-border">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-muted"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#52525b;width:45%">
Receipt number
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;text-align:right">
{{receipt_number}}
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-border"
style="border-top:1px solid #e4e4e7">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-muted"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#52525b;width:45%">
Date paid
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;text-align:right">
{{paid_at}}
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-border"
style="border-top:1px solid #e4e4e7">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-muted"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#52525b;width:45%">
Payment method
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;text-align:right">
{{payment_method}}
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="margin:0 0 24px">
<tbody>
<tr>
<td>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-border">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-muted"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#52525b;width:45%">
{{item_description}}
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;text-align:right">
{{item_amount}}
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-border"
style="border-top:1px solid #e4e4e7">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-muted"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#52525b;width:45%">
Subtotal
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;text-align:right">
{{subtotal}}
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-border"
style="border-top:1px solid #e4e4e7">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-muted"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#52525b;width:45%">
Tax
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;text-align:right">
{{tax}}
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
class="em-strong-border"
style="border-top:2px solid #18181b">
<tbody style="width:100%">
<tr style="width:100%">
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;font-weight:650;padding-top:12px">
Total paid
</td>
<td
class="em-text"
data-id="__react-email-column"
style="padding:10px 0;font-size:14px;line-height:20px;vertical-align:top;color:#18181b;font-weight:650;text-align:right;padding-top:12px">
{{total}}
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="margin:8px 0 24px">
<tbody>
<tr>
<td>
<a
class="em-button"
href="{{receipt_url}}"
style="line-height:20px;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;background-color:#2f5bd3;color:#ffffff;font-size:15px;font-weight:600;border-radius:8px;padding:12px 22px;padding-top:12px;padding-right:22px;padding-bottom:12px;padding-left:22px"
target="_blank"
><span
><!--[if mso]><i style="mso-font-width:366.66666666666663%;mso-text-raise:18px" hidden>   </i><![endif]--></span
><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px"
>View invoice</span
><span
><!--[if mso]><i style="mso-font-width:366.66666666666663%" hidden>   ​</i><![endif]--></span
></a
>
</td>
</tr>
</tbody>
</table>
<p
class="em-muted"
style="font-size:13px;line-height:20px;margin:0;color:#52525b;margin-top:0;margin-bottom:0;margin-left:0;margin-right:0">
Questions about this charge? Reply to this
email and we'll help.
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="text-align:center">
<tbody>
<tr>
<td style="padding:24px 8px 0">
<p
class="em-muted"
style="font-size:12px;line-height:18px;margin:0 0 6px;color:#52525b;margin-top:0;margin-right:0;margin-bottom:6px;margin-left:0">
You're receiving this because you made a
payment. Keep it for your records.
</p>
<p
class="em-muted"
style="font-size:12px;line-height:18px;margin:0 0 6px;color:#52525b;margin-top:0;margin-right:0;margin-bottom:6px;margin-left:0">
Acme Inc. · 100 Market Street · San Francisco, CA
94105
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</body>
</html>
Thanks for your payment
Hi {{name}}, we received your payment of {{total}}. Here's your receipt.
Receipt number: {{receipt_number}}
Date paid: {{paid_at}}
Payment method: {{payment_method}}
{{item_description}}: {{item_amount}}
Subtotal: {{subtotal}}
Tax: {{tax}}
Total paid: {{total}}
View invoice: {{receipt_url}}
Questions about this charge? Reply to this email and we'll help.
--
Acme
Acme Inc. · 100 Market Street · San Francisco, CA 94105
# Create the template in your account once
npx -y --package=groundmail-cli -- groundmail templates push templates/receipt/receipt.html \
--alias receipt \
--name "Receipt" \
--subject "Your Acme receipt #{{receipt_number}}" \
--text templates/receipt/receipt.txt \
--publish
// Then send it by alias from your app
import { Groundmail } from 'groundmail'
const groundmail = new Groundmail(process.env.GROUNDMAIL_API_KEY!)
await groundmail.emails.send({
from: 'Acme <noreply@yourdomain.com>',
to: 'ada@example.com',
template: {
id: 'receipt',
variables: {
name: 'Ada',
receipt_number: '2041-7730',
paid_at: 'October 16, 2026',
payment_method: 'Visa ending in 4242',
item_description: 'Pro plan (monthly)',
item_amount: '$40.00',
subtotal: '$50.00',
tax: '$4.13',
total: '$54.13',
receipt_url: 'https://example.com/billing/invoices/2041-7730',
},
},
})receipt.tsx, 99 lines, shown in full (scroll inside the panel). Typed props. Copy it with theme.ts and components/ from the repo. View on GitHub
Send it with Groundmail
Create the template in your account once, then send it by alias from your app.
npx -y --package=groundmail-cli -- groundmail templates push templates/receipt/receipt.html \
--alias receipt \
--name "Receipt" \
--subject "Your Acme receipt #{{receipt_number}}" \
--text templates/receipt/receipt.txt \
--publishimport { Groundmail } from 'groundmail'
const groundmail = new Groundmail(process.env.GROUNDMAIL_API_KEY!)
await groundmail.emails.send({
from: 'Acme <noreply@yourdomain.com>',
to: 'ada@example.com',
template: {
id: 'receipt',
variables: {
name: 'Ada',
receipt_number: '2041-7730',
paid_at: 'October 16, 2026',
payment_method: 'Visa ending in 4242',
item_description: 'Pro plan (monthly)',
item_amount: '$40.00',
subtotal: '$50.00',
tax: '$4.13',
total: '$54.13',
receipt_url: 'https://example.com/billing/invoices/2041-7730',
},
},
})The CLI command and the Node.js send from the template's README, which also has Python, PHP and cURL. View on GitHub
Subject lines
Receipts get searched for months later, by product name and receipt number. Make both easy to find.
- Your Acme receipt #{{receipt_number}}DefaultProduct, document type and number: everything a search needs.
- Your Acme receipt for {{total}}GoodShows the amount before opening, which is what most people want to check.
- Acme payment received: {{total}}GoodConfirms the payment went through, brand first.
- Thanks!AvoidNothing to search for, and nothing to tell a receipt from a newsletter.
Receipts are billing records, so send them like it
People search for receipts months later, and teams forward them to accounting. They need to arrive every time and stay easy to find.
Google, Yahoo and Microsoft sender rules →- Send on a transactional stream, apart from marketing, so an unsubscribe never stops a receipt.
- Send from your own domain with SPF, DKIM and DMARC set up.
- Keep the from name and subject pattern the same every time, so search and inbox rules find them.
- Groundmail sends this template by alias from your own domain, on our own mail servers. Send it with Groundmail
More billing templates
All email templates →- Payment failed email templateDunning email for a declined card: what failed, when it retries, and one fix button.
- Subscription renewal email templateAn upcoming-renewal reminder with the amount, date and card, sent before the charge.
- Usage alert email templateWarns before a plan limit is hit, with a progress bar and the reset date.
Questions
Is it free to use?
Yes. The templates are MIT-licensed: use them in commercial products and change them however you like.
Do I need Groundmail to use it?
No. The HTML and text files use plain placeholders that work with most providers' template engines and with Handlebars or Mustache. Sending through Groundmail just saves a step.
Does it work in dark mode and Outlook?
Yes. The full HTML file carries dark-mode styles for Apple Mail and Outlook.com, and the extra markup desktop Outlook needs to draw the button.
How do I change the colors and logo?
Edit theme.ts once (name, logo, address and colors for light and dark) and rebuild. Every template in the set picks it up.
Can the HTML version show more than one line item?
The ready-to-send HTML, the MJML and the hosted version have one line item ({{item_description}} and {{item_amount}}). For several, render the React Email component in your app, which takes an items array, or repeat the row in the MJML.