PDFPipe

Billing and receivables / A4

GST tax invoice HTML template for India

An Indian tax invoice with GSTIN for both parties, HSN codes per line, and tax split into CGST and SGST or charged as IGST depending on the place of supply.

When this document is the right one

A registered supplier in India issues an invoice to another registered person. The document has prescribed particulars and a prescribed set of copies, and getting the tax split wrong is not a formatting error, it changes which government is paid.

Why each part is there

The fields are visible in the markup. What is not visible is why they are present, which is the part that gets removed first when someone adapts a template for their own use.

  • The place of supply, stated explicitly. It is what decides whether the tax is CGST plus SGST or IGST, and the two are never both shown.
  • GSTIN for supplier and recipient, with the state and its numeric code, because the first two digits of a GSTIN are the state.
  • An HSN or SAC code on every line, at the digit length the supplier's turnover requires.
  • A copy marking. The original is for the recipient, the duplicate for the transporter and the triplicate for the supplier, and only one of them is the original.
  • The amount in words as well as figures, which is conventional to the point of being expected.
  • A signature block, physical or digital, on behalf of the supplier.

The template

A complete file. Doctype, stylesheet, body: paste it into a renderer unchanged and it produces a finished A4 page. Replace the sample data and the styling holds.

html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Tax invoice KA/2026/1187</title>
<style>
  @page { size: A4; margin: 12mm; }
  * { box-sizing: border-box; }
  body { margin: 0; font: 9pt/1.4 Arial, sans-serif; color: #000; }

  .sheet { border: 1px solid #000; }
  .title { text-align: center; padding: 2mm; border-bottom: 1px solid #000;
           font-weight: 700; letter-spacing: 0.06em; }
  .title small { display: block; font-weight: 400; font-size: 7.5pt; letter-spacing: 0; }

  .top { display: grid; grid-template-columns: 1fr 1fr; }
  .top > div { padding: 3mm; }
  .top > div:first-child { border-right: 1px solid #000; }
  .kv { display: grid; grid-template-columns: 34mm 1fr; gap: 0.8mm 2mm; font-size: 8.5pt; }
  .kv span:first-child { color: #333; }
  address { font-style: normal; }

  .addr-row { display: grid; grid-template-columns: 1fr 1fr; border-top: 1px solid #000; }
  .addr-row > div { padding: 3mm; }
  .addr-row > div:first-child { border-right: 1px solid #000; }
  h2 { margin: 0 0 1.5mm; font-size: 8pt; text-transform: uppercase; }

  table { width: 100%; border-collapse: collapse; border-top: 1px solid #000; }
  thead { display: table-header-group; }
  th, td { border: 1px solid #000; padding: 1.6mm; font-size: 8pt; }
  th { background: #eee; text-align: left; }
  tr { break-inside: avoid; }
  .num { text-align: right; font-variant-numeric: tabular-nums; }

  /* CGST and SGST for a supply inside the state, IGST for one across state
     lines. Never both. The place of supply above decides which applies. */
  .tax-split { break-inside: avoid; }

  .foot { display: grid; grid-template-columns: 1fr 70mm; border-top: 1px solid #000;
          break-inside: avoid; }
  .foot > div { padding: 3mm; }
  .foot > div:first-child { border-right: 1px solid #000; }
  .words { font-size: 8.5pt; }
  .sign { text-align: right; padding-top: 16mm; font-size: 8.5pt; }
</style>
</head>
<body>
<div class="sheet">
  <div class="title">TAX INVOICE<small>Original for recipient</small></div>

  <div class="top">
    <div>
      <strong>Anantha Instruments Pvt Ltd</strong>
      <address>44/2 Hosur Road, Bommanahalli<br>Bengaluru 560068, Karnataka</address>
      <div class="kv" style="margin-top:2mm">
        <span>GSTIN</span><span>29AAFCA4471J1ZP</span>
        <span>State</span><span>Karnataka, code 29</span>
      </div>
    </div>
    <div>
      <div class="kv">
        <span>Invoice number</span><span>KA/2026/1187</span>
        <span>Invoice date</span><span>14 May 2026</span>
        <span>Place of supply</span><span>Karnataka (29)</span>
        <span>Reverse charge</span><span>No</span>
        <span>Transport doc</span><span>LR 88213, 12 May 2026</span>
      </div>
    </div>
  </div>

  <div class="addr-row">
    <div>
      <h2>Bill to</h2>
      <address><strong>Vidyut Controls</strong><br>
        12 Industrial Layout, Peenya<br>Bengaluru 560058, Karnataka</address>
      <div class="kv" style="margin-top:2mm"><span>GSTIN</span><span>29AACCV1188K1ZR</span></div>
    </div>
    <div>
      <h2>Ship to</h2>
      <address><strong>Vidyut Controls, Unit 2</strong><br>
        Plot 61, KIADB Phase 3<br>Bengaluru 560058, Karnataka</address>
    </div>
  </div>

  <table>
    <thead>
      <tr>
        <th>#</th><th>Description</th><th>HSN</th>
        <th class="num">Qty</th><th class="num">Rate</th>
        <th class="num">Taxable</th>
        <th class="num">CGST 9%</th><th class="num">SGST 9%</th>
        <th class="num">Total</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td><td>Digital pressure transmitter DPT-200</td><td>9026</td>
        <td class="num">10</td><td class="num">7,400.00</td>
        <td class="num">74,000.00</td>
        <td class="num">6,660.00</td><td class="num">6,660.00</td>
        <td class="num">87,320.00</td>
      </tr>
      <tr>
        <td>2</td><td>Mounting bracket, stainless</td><td>7326</td>
        <td class="num">10</td><td class="num">480.00</td>
        <td class="num">4,800.00</td>
        <td class="num">432.00</td><td class="num">432.00</td>
        <td class="num">5,664.00</td>
      </tr>
      <tr class="tax-split">
        <td colspan="5"><strong>Total</strong></td>
        <td class="num"><strong>78,800.00</strong></td>
        <td class="num"><strong>7,092.00</strong></td>
        <td class="num"><strong>7,092.00</strong></td>
        <td class="num"><strong>92,984.00</strong></td>
      </tr>
    </tbody>
  </table>

  <div class="foot">
    <div class="words">
      <strong>Amount in words</strong><br>
      Rupees Ninety Two Thousand Nine Hundred Eighty Four only.
      <p style="margin:3mm 0 0">Certified that the particulars given above are true
      and correct, and that the amount indicated represents the price actually charged.</p>
    </div>
    <div class="sign">
      For Anantha Instruments Pvt Ltd
      <div style="height:14mm"></div>
      Authorised signatory
    </div>
  </div>
</div>
</body>
</html>

The layout decision worth understanding

The whole document sits inside a single ruled box with internal dividers rather than using whitespace to separate sections. That is not a stylistic choice: this format is read alongside handwritten and pre-printed versions of the same document, and the ruled grid is what makes it recognisable as the same thing.

The mistake people make adapting it

Showing CGST, SGST and IGST columns all at once and leaving the ones that do not apply as zero. It looks thorough and it is wrong: the place of supply determines which apply, and showing all three suggests the system does not know which case it is in.

Rendering it

The page box is declared in the template's own CSS as A4, so the size travels with the markup rather than living in the code that calls the renderer. A4 is also one of the six named formats the API accepts, so it can be passed in the request options instead if that suits your setup better.

bash
curl -X POST https://api.pdfpipe.xyz/v1/pdf \
  -H "Authorization: Bearer $PDFPIPE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<!doctype html>...",
    "options": { "format": "A4" }
  }' \
  --output invoice-gst.pdf

Frequently asked

Can I use this template commercially?

Yes. Copy it, change it, ship it. It is sample markup written to be taken, and the sample data in it is invented, so replace the names and the numbers before anyone sees it.

Why is the page size in the CSS rather than in the API call?

Because a indian gst tax invoice is a A4 document, and that is a property of the document rather than of the code that renders it. Declaring it in the page box means the size cannot be lost between the template and the call, which is the usual way a document ends up on the wrong paper.

Will it look the same in every renderer?

The layout will, because it uses ordinary CSS. The page break behaviour will not: break-inside and break-after are implemented differently by browser engines and by dedicated print engines, and some of the paged media specification is not implemented in browser renders at all. That difference is documented per property in the paged CSS reference.

Other templates

Documents from the same part of a business, and one from each of the other groups.

Paste the template into the playground and get the PDF back. No signup, no key.