Dynamic table tutorial

In-depth tutorial of how to neatly break apart sets of data into paginated segments

For certain use cases, you may need a table of dynamic length. These tables should not have rows that are divided between pages, and you may need a header for each page. This tutorial breaks down how to create them using the Handlebars engine.

The tools

This will allow you to group elements into the subarrays that will make each page of the dynamic table

This allows you to iterate over each "row" in the withGroup subarrays.

In this case, we're using the if statement to evaluate whether or not we're at the absolute final row for the entirety of our table

The template


        body {
            width: 8.5in;
            height: 11in;
            margin: 0
        .page {
            page-break-after: always;
            position: relative;
            width: 8.5in;
            height: 11in;
        table {
            border-spacing: 5px;
            margin-left: .35in;

        td {
            vertical-align: top;
            text-align: left;

        tr {
            font-size: 14;

        tr:nth-child(odd) {
            background-color: #e4e4e4;
    {{#withGroup items 9}}
    <div id="d2" class="page">
            <thead class="columnheaders">
                <th colspan="6" style="font-size: 20">Your Invoice</th>
                    <th style="font-size: 20">Date of Service</th>
                    <th style="font-size: 20">Service</th>
                    <th style="font-size: 20">Provider</th>
                    <th style="font-size: 20">Charge</th>
            <tbody class="columnwrapper">
                {{#each this}}
                    <tr class="wrapper">
                    {{#if superlast}}
                        <th colspan="6" style="font-size: 20">Thank you for choosing Lob for your services {{../../first_name}}!</th>

Within our template, let's place a table and use {{withGroup}}to break apart our rows merge variable parameter into 9 groups per page, or {{withGroup rows 9}}. We have chosen 9 because we have already determined that any more rows than this would spill into the following page. This of course, means that going into this, you'll need an idea of how much space each row will take.

Then, we'll use {{each}} to iterate over each of the 9 rows per page and access nested elements within the row.

Lastly, we'll use {{if superlast}} to evaluate if the row we're iterating over is truly the last in the spreadsheet. In our JSON, the last item of rows will have parameter superlast equal to true, which triggers this portion off. This logic enables the ability to append a footer to the last page in our spreadsheet.

You'll notice that within this section, we include {{../../first_name}}, this is because we're referencing the merge variable first_name two scopes up (one out of the each loop, one out of the withGroup loop).

The JSON merge variable data

When we're ready to merge_variables , we'll need to include our complete array of rows. Note the last item in "rows" has an extra parameter superlast which is set to true

    "merge_variables": {
        "first_name": "Michael",
        "rows": [
                "service": "Eye Exam",
                "provider": "Dr. Mobius",
                "cost": "200",
                "dateOfService": "2/23/23"
                "service": "General Checkup",
                "provider": "Dr. Murphey",
                "cost": "300",
                "dateOfService": "1/23/23"
                "service": "Perscription Evaluation",
                "provider": "Dr. Mario",
                "cost": "200",
                "dateOfService": "1/19/23"
                "service": "Blood Test",
                "provider": "Dr. Morbius",
                "cost": "100",
                "dateOfService": "1/22/23",
                "superlast": true

Making the requests

Once we've implemented the logic for dynamic tables into our own templates, we can post to the /templates endpoint, making sure that the engine parameter is set to handlebars. This will provide back a template reference id. We can then use this in the file parameter for the /letters endpoint. For more information on advanced template creation and request submission see the associated hyperlinks.

Last updated