Reusable Templates

How to create PDF invoices with HTML templates

Learn how to create PDF invoices using HTML templates and MarkupGo's powerful API. Generate professional invoices with dynamic data and reusable templates.

Introduction

Invoicing is a crucial part of any business, and creating professional invoices can be time-consuming. MarkupGo simplifies this process by allowing you to generate PDF invoices using HTML templates. This feature enables you to create custom invoice designs, populate them with dynamic data, and automate the generation of invoices for your business.

Why Use HTML Templates for Invoices?

Using HTML templates for invoices offers several advantages over traditional methods:

  • Customization: Design invoices that match your brand identity and include specific details.
  • Dynamic Data: Populate invoices with dynamic data such as customer information, order details, and payment status.
  • Automation: Generate invoices automatically based on predefined templates and data sources.
  • Flexibility: Easily update and modify invoice templates without the need for specialized software.

Creating an Invoice Template

We will use a simple HTML template using Tailwind CSS to create a professional invoice design. The template includes placeholders for dynamic data such as the invoice number, customer details, and itemized charges.

In this example, we wil use https://preline.co example invoice template. You can check the original template here. This template is a simple and compact invoice design that can be customized to suit your needs and best for our tutorial.

Invoice Template HTML
<!-- Invoice -->
<div class="max-w-[85rem] px-4 sm:px-6 lg:px-8 mx-auto my-4 sm:my-10">
  <!-- Grid -->
  <div class="mb-5 pb-5 flex justify-between items-center border-b border-gray-200 dark:border-neutral-700">
    <div>
      <h2 class="text-2xl font-semibold text-gray-800 dark:text-neutral-200">Invoice</h2>
    </div>
    <!-- Col -->

    <div class="inline-flex gap-x-2">
      <a class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none focus:outline-none focus:bg-gray-50 dark:bg-transparent dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800 dark:focus:bg-neutral-800" href="#">
      <svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
        Invoice PDF
      </a>
      <a class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none" href="#">
        <svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect width="12" height="8" x="6" y="14"/></svg>
        Print
      </a>
    </div>
    <!-- Col -->
  </div>
  <!-- End Grid -->

  <!-- Grid -->
  <div class="grid md:grid-cols-2 gap-3">
    <div>
      <div class="grid space-y-3">
        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Billed to:
          </dt>
          <dd class="text-gray-800 dark:text-neutral-200">
            <a class="inline-flex items-center gap-x-1.5 text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline font-medium dark:text-blue-500" href="#">
              [email protected]
            </a>
          </dd>
        </dl>

        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Billing details:
          </dt>
          <dd class="font-medium text-gray-800 dark:text-neutral-200">
            <span class="block font-semibold">Sara Williams</span>
            <address class="not-italic font-normal">
              280 Suzanne Throughway,<br>
              Breannabury, OR 45801,<br>
              United States<br>
            </address>
          </dd>
        </dl>

        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Shipping details:
          </dt>
          <dd class="font-medium text-gray-800 dark:text-neutral-200">
            <span class="block font-semibold">Sara Williams</span>
            <address class="not-italic font-normal">
              280 Suzanne Throughway,<br>
              Breannabury, OR 45801,<br>
              United States<br>
            </address>
          </dd>
        </dl>
      </div>
    </div>
    <!-- Col -->

    <div>
      <div class="grid space-y-3">
        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Invoice number:
          </dt>
          <dd class="font-medium text-gray-800 dark:text-neutral-200">
            ADUQ2189H1-0038
          </dd>
        </dl>

        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Currency:
          </dt>
          <dd class="font-medium text-gray-800 dark:text-neutral-200">
            USD - US Dollar
          </dd>
        </dl>

        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Due date:
          </dt>
          <dd class="font-medium text-gray-800 dark:text-neutral-200">
            10 Jan 2023
          </dd>
        </dl>

        <dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
          <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
            Billing method:
          </dt>
          <dd class="font-medium text-gray-800 dark:text-neutral-200">
            Send invoice
          </dd>
        </dl>
      </div>
    </div>
    <!-- Col -->
  </div>
  <!-- End Grid -->

  <!-- Table -->
  <div class="mt-6 border border-gray-200 p-4 rounded-lg space-y-4 dark:border-neutral-700">
    <div class="hidden sm:grid sm:grid-cols-5">
      <div class="sm:col-span-2 text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Item</div>
      <div class="text-start text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Qty</div>
      <div class="text-start text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Rate</div>
      <div class="text-end text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Amount</div>
    </div>

    <div class="hidden sm:block border-b border-gray-200 dark:border-neutral-700"></div>

    <div class="grid grid-cols-3 sm:grid-cols-5 gap-2">
      <div class="col-span-full sm:col-span-2">
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Item</h5>
        <p class="font-medium text-gray-800 dark:text-neutral-200">Design UX and UI</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Qty</h5>
        <p class="text-gray-800 dark:text-neutral-200">1</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Rate</h5>
        <p class="text-gray-800 dark:text-neutral-200">5</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Amount</h5>
        <p class="sm:text-end text-gray-800 dark:text-neutral-200">$500</p>
      </div>
    </div>

    <div class="sm:hidden border-b border-gray-200 dark:border-neutral-700"></div>

    <div class="grid grid-cols-3 sm:grid-cols-5 gap-2">
      <div class="col-span-full sm:col-span-2">
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Item</h5>
        <p class="font-medium text-gray-800 dark:text-neutral-200">Web project</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Qty</h5>
        <p class="text-gray-800 dark:text-neutral-200">1</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Rate</h5>
        <p class="text-gray-800 dark:text-neutral-200">24</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Amount</h5>
        <p class="sm:text-end text-gray-800 dark:text-neutral-200">$1250</p>
      </div>
    </div>

    <div class="sm:hidden border-b border-gray-200 dark:border-neutral-700"></div>

    <div class="grid grid-cols-3 sm:grid-cols-5 gap-2">
      <div class="col-span-full sm:col-span-2">
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Item</h5>
        <p class="font-medium text-gray-800 dark:text-neutral-200">SEO</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Qty</h5>
        <p class="text-gray-800 dark:text-neutral-200">1</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Rate</h5>
        <p class="text-gray-800 dark:text-neutral-200">6</p>
      </div>
      <div>
        <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Amount</h5>
        <p class="sm:text-end text-gray-800 dark:text-neutral-200">$2000</p>
      </div>
    </div>
  </div>
  <!-- End Table -->

  <!-- Flex -->
  <div class="mt-8 flex sm:justify-end">
    <div class="w-full max-w-2xl sm:text-end space-y-2">
      <!-- Grid -->
      <div class="grid grid-cols-2 sm:grid-cols-1 gap-3 sm:gap-2">
        <dl class="grid sm:grid-cols-5 gap-x-3 text-sm">
          <dt class="col-span-3 text-gray-500 dark:text-neutral-500">Subotal:</dt>
          <dd class="col-span-2 font-medium text-gray-800 dark:text-neutral-200">$2750.00</dd>
        </dl>

        <dl class="grid sm:grid-cols-5 gap-x-3 text-sm">
          <dt class="col-span-3 text-gray-500 dark:text-neutral-500">Total:</dt>
          <dd class="col-span-2 font-medium text-gray-800 dark:text-neutral-200">$2750.00</dd>
        </dl>

        <dl class="grid sm:grid-cols-5 gap-x-3 text-sm">
          <dt class="col-span-3 text-gray-500 dark:text-neutral-500">Tax:</dt>
          <dd class="col-span-2 font-medium text-gray-800 dark:text-neutral-200">$39.00</dd>
        </dl>

        <dl class="grid sm:grid-cols-5 gap-x-3 text-sm">
          <dt class="col-span-3 text-gray-500 dark:text-neutral-500">Amount paid:</dt>
          <dd class="col-span-2 font-medium text-gray-800 dark:text-neutral-200">$2789.00</dd>
        </dl>

        <dl class="grid sm:grid-cols-5 gap-x-3 text-sm">
          <dt class="col-span-3 text-gray-500 dark:text-neutral-500">Due balance:</dt>
          <dd class="col-span-2 font-medium text-gray-800 dark:text-neutral-200">$0.00</dd>
        </dl>
      </div>
      <!-- End Grid -->
    </div>
  </div>
  <!-- End Flex -->
</div>
<!-- End Invoice -->

Please note that this is a plain HTML template with Tailwind CSS classes. We don't have any dynamic data in the template yet. We will add dynamic data in the next step.

Let's make it dynamic

To make the invoice template dynamic, we will replace the static content with placeholders that will be populated with real data. Before updating the template, let's define the dynamic data we want to include in the invoice:

Invoice Template Context
{
  "logo": "https://assets.markupgo.com/brand/logo-violet.svg",
  "billed_to": "[email protected]",
  "billing_details": "280 Suzanne Throughway, Breannabury, OR 45801, United States",
  "billing_details_title": "Sara Williams",
  "shipping_details": "280 Suzanne Throughway, Breannabury, OR 45801, United States",
  "shipping_details_title": "Sara Williams",
  "invoice_number": "ADUQ2189H1-0038",
  "currency": "USD - US Dollar",
  "due_date": "10 Jan 2024",
  "billing_method": "Send invoice",
  "items": [
    {
      "item_name": "Design UX and UI",
      "qty": 1,
      "rate": 500,
      "amount": "$500"
    },
    {
      "item_name": "Web project",
      "qty": 1,
      "rate": 1250,
      "amount": "$1250"
    },
    {
      "item_name": "SEO",
      "qty": 1,
      "rate": 2000,
      "amount": "$2000"
    }
  ],
  "subtotal": "$3750",
  "tax": "$675",
  "total": "$4425",
  "amount_paid": "$4425",
  "due_balance": "$0"
}

Now, let's update the invoice template with the dynamic data placeholders, we will use the {{ variable }} syntax to define placeholders that will be replaced with real data when generating the invoice PDF.

MarkupGo internaly uses Handlebars to render the dynamic data in the template. You can use the same syntax to define placeholders in your HTML template.

  <div class="inline-flex gap-x-2">
    <img src="{{ logo }}" alt="MarkupGo Logo" class="w-8 h-8">
  </div>
<dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
  <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
    Billed to:
  </dt>
  <dd class="text-gray-800 dark:text-neutral-200">
    <a class="inline-flex items-center gap-x-1.5 decoration-2 hover:underline focus:outline-none focus:underline font-medium">
      {{billed_to}}
    </a>
  </dd>
</dl>
<dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
  <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
    Billing details:
  </dt>
  <dd class="font-medium text-gray-800 dark:text-neutral-200">
    <span class="block font-semibold">{{billing_details_title}}</span>
    <address class="not-italic font-normal max-w-[200px]">
      {{billing_details}}
    </address>
  </dd>
</dl>
<dl class="flex flex-col sm:flex-row gap-x-3 text-sm">
  <dt class="min-w-36 max-w-[200px] text-gray-500 dark:text-neutral-500">
    Shipping details:
  </dt>
  <dd class="font-medium text-gray-800 dark:text-neutral-200">
    <span class="block font-semibold">{{shipping_details_title}}</span>
    <address class="not-italic font-normal max-w-[200px]">
      {{shipping_details}}
    </address>
  </dd>
</dl>
<dd class="font-medium text-gray-800 dark:text-neutral-200">
  {{invoice_number}}
</dd>
<!-- .... -->
<dd class="font-medium text-gray-800 dark:text-neutral-200">
  {{currency}}
</dd>
<!-- .... -->
<dd class="font-medium text-gray-800 dark:text-neutral-200">
  {{due_date}}
</dd>
<!-- .... -->
<dd class="font-medium text-gray-800 dark:text-neutral-200">
  {{billing_method}}
</dd>

Now let's add the items section

This section will be repeated for each item in the invoice. We will use the {{#each items}} syntax to loop through the items array and render the item details.

With this approach, you can add as many items as you need to the invoice, and the template will automatically generate the itemized list.

 <!-- Table -->
  <div class="mt-6 border border-gray-200 p-4 rounded-lg space-y-4 dark:border-neutral-700">
    <div class="hidden sm:grid sm:grid-cols-5">
      <div class="sm:col-span-2 text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Item</div>
      <div class="text-start text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Qty</div>
      <div class="text-start text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Rate</div>
      <div class="text-end text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Amount</div>
    </div>

    <div class="hidden sm:block border-b border-gray-200 dark:border-neutral-700"></div>

    {{#each items}}
      <div class="grid grid-cols-3 sm:grid-cols-5 gap-2">
        <div class="col-span-full sm:col-span-2">
          <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Item</h5>
          <p class="font-medium text-gray-800 dark:text-neutral-200">{{item_name}}</p>
        </div>
        <div>
          <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Qty</h5>
          <p class="text-gray-800 dark:text-neutral-200">{{qty}}</p>
        </div>
        <div>
          <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Rate</h5>
          <p class="text-gray-800 dark:text-neutral-200">{{rate}}</p>
        </div>
        <div>
          <h5 class="sm:hidden text-xs font-medium text-gray-500 uppercase dark:text-neutral-500">Amount</h5>
          <p class="sm:text-end text-gray-800 dark:text-neutral-200">{{amount}}</p>
        </div>
      </div>

      <div class="sm:hidden border-b border-gray-200 dark:border-neutral-700"></div>
    {{/each}}
  </div>
  <!-- End Table -->

And the rest of the sections will be updated in the same way. You can find the complete template with dynamic data placeholders our Editor to test it.

Generating the Invoice PDF

Once you have updated the invoice template with dynamic data placeholders, you can use MarkupGo's API to generate the invoice PDF. Before making the API request, you need to save the template and get the template ID or use sample API request from the template details page.

Here is a example video on how to save the template and get the template ID:

Sample API Request

Now, we are ready to generate the invoice PDF using the template ID and dynamic data. Here is a sample API request using native fetch API in JavaScript:

fetch("https://api.markupgo.com/api/v1/pdf", {
  method: "POST",
  headers: {
    "x-api-key": process.env.MARKUPGO_API_KEY,
    "content-type": "application/json",
  }, 
  body: JSON.stringify({
    source: {
      type: "template",
      data: {
        id: process.env.INVOICE_TEMPLATE_ID,
        context: invoice_1 // Please check the tab below for invoice data
      }
    }
  })
})
.then(res => res.json())
.then(task => console.log(task.url))

That's it! Here are the generated PDF invoices:

Invoice 1

Invoice 2

Invoice 3

Conclusion

In this tutorial, we have learned how to create a custom invoice template using HTML and Tailwind CSS, populate the template with dynamic data, and generate PDF invoices using MarkupGo's API. By using HTML templates for invoices, you can create professional and customizable designs that match your brand identity and automate the generation of invoices for your business. If you have any questions or need further assistance, feel free to reach out to our team for support. Happy invoicing!

Additional Resources