Skip to main content
Ready-to-use templates you can copy into your project or the hosted dashboard. Each template includes the full JSX and sample JSON data.

Invoice

Professional invoice with line items, tax calculation, and payment terms.
import { Document, Page, View, Text, Table, Row, Cell, Fixed } from '@formepdf/react';

export default function Invoice(data) {
  const missing = ['items', 'date', 'dueDate', 'clientName'].filter(f => !data[f]);
  if (missing.length) throw new Error('Missing required fields: ' + missing.map(f => '"' + f + '"').join(', '));
  if (!Array.isArray(data.items) || !data.items.length) throw new Error('"items" must be a non-empty array of { description, quantity, unitPrice }');

  const taxRate = data.taxRate ?? 0;
  const subtotal = data.items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
  const tax = subtotal * taxRate;
  const total = subtotal + tax;

  return (
    <Document title="Invoice" author="Acme Inc">
      <Page size="Letter" margin={48}>
        <Fixed position="footer">
          <View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingTop: 8, borderTopWidth: 1, borderColor: '#e2e8f0' }}>
            <Text style={{ fontSize: 8, color: '#94a3b8' }}>Acme Inc</Text>
            <Text style={{ fontSize: 8, color: '#94a3b8' }}>Page {'{{pageNumber}}'} of {'{{totalPages}}'}</Text>
          </View>
        </Fixed>

        <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 32 }}>
          <View>
            <View style={{ width: 48, height: 48, backgroundColor: '#2563eb', borderRadius: 8, marginBottom: 12, justifyContent: 'center', alignItems: 'center' }}>
              <Text style={{ fontSize: 18, fontWeight: 700, color: '#ffffff', textAlign: 'center', lineHeight: 1.2 }}>A</Text>
            </View>
            <Text style={{ fontSize: 16, fontWeight: 700, color: '#1e293b' }}>Acme Inc</Text>
            <Text style={{ fontSize: 9, color: '#64748b', marginTop: 4 }}>123 Business Ave</Text>
            <Text style={{ fontSize: 9, color: '#64748b' }}>San Francisco, CA 94102</Text>
          </View>
          <View style={{ alignItems: 'flex-end' }}>
            <Text style={{ fontSize: 32, fontWeight: 700, color: '#2563eb' }}>INVOICE</Text>
            <Text style={{ fontSize: 10, color: '#64748b', marginTop: 8 }}>Invoice No: INV-001</Text>
            <Text style={{ fontSize: 10, color: '#64748b', marginTop: 2 }}>Date: {data.date}</Text>
            <Text style={{ fontSize: 10, color: '#64748b', marginTop: 2 }}>Due: {data.dueDate}</Text>
          </View>
        </View>

        <View style={{ marginBottom: 24 }}>
          <Text style={{ fontSize: 9, fontWeight: 700, color: '#2563eb', textTransform: 'uppercase', letterSpacing: 1, marginBottom: 8 }}>Bill To</Text>
          <Text style={{ fontSize: 10, fontWeight: 700, color: '#1e293b' }}>{data.clientName}</Text>
          {data.clientCompany && <Text style={{ fontSize: 9, color: '#64748b', marginTop: 2 }}>{data.clientCompany}</Text>}
        </View>

        <Table columns={[
          { width: { fraction: 0.45 } },
          { width: { fraction: 0.15 } },
          { width: { fraction: 0.2 } },
          { width: { fraction: 0.2 } }
        ]}>
          <Row header style={{ backgroundColor: '#2563eb' }}>
            <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, fontWeight: 700, color: '#ffffff' }}>Description</Text></Cell>
            <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, fontWeight: 700, color: '#ffffff', textAlign: 'center' }}>Qty</Text></Cell>
            <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, fontWeight: 700, color: '#ffffff', textAlign: 'right' }}>Unit Price</Text></Cell>
            <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, fontWeight: 700, color: '#ffffff', textAlign: 'right' }}>Amount</Text></Cell>
          </Row>
          {data.items.map((item, i) => (
            <Row key={i} style={{ backgroundColor: i % 2 === 0 ? '#ffffff' : '#f8fafc' }}>
              <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, color: '#1e293b' }}>{item.description}</Text></Cell>
              <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, color: '#475569', textAlign: 'center' }}>{item.quantity}</Text></Cell>
              <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, color: '#475569', textAlign: 'right' }}>${item.unitPrice.toFixed(2)}</Text></Cell>
              <Cell style={{ padding: 10 }}><Text style={{ fontSize: 9, color: '#1e293b', textAlign: 'right' }}>${(item.quantity * item.unitPrice).toFixed(2)}</Text></Cell>
            </Row>
          ))}
        </Table>

        <View style={{ flexDirection: 'row', justifyContent: 'flex-end', marginTop: 16 }}>
          <View style={{ width: 200 }}>
            <View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 8 }}>
              <Text style={{ fontSize: 9, color: '#64748b' }}>Subtotal</Text>
              <Text style={{ fontSize: 9, color: '#1e293b' }}>${subtotal.toFixed(2)}</Text>
            </View>
            {taxRate > 0 && (
              <View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 8 }}>
                <Text style={{ fontSize: 9, color: '#64748b' }}>Tax ({(taxRate * 100).toFixed(0)}%)</Text>
                <Text style={{ fontSize: 9, color: '#1e293b' }}>${tax.toFixed(2)}</Text>
              </View>
            )}
            <View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 12, backgroundColor: '#2563eb', borderRadius: 4, marginTop: 4 }}>
              <Text style={{ fontSize: 11, fontWeight: 700, color: '#ffffff' }}>Total Due</Text>
              <Text style={{ fontSize: 11, fontWeight: 700, color: '#ffffff' }}>${total.toFixed(2)}</Text>
            </View>
          </View>
        </View>

        <View style={{ marginTop: 32, padding: 16, backgroundColor: '#f8fafc', borderRadius: 4 }}>
          <Text style={{ fontSize: 9, fontWeight: 700, color: '#1e293b', marginBottom: 8 }}>Payment Terms</Text>
          <Text style={{ fontSize: 9, color: '#64748b' }}>Net 30. Please make payment by the due date.</Text>
        </View>
      </Page>
    </Document>
  );
}

Receipt

Simple purchase receipt with itemized totals and tax.
import { Document, Page, View, Text } from '@formepdf/react';

export default function Receipt(data) {
  const missing = ['storeName', 'items', 'date'].filter(f => !data[f]);
  if (missing.length) throw new Error('Missing required fields: ' + missing.map(f => '"' + f + '"').join(', '));

  const taxRate = data.taxRate ?? 0;
  const subtotal = data.items.reduce((sum, item) => sum + item.price * (item.quantity || 1), 0);
  const tax = subtotal * taxRate;
  const total = subtotal + tax;

  return (
    <Document title="Receipt">
      <Page size="Letter" margin={{ top: 72, right: 120, bottom: 72, left: 120 }}>
        <View style={{ alignItems: 'center', marginBottom: 24 }}>
          <Text style={{ fontSize: 20, fontWeight: 700, color: '#1e293b' }}>{data.storeName}</Text>
          <Text style={{ fontSize: 9, color: '#64748b', marginTop: 4 }}>123 Main Street</Text>
          <Text style={{ fontSize: 9, color: '#64748b', marginTop: 2 }}>Portland, OR 97201</Text>
        </View>
        <View style={{ borderTopWidth: 1, borderColor: '#e2e8f0', marginBottom: 16 }} />
        <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16 }}>
          <Text style={{ fontSize: 9, color: '#64748b' }}>Receipt #1042</Text>
          <Text style={{ fontSize: 9, color: '#64748b' }}>{data.date}</Text>
        </View>
        {data.items.map((item, i) => (
          <View key={i} style={{ flexDirection: 'row', justifyContent: 'space-between', paddingTop: 6, paddingBottom: 6 }}>
            <View style={{ flexDirection: 'row', gap: 8, flexGrow: 1 }}>
              <Text style={{ fontSize: 9, color: '#1e293b' }}>{item.name}</Text>
              {(item.quantity || 1) > 1 && <Text style={{ fontSize: 9, color: '#94a3b8' }}>x{item.quantity}</Text>}
            </View>
            <Text style={{ fontSize: 9, color: '#1e293b' }}>${(item.price * (item.quantity || 1)).toFixed(2)}</Text>
          </View>
        ))}
        <View style={{ borderTopWidth: 1, borderColor: '#e2e8f0', marginTop: 12, marginBottom: 12 }} />
        <View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingTop: 4, paddingBottom: 4 }}>
          <Text style={{ fontSize: 9, color: '#64748b' }}>Subtotal</Text>
          <Text style={{ fontSize: 9, color: '#1e293b' }}>${subtotal.toFixed(2)}</Text>
        </View>
        {taxRate > 0 && (
          <View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingTop: 4, paddingBottom: 4 }}>
            <Text style={{ fontSize: 9, color: '#64748b' }}>Tax ({(taxRate * 100).toFixed(1)}%)</Text>
            <Text style={{ fontSize: 9, color: '#1e293b' }}>${tax.toFixed(2)}</Text>
          </View>
        )}
        <View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingTop: 12, paddingBottom: 12, borderTopWidth: 2, borderColor: '#1e293b', marginTop: 4 }}>
          <Text style={{ fontSize: 12, fontWeight: 700, color: '#1e293b' }}>Total</Text>
          <Text style={{ fontSize: 12, fontWeight: 700, color: '#1e293b' }}>${total.toFixed(2)}</Text>
        </View>
        <View style={{ alignItems: 'center', marginTop: 32 }}>
          <Text style={{ fontSize: 10, color: '#64748b' }}>Thank you for your purchase!</Text>
        </View>
      </Page>
    </Document>
  );
}

Shipping Label

4x6 shipping label with sender/receiver addresses and tracking barcode placeholder.
import { Document, Page, View, Text } from '@formepdf/react';

export default function ShippingLabel(data) {
  const missing = ['fromName', 'fromAddress', 'fromCity', 'toName', 'toAddress', 'toCity', 'tracking'].filter(f => !data[f]);
  if (missing.length) throw new Error('Missing required fields: ' + missing.map(f => '"' + f + '"').join(', '));

  return (
    <Document title="Shipping Label">
      <Page size={{ width: 288, height: 432 }} margin={16}>
        <View style={{ marginBottom: 12, padding: 8 }}>
          <Text style={{ fontSize: 7, fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1, marginBottom: 4 }}>From</Text>
          <Text style={{ fontSize: 8, color: '#334155' }}>{data.fromName}</Text>
          <Text style={{ fontSize: 8, color: '#334155' }}>{data.fromAddress}</Text>
          <Text style={{ fontSize: 8, color: '#334155' }}>{data.fromCity}</Text>
        </View>
        <View style={{ borderTopWidth: 2, borderColor: '#0f172a', marginBottom: 12 }} />
        <View style={{ padding: 12, marginBottom: 12 }}>
          <Text style={{ fontSize: 7, fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1, marginBottom: 8 }}>To</Text>
          <Text style={{ fontSize: 10, fontWeight: 700, color: '#0f172a' }}>{data.toName}</Text>
          <Text style={{ fontSize: 10, color: '#0f172a', marginTop: 4 }}>{data.toAddress}</Text>
          <Text style={{ fontSize: 10, fontWeight: 700, color: '#0f172a', marginTop: 2 }}>{data.toCity}</Text>
        </View>
        <View style={{ marginBottom: 8, padding: 8 }}>
          <Text style={{ fontSize: 8, fontWeight: 700, color: '#0f172a', letterSpacing: 2, textAlign: 'center' }}>{data.tracking}</Text>
        </View>
        <View style={{ borderTopWidth: 1, borderColor: '#cbd5e1', marginBottom: 8 }} />
        <View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 8 }}>
          <View style={{ flex: 1 }}>
            <Text style={{ fontSize: 7, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1 }}>Weight</Text>
            <Text style={{ fontSize: 10, fontWeight: 700, color: '#0f172a', marginTop: 2 }}>{data.weight || '—'}</Text>
          </View>
          <View style={{ flex: 1, alignItems: 'center' }}>
            <Text style={{ fontSize: 7, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1 }}>Dimensions</Text>
            <Text style={{ fontSize: 10, fontWeight: 700, color: '#0f172a', marginTop: 2 }}>{data.dimensions || '—'}</Text>
          </View>
          <View style={{ flex: 1, alignItems: 'flex-end' }}>
            <Text style={{ fontSize: 7, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1 }}>Service</Text>
            <Text style={{ fontSize: 10, fontWeight: 700, color: '#0f172a', marginTop: 2 }}>{data.service || '—'}</Text>
          </View>
        </View>
      </Page>
    </Document>
  );
}

Event Ticket

Horizontal event ticket with seat information and tear-off stub.
import { Document, Page, View, Text } from '@formepdf/react';

export default function EventTicket(data) {
  const missing = ['eventName', 'venue', 'date', 'time'].filter(f => !data[f]);
  if (missing.length) throw new Error('Missing required fields: ' + missing.map(f => '"' + f + '"').join(', '));

  return (
    <Document title="Event Ticket">
      <Page size={{ width: 612, height: 288 }} margin={24}>
        <View style={{ flexDirection: 'row' }}>
          <View style={{ flex: 1, paddingRight: 20 }}>
            {data.presenter && (
              <Text style={{ fontSize: 8, fontWeight: 700, color: '#2563eb', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 4 }}>
                {data.presenter}
              </Text>
            )}
            <Text style={{ fontSize: 16, fontWeight: 700, color: '#0f172a', lineHeight: 1.2, marginBottom: 3 }}>
              {data.eventName}
            </Text>
            <Text style={{ fontSize: 9, color: '#475569', marginBottom: 4 }}>{data.venue}</Text>
            <Text style={{ fontSize: 10, fontWeight: 700, color: '#1e293b', marginBottom: 14 }}>
              {data.date} · {data.time}
            </Text>
            <View style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0, marginBottom: 10 }}>
              <View style={{ backgroundColor: '#1e293b', padding: 5 }}>
                <Text style={{ fontSize: 7, fontWeight: 700, color: '#ffffff', textTransform: 'uppercase', letterSpacing: 0.5 }}>Section</Text>
              </View>
              <View style={{ backgroundColor: '#1e293b', padding: 5 }}>
                <Text style={{ fontSize: 7, fontWeight: 700, color: '#ffffff', textTransform: 'uppercase', letterSpacing: 0.5 }}>Row</Text>
              </View>
              <View style={{ backgroundColor: '#1e293b', padding: 5 }}>
                <Text style={{ fontSize: 7, fontWeight: 700, color: '#ffffff', textTransform: 'uppercase', letterSpacing: 0.5 }}>Seat</Text>
              </View>
              <View style={{ backgroundColor: '#1e293b', padding: 5 }}>
                <Text style={{ fontSize: 7, fontWeight: 700, color: '#ffffff', textTransform: 'uppercase', letterSpacing: 0.5 }}>Gate</Text>
              </View>
              <View style={{ backgroundColor: '#f8fafc', padding: 5, borderBottomWidth: 1, borderColor: '#e2e8f0' }}>
                <Text style={{ fontSize: 11, fontWeight: 700, color: '#0f172a' }}>{data.section || '—'}</Text>
              </View>
              <View style={{ backgroundColor: '#f8fafc', padding: 5, borderBottomWidth: 1, borderColor: '#e2e8f0' }}>
                <Text style={{ fontSize: 11, fontWeight: 700, color: '#0f172a' }}>{data.row || '—'}</Text>
              </View>
              <View style={{ backgroundColor: '#f8fafc', padding: 5, borderBottomWidth: 1, borderColor: '#e2e8f0' }}>
                <Text style={{ fontSize: 11, fontWeight: 700, color: '#0f172a' }}>{data.seat || '—'}</Text>
              </View>
              <View style={{ backgroundColor: '#f8fafc', padding: 5, borderBottomWidth: 1, borderColor: '#e2e8f0' }}>
                <Text style={{ fontSize: 11, fontWeight: 700, color: '#0f172a' }}>{data.gate || '—'}</Text>
              </View>
            </View>
            <Text style={{ fontSize: 8, color: '#64748b' }}>General Admission · Doors open 6:00 PM</Text>
          </View>
          <View style={{ width: 1, backgroundColor: '#e2e8f0' }} />
          <View style={{ width: 130, alignItems: 'center', justifyContent: 'center', paddingLeft: 16 }}>
            <Text style={{ fontSize: 8, fontWeight: 700, color: '#0f172a', marginTop: 10 }}>
              {data.ticketNumber || '—'}
            </Text>
            <View style={{ marginTop: 8, paddingVertical: 3, paddingHorizontal: 6, backgroundColor: '#f1f5f9', borderRadius: 3 }}>
              <Text style={{ fontSize: 7, fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: 0.5 }}>
                Scan for entry
              </Text>
            </View>
          </View>
        </View>
      </Page>
    </Document>
  );
}

Catalog

Product catalog with categories, price badges, and sale indicators.
import { Document, Page, View, Text, Fixed } from '@formepdf/react';

function Badge({ label }) {
  const bg = label === 'SALE' ? '#dc2626' : '#2563eb';
  return (
    <View style={{ backgroundColor: bg, borderRadius: 3, textAlign: 'center', paddingVertical: 2, paddingHorizontal: 6 }}>
      <Text style={{ fontSize: 7, fontWeight: 700, color: '#ffffff', letterSpacing: 0.5 }}>{label}</Text>
    </View>
  );
}

function ProductCard({ product }) {
  const hasSale = product.originalPrice && product.originalPrice > product.price;
  return (
    <View style={{ flexBasis: '48%', padding: 12, borderWidth: 1, borderColor: '#e2e8f0', borderRadius: 6, marginBottom: 10 }} wrap={false}>
      {product.badge && (
        <View style={{ position: 'absolute', top: -18, right: -18 }}>
          <Badge label={product.badge} />
        </View>
      )}
      <Text style={{ fontSize: 10, fontWeight: 700, color: '#1e293b', marginBottom: 4 }}>{product.name}</Text>
      <View style={{ marginBottom: 4 }}>
        {hasSale ? (
          <Text style={{ fontSize: 10 }}>
            <Text style={{ fontWeight: 700, color: '#1e293b' }}>${product.price.toFixed(2)}</Text>
            <Text style={{ color: '#94a3b8', textDecoration: 'line-through', fontSize: 9 }}> ${product.originalPrice.toFixed(2)}</Text>
          </Text>
        ) : (
          <Text style={{ fontSize: 10, fontWeight: 700, color: '#1e293b' }}>${product.price.toFixed(2)}</Text>
        )}
      </View>
      <Text style={{ fontSize: 8, color: '#64748b', lineHeight: 1.4 }}>{product.description}</Text>
    </View>
  );
}

export default function Catalog(data) {
  if (!data.companyName) throw new Error('Missing required field: "companyName"');
  if (!Array.isArray(data.categories) || !data.categories.length) throw new Error('"categories" must be a non-empty array');

  return (
    <Document title="Product Catalog">
      <Page size="Letter" margin={48}>
        <Fixed position="footer">
          <View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingTop: 8, borderTopWidth: 1, borderColor: '#e2e8f0' }}>
            <Text style={{ fontSize: 8, color: '#94a3b8' }}>Product Catalog</Text>
            <Text style={{ fontSize: 8, color: '#94a3b8' }}>Page {'{{pageNumber}}'} of {'{{totalPages}}'}</Text>
          </View>
        </Fixed>
        <View style={{ marginBottom: 20 }}>
          <Text style={{ fontSize: 22, fontWeight: 700, color: '#1e293b' }}>{data.companyName}</Text>
          <Text style={{ fontSize: 10, color: '#64748b', fontStyle: 'italic', marginTop: 2 }}>Product Catalog 2024</Text>
        </View>
        <View style={{ borderTopWidth: 1, borderColor: '#e2e8f0', marginBottom: 20 }} />
        {data.categories.map((category, ci) => (
          <View key={ci} style={{ marginBottom: 20 }}>
            <View style={{ marginBottom: 10, paddingBottom: 6, borderBottomWidth: 2, borderColor: '#1e293b' }}>
              <Text style={{ fontSize: 14, fontWeight: 700, color: '#1e293b', textTransform: 'uppercase', letterSpacing: 1 }}>{category.name}</Text>
            </View>
            <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>
              {category.products.map((product, pi) => (
                <ProductCard key={pi} product={product} />
              ))}
            </View>
          </View>
        ))}
      </Page>
    </Document>
  );
}

Typography

Two-column text layout with a pull quote, demonstrating text formatting.
import { Document, Page, View, Text } from '@formepdf/react';

export default function Typography(data) {
  if (!data.title) throw new Error('Missing required field: "title"');

  return (
    <Document title={data.title} lang="en">
      <Page size="Letter" margin={{ top: 48, right: 48, bottom: 54, left: 48 }}>
        <View style={{ marginBottom: 20 }}>
          <Text style={{ fontSize: 26, fontWeight: 700, color: '#1a1a1a', letterSpacing: -0.5 }}>
            {data.title}
          </Text>
          {data.subtitle && (
            <Text style={{ fontSize: 10, color: '#6b7280', marginTop: 4, letterSpacing: 0.3 }}>
              {data.subtitle}
            </Text>
          )}
          <View style={{ height: 0.75, backgroundColor: '#d1d5db', marginTop: 14 }} />
        </View>
        <View style={{ flexDirection: 'row', gap: 24 }}>
          <View style={{ flex: 1 }}>
            <Text style={{ fontSize: 9, lineHeight: 1.5, color: '#1a1a1a', textAlign: 'justify', hyphens: 'auto' }}>
              Typography is the art and technique of arranging type to make written language legible, readable, and appealing when displayed.
            </Text>
            <Text style={{ fontSize: 9, lineHeight: 1.5, color: '#1a1a1a', textAlign: 'justify', hyphens: 'auto', marginTop: 9 }}>
              The term typography is also applied to the style, arrangement, and appearance of the letters, numbers, and symbols created by the process.
            </Text>
          </View>
          <View style={{ flex: 1 }}>
            <Text style={{ fontSize: 9, lineHeight: 1.5, color: '#1a1a1a', textAlign: 'justify', hyphens: 'auto' }}>
              Good typography establishes a visual hierarchy, provides a graphic balance to the page, and sets the overall tone of the product.
            </Text>
            <View style={{ borderLeftWidth: 2.5, borderLeftColor: '#9ca3af', paddingLeft: 14, paddingVertical: 10, marginVertical: 14 }}>
              <Text style={{ fontSize: 11, fontStyle: 'italic', lineHeight: 1.55, color: '#374151' }}>
                "Typography is two-dimensional architecture, based on experience and imagination."
              </Text>
              <Text style={{ fontSize: 8, color: '#6b7280', marginTop: 5, letterSpacing: 0.2 }}>
                — Hermann Zapf
              </Text>
            </View>
          </View>
        </View>
      </Page>
    </Document>
  );
}