•
7 min read
make-bill
Next.js TypeScript Tailwind CSS Supabase Puppeteer

Design invoices visually. Generate PDFs via API.

Stop wrestling with wkhtmltopdf configs and Puppeteer headaches. make-bill is an open-source, visual invoice builder with an API-first approach to PDF generation.


🎯 What is make-bill?

Have you ever spent hours trying to generate pixel-perfect invoices programmatically? Fought with HTML-to-PDF libraries that never quite render things correctly? make-bill was built to solve exactly that.

It’s a modern web application that combines:

  • A visual drag-and-drop editor for designing invoice templates
  • Pixel-perfect PDF generation via a simple REST API
  • Dynamic data binding so your templates come alive with real data

Think of it as Canva meets Stripe Invoicesβ€”design once, generate forever.


✨ Features at a Glance

FeatureDescription
🎨 Visual EditorDrag-and-drop interface with absolute positioning on an A4 canvas
πŸ“ Multiple Page SizesA4, A3, A5, Letter, Legalβ€”with portrait and landscape orientations
πŸ”— Data BindingBind text fields to data paths like invoice.customer.name
πŸ“Š TablesDynamic tables that expand with your data arrays
πŸ–ΌοΈ ImagesUpload logos, signatures, or any image assets
πŸ“„ PDF GenerationServer-side rendering with Puppeteer for consistent output
πŸ’Ύ Template ManagementSave, load, and version your templates
πŸ” AuthenticationSupabase-powered user authentication
⚑ REST APIGenerate PDFs programmatically with a single POST request

πŸš€ Getting Started

Prerequisites

  • Node.js v18 or higher
  • Bun (recommended) or npm/yarn/pnpm
  • A Supabase project (for authentication and template storage)

Installation

  1. Clone the repository

    git clone https://github.com/pratikstemkar/make-bill.git
    cd make-bill
  2. Install dependencies

    bun install
    # or
    npm install
  3. Set up environment variables

    Copy the example environment file and fill in your credentials:

    cp .env.example .env.local

    Your .env.local should contain:

    NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
    NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
  4. Start the development server

    bun dev
    # or
    npm run dev
  5. Open your browser

    Navigate to http://localhost:3000 and start building!


🎨 Using the Visual Editor

The heart of make-bill is its visual editor. Here’s how to create your first template:

1. Open the Editor

Click β€œOpen Editor” from the landing page or navigate directly to /editor.

2. Add Elements

The left panel contains your toolbox. Drag elements onto the canvas:

  • Text β€” Static text or dynamic data bindings
  • Image β€” Upload logos, product images, or signatures
  • Line β€” Horizontal separators for visual structure
  • Table β€” Dynamic tables that expand with your data

3. Configure Page Settings

At the top of the left panel, configure your document:

  • Page Size β€” A4, A3, A5, Letter, or Legal
  • Orientation β€” Portrait or Landscape
  • Margins β€” Control the printable area

4. Bind Data to Elements

Select a text element and use the Inspector panel (right side) to set a binding path:

invoice.customer.name
invoice.number
invoice.date
invoice.total

When you generate the PDF via API, these placeholders are replaced with actual data.

5. Preview Your Template

Click β€œPreview” to see how your invoice will look with sample data.

6. Save Your Template

Hit β€œSave Template” to persist your design. Templates are versioned, so you can iterate without breaking existing integrations.


πŸ“‘ API Usage

Once you’ve designed and saved a template, generate PDFs with a simple HTTP request.

Generate a PDF

curl -X POST https://your-domain.com/api/generate-pdf \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "invoice_v1",
    "data": {
      "invoice": {
        "number": "INV-2024-001",
        "date": "2024-01-15",
        "customer": {
          "name": "Acme Corporation",
          "address": "123 Business Street"
        },
        "items": [
          { "name": "Consulting Services", "qty": 10, "price": 150 },
          { "name": "Development Work", "qty": 20, "price": 100 }
        ],
        "total": 3500
      }
    }
  }'

Response

The API returns the PDF as a binary stream with Content-Type: application/pdf.


πŸ“ Element Types

Text Element

Static or dynamic text content.

{
  type: "text",
  content: "Invoice",           // Static content
  binding: "invoice.number",    // OR dynamic binding
  fontSize: 16,
  fontWeight: "bold" | "normal",
  align: "left" | "center" | "right"
}

Image Element

Embed images with optional aspect ratio locking.

{
  type: "image",
  src: "https://...",          // Image URL or base64
  maintainAspectRatio: true
}

Line Element

Horizontal separator lines.

{
  type: "line",
  thickness: 2                 // Line thickness in pixels
}

Table Element

Dynamic tables bound to an array in your data.

{
  type: "table",
  columns: ["Item", "Qty", "Price"],
  binding: "invoice.items"     // Path to array data
}

πŸ—οΈ Project Structure

make-bill/
β”œβ”€β”€ app/                    # Next.js App Router pages
β”‚   β”œβ”€β”€ api/               # API routes (PDF generation, etc.)
β”‚   β”œβ”€β”€ editor/            # Visual template editor
β”‚   β”œβ”€β”€ login/             # Authentication pages
β”‚   β”œβ”€β”€ templates/         # Template management
β”‚   └── page.tsx           # Landing page
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ editor/            # Editor components (Canvas, Inspector, etc.)
β”‚   β”œβ”€β”€ preview/           # PDF preview components
β”‚   └── ui/                # Reusable UI components (shadcn/ui)
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ api/               # API client utilities
β”‚   β”œβ”€β”€ htmlGenerator.ts   # Server-side HTML generation for PDFs
β”‚   β”œβ”€β”€ types.ts           # TypeScript type definitions
β”‚   └── supabase/          # Supabase client configuration
β”œβ”€β”€ hooks/                 # Custom React hooks
└── store/                 # Zustand state management

πŸ› οΈ Tech Stack

CategoryTechnology
FrameworkNext.js 16 (App Router)
LanguageTypeScript
StylingTailwind CSS v4
UI Componentsshadcn/ui + Radix UI
State ManagementZustand
AuthenticationSupabase Auth
DatabaseSupabase (PostgreSQL)
PDF GenerationPuppeteer Core + @sparticuz/chromium
IconsLucide React
NotificationsSonner

πŸ”‘ Environment Variables

VariableDescription
NEXT_PUBLIC_SUPABASE_URLYour Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEYYour Supabase anonymous/public key

πŸ“¦ Available Scripts

# Development server
bun dev

# Production build
bun run build

# Start production server
bun start

# Lint the codebase
bun run lint

🚒 Deployment

make-bill is optimized for Vercel deployment. Simply connect your GitHub repository and Vercel will handle the rest.

Note: PDF generation uses @sparticuz/chromium which is specifically optimized for serverless environments like Vercel Functions.

Other Platforms

For other platforms, ensure you have:

  • Node.js 18+ runtime
  • Sufficient memory for Puppeteer (512MB+ recommended)
  • Proper environment variable configuration

🀝 Contributing

Contributions are welcome! Whether it’s bug fixes, new features, or documentation improvementsβ€”we’d love to have your help.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“œ License

This project is open source. Feel free to use, modify, and distribute as needed.


πŸ’‘ Why make-bill?

Because generating invoices should be simple.

No more:

  • ❌ Wrestling with wkhtmltopdf configurations
  • ❌ Debugging CSS rendering differences in headless Chrome
  • ❌ Manually crafting HTML templates with complex positioning
  • ❌ Dealing with font embedding issues

Instead:

  • βœ… Design visually, see exactly what you’ll get
  • βœ… One API call to generate a PDF
  • βœ… Consistent, pixel-perfect output every time
  • βœ… Templates that non-developers can update

Built with ❀️ for developers who hate fighting with PDF libraries.