Getting Started

Your First Report

Create a complete invoice report with headers, detail rows, expressions, and formatting.

Overview

This walkthrough creates a realistic invoice report that demonstrates every core concept: multiple band types, text components with expressions, data binding, and built-in formatting functions.

The complete schema

const invoiceSchema = {
  version: '1.0',
  type: 'report',
  locale: 'en-US',
  page: {
    size: 'A4',
    orientation: 'portrait',
    margins: { top: 30, right: 30, bottom: 30, left: 30 },
  },
  dataSource: 'invoice',
  bands: [
    {
      type: 'header',
      height: 120,
      components: [
        {
          type: 'text',
          position: { x: 0, y: 0, width: 250, height: 30 },
          content: '{{company.name}}',
          fontSize: 22,
          fontWeight: 'bold',
          color: '#1e293b',
        },
        {
          type: 'text',
          position: { x: 0, y: 35, width: 250, height: 20 },
          content: '{{company.address}}',
          fontSize: 11,
          color: '#64748b',
        },
        {
          type: 'text',
          position: { x: 350, y: 0, width: 180, height: 30 },
          content: 'INVOICE',
          fontSize: 22,
          fontWeight: 'bold',
          textAlign: 'right',
          color: '#6366f1',
        },
        {
          type: 'text',
          position: { x: 350, y: 35, width: 180, height: 20 },
          content: '#{{invoiceNumber}}',
          fontSize: 11,
          textAlign: 'right',
          color: '#64748b',
        },
        {
          type: 'text',
          position: { x: 350, y: 55, width: 180, height: 20 },
          content: '{{formatDate(issueDate, "MMMM d, yyyy")}}',
          fontSize: 11,
          textAlign: 'right',
          color: '#64748b',
        },
        {
          type: 'text',
          position: { x: 0, y: 80, width: 250, height: 20 },
          content: 'Bill to: {{customer.name}}',
          fontSize: 12,
          fontWeight: 'bold',
        },
        {
          type: 'text',
          position: { x: 0, y: 98, width: 250, height: 20 },
          content: '{{customer.address}}',
          fontSize: 11,
          color: '#64748b',
        },
      ],
    },
    {
      type: 'detail',
      height: 28,
      dataBinding: 'items',
      components: [
        {
          type: 'text',
          position: { x: 0, y: 4, width: 250, height: 20 },
          content: '{{item.description}}',
          fontSize: 12,
        },
        {
          type: 'text',
          position: { x: 260, y: 4, width: 60, height: 20 },
          content: '{{item.quantity}}',
          fontSize: 12,
          textAlign: 'center',
        },
        {
          type: 'text',
          position: { x: 330, y: 4, width: 90, height: 20 },
          content: '{{formatCurrency(item.unitPrice, "USD")}}',
          fontSize: 12,
          textAlign: 'right',
        },
        {
          type: 'text',
          position: { x: 430, y: 4, width: 100, height: 20 },
          content: '{{formatCurrency(item.total, "USD")}}',
          fontSize: 12,
          fontWeight: 'bold',
          textAlign: 'right',
        },
      ],
    },
    {
      type: 'footer',
      height: 80,
      components: [
        {
          type: 'text',
          position: { x: 330, y: 10, width: 90, height: 20 },
          content: 'Subtotal:',
          fontSize: 12,
          textAlign: 'right',
          color: '#64748b',
        },
        {
          type: 'text',
          position: { x: 430, y: 10, width: 100, height: 20 },
          content: '{{formatCurrency(sum(items, "total"), "USD")}}',
          fontSize: 12,
          textAlign: 'right',
        },
        {
          type: 'text',
          position: { x: 330, y: 35, width: 90, height: 20 },
          content: 'Total:',
          fontSize: 14,
          fontWeight: 'bold',
          textAlign: 'right',
        },
        {
          type: 'text',
          position: { x: 430, y: 35, width: 100, height: 20 },
          content: '{{formatCurrency(sum(items, "total"), "USD")}}',
          fontSize: 14,
          fontWeight: 'bold',
          textAlign: 'right',
          color: '#6366f1',
        },
        {
          type: 'text',
          position: { x: 0, y: 55, width: 300, height: 20 },
          content: 'Thank you for your business!',
          fontSize: 11,
          color: '#64748b',
        },
      ],
    },
  ],
}

The data

const invoiceData = {
  company: {
    name: 'Acme Corp',
    address: '123 Main Street, San Francisco, CA 94105',
  },
  customer: {
    name: 'Jane Smith',
    address: '456 Oak Avenue, Portland, OR 97201',
  },
  invoiceNumber: 'INV-2026-001',
  issueDate: '2026-04-12',
  items: [
    { description: 'Web Design Services', quantity: 1, unitPrice: 2500, total: 2500 },
    { description: 'Frontend Development', quantity: 40, unitPrice: 150, total: 6000 },
    { description: 'API Integration', quantity: 16, unitPrice: 175, total: 2800 },
    { description: 'QA Testing', quantity: 8, unitPrice: 125, total: 1000 },
  ],
}

Render and output

import { renderReport, renderToHtml } from '@nextreport/engine'

const result = renderReport(invoiceSchema, invoiceData)
const html = renderToHtml(result)

// Write to file or serve via HTTP
import { writeFileSync } from 'fs'
writeFileSync('invoice.html', html)

Understanding the schema

Header band

The header band renders once at the top. It contains seven text components:

  • Company info on the left (name and address)
  • Invoice label and number on the right, with accent color
  • Date formatted using formatDate with a human-readable pattern
  • Customer billing info below

Detail band

The detail band has dataBinding: 'items', which tells the engine to iterate over the items array. Each element becomes available as item (the engine derives the singular form automatically by removing the trailing “s”).

Each row shows the description, quantity, unit price, and line total. Currency values use formatCurrency for consistent formatting.

The footer uses sum(items, "total") to calculate the subtotal across all line items. This built-in function takes an array and a property name, returning the sum of all values.

Next steps