LogoLogo
API DocsTemplate GalleryProduct UpdatesContact Us
  • 📬Print & Mail
    • Ready to get started?
      • Pricing details
      • Fast Track Guide
    • Integrations
      • API integrations
        • Action IQ
        • Adobe Marketo Engage
        • Blueshift
        • Braze
        • Customer.io
        • Hubspot
        • Iterable
        • Jotform
        • Klayvio
        • Listrak
        • Make
        • Optimove
        • Salesforce
        • Salesforce Marketing Cloud
        • Segment
        • Simon Data
        • Zapier
      • No-code integrations
        • Agile CRM
        • Freshsales Suite
        • HubSpot
        • Microsoft Dynamics 365
        • Pipedrive
        • Salesforce
        • Salesforce Marketing Cloud
        • Zoho
      • Creative conversion tools
        • Figma plugin
    • Reaching your audience
      • Lob Audience
        • Target Audiences
        • Lookalike Audiences
        • Purchasing Audiences
      • All about addresses
      • Campaign audience guide
      • Additional Lob NCOA functionality
    • Designing mail creatives
      • Artboard layout
      • Creative formatting
        • PDF preflight checklist
        • Exporting PDFs from InDesign
        • Rendering errors
      • Mail piece design specs
        • Postcards
        • Self-Mailers
        • Letters
        • Letter envelopes
        • Letter add-ons
        • Checks
        • Snap packs
        • Booklets
        • Custom mail
      • Maximizing engagement
        • Dynamic personalization
          • Advanced templating (Handlebars)
            • Dynamic table tutorial
        • Adding QR codes
        • Short URLs
        • Informed Delivery
    • Building a mail strategy
      • One-time campaigns or triggered sends?
      • Choosing a delivery strategy
      • Managing mail settings
        • Using metadata
        • Declaring mail use type
      • USPS Promotions Through Lob
        • Tactile, Sensory & Interactive Promotion
        • Integrated Technology Promotion
        • First Class Mail Advertising Promotion
        • Add-Ons
      • Mailing class and postage
        • Certified Mail or Registered Mail
      • International mail
    • Send mail!
      • Launch your first campaign
      • Send mail via Print & Mail API
      • Send campaigns via the Campaigns API
      • USPS Secure Destruction
    • Getting data & results
      • Tracking your mail
      • Mail analytics
      • Measuring attribution
      • Using webhooks
      • Exporting mail data
  • 🏠Address Verification
    • Ready to start AV?
      • US AV product suite
      • International AV suite
      • AV pricing
    • AV best practices
    • AV integrations & libraries
      • AV Elements
      • Shopify App: Address Cleanser
    • AV FAQs
  • 💻Developer Docs
    • API quickstart guide
    • SDKs & libraries
    • Postman & similar tools
    • Error reference
    • Upgrading API version
    • Technical use case guides
      • Mass deletion setup
      • NCOA responses
      • Override cancellation window
      • Visibility of address changes
      • Ingesting tracking events with webhooks
  • 🔑Account Management
    • Signing into Lob
    • API keys
    • Account settings
      • Account-level data logs
    • User settings
    • Billing
      • Lob Credits
      • Lob Payment Methods
      • Sales Tax FAQ
        • Applicable sales tax by state
          • ​Subscriptions and Services
          • Lob Audience
          • Delivery Location for Operational Mail
          • Customer Billing Address for Operational Mail
          • Delivery Location for Marketing Mail
          • Postage Exemption
          • Professional Services
        • Tax exemption guide
  • 📞Resources
    • Getting support
    • Security & privacy
    • Data retention
    • Sustainability
    • Private labeling Lob
    • Direct mail glossary
Powered by GitBook
On this page
  • The tools
  • The template
  • The JSON merge variable data
  • Making the requests

Was this helpful?

Export as PDF
  1. Print & Mail
  2. Designing mail creatives
  3. Maximizing engagement
  4. Dynamic personalization
  5. Advanced templating (Handlebars)

Dynamic table tutorial

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

PreviousAdvanced templating (Handlebars)NextAdding QR codes

Last updated 12 months ago

Was this helpful?

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 .

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

CSS / HTML

        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">
        <table>
            <thead class="columnheaders">
                <th colspan="6" style="font-size: 20">Your Invoice</th>
                <tr>
                    <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>
                </tr>
            </thead>
            <tbody class="columnwrapper">
                {{#each this}}
                    <tr class="wrapper">
                        <td><b>{{dateOfService}}</td>
                        <td><b>{{provider}}</b></td>
                        <td><b>{{service}}</td>
                        <td><b>${{cost}}</b></td>
                    </tr>
                    {{#if superlast}}
                        <th colspan="6" style="font-size: 20">Thank you for choosing Lob for your services {{../../first_name}}!</th>
                    {{/if}}
                {{/each}}
            </tbody>
        </table>   
    </div>
    {{/withGroup}}

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 and see the associated hyperlinks.

📬
Handlebars engine
{{withGroup}}
{{each}}
{{if}}
advanced template creation
request submission