How to build a custom module to create tables

Hi everyone,

I’m exploring whether it’s relatively straightforward to create a custom module for HubSpot marketing emails that would allow me to insert a basic table structure.

What I’m looking for is fairly simple:

  • A two-column table
  • Ability to insert multiple rows (ideally dynamically or via repeater fields)
  • A header row at the top
  • Compatible with HubSpot drag-and-drop email builder
  • Mobile-responsive (or at least reasonably adaptable for email clients)

Hey @JMackenzie8 and welcome, we are so glad you are here :orange_heart:
Great question, thanks for asking the HubSpot Community!
Let’s consult our Top Experts and see if they have any tips for you: Hi @Anton, @SteveHTM and @MichaelMa do you have suggestions to help @JMackenzie8, please?
In the meantime, here is a documentation that might come in handy: “Coding Custom Modules”.
Thanks so much and have a lovely weekend!
Bérangère

Hi @JMackenzie8,

The repeater field-based table module isn’t straightforward to develop. There is quite a lot of conditional logic to implement.

As a workaround, it’s much easier to use an email signature generator (which is HTML table-based) to build your table, and you can paste the HTML code into your email template.

Hey @JMackenzie8,

I’d recommend to start with something like this:

<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
 <tbody>
 <tr>
 <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
 <table cellpadding="0" cellspacing="0" width="100%" border="0" style="color:#000000;line-height:22px;table-layout:auto;width:100%;border:none;">
 <tr style="padding:10px">
 <th style="background-color:#eeeeee;font-weight:bold">Left Table Head</th>
 <th style="background-color:#eeeeee;font-weight:bold">Right Table Head</th>
 </tr>
{% for single_row in module.table_rows %}
 <tr style="padding:10px">
 <td style="padding:10px;">{{ single_row.left_column_text }}</td>
 <td style="padding:10px;">{{ single_row.right_column_text }}</td>
 </tr>
{% endfor %}
 </table>
 </td>
 </tr>
 </tbody>

Another proven practise is to build the layout itself in the email builder, copying the required source code piece of the source code while previewing it, pasting it into a custom module and enhance it with your HubL code. The important part here is to grab all sort of mso-tags (microsoft office) - otherwise the email won’t look good in Outlook.

Such basic layout

will give you this source code

<div class="hse-column-container hse-border-bottom hse-border hse-border-top hse-border-bottom-m hse-border-m hse-border-top-m" style="min-width:280px; max-width:600px; margin:0 auto; border-left:1px solid #cccccc; border-right:1px solid #cccccc; box-sizing:border-box; border-bottom:1px solid #cccccc; border-top:1px solid #cccccc; background-color:#ffffff" bgcolor="#ffffff">

 <!--[if (mso)|(IE)]>
 <table align="center" style="width:600px;border-bottom:1px solid #cccccc;border-left:1px solid #cccccc;border-right:1px solid #cccccc;border-top:1px solid #cccccc" cellpadding="0" cellspacing="0" role="presentation" class="hse-border-bottom hse-border hse-border-top hse-border-bottom-m hse-border-m hse-border-top-m" width="600" bgcolor="#ffffff">
 <tr style="background-color:#ffffff;">
 <![endif]-->

 <!--[if (mso)|(IE)]>
 <td valign="top" style="width:299px;">
<![endif]-->
<!--[if gte mso 9]>
 <table role="presentation" width="299" cellpadding="0" cellspacing="0" style="width:299px">
<![endif]-->
<div id="column_1770967726516_0" class="hse-column hse-size-6">
 <table role="presentation" cellpadding="0" cellspacing="0" width="100%" class="" style=""><tbody><tr><td class="hs_padded" style="font-family:Arial, sans-serif; font-size:15px; color:#23496d; word-break:break-word; padding:10px 20px"><div id="hs_cos_wrapper_module_17709677265051" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module"><div id="hs_cos_wrapper_module_17709677265051_" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_rich_text" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="rich_text"><h2 style="margin:0; line-height:175%; font-size:22px">Something Powerful</h2>
<h3 style="font-size:17px; margin:0; line-height:175%">Tell The Reader More</h3>
<p style="line-height:175%">The headline and subheader tells us what you're <a href="#" style="color:#00a4bd" data-hs-link-id="0" target="_blank">offering</a>, and the form header closes the deal. Over here you can explain why your offer is so great it's worth filling out a form for.</p>
<p style="line-height:175%">Remember:</p>
<ul style="line-height:175%">
<li>Bullets are great</li>
<li>For spelling out <a href="#" style="color:#00a4bd" data-hs-link-id="1" target="_blank">benefits</a> and</li>
<li>Turning visitors into leads.</li>
</ul></div></div></td></tr></tbody></table>
</div>
<!--[if gte mso 9]></table><![endif]-->
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]>
 <td valign="top" style="width:299px;">
<![endif]-->
<!--[if gte mso 9]>
 <table role="presentation" width="299" cellpadding="0" cellspacing="0" style="width:299px">
<![endif]-->
<div id="column-0-0" class="hse-column hse-size-6">
 <table role="presentation" cellpadding="0" cellspacing="0" width="100%" class="" style=""><tbody><tr><td class="hs_padded" style="font-family:Arial, sans-serif; font-size:15px; color:#23496d; word-break:break-word; padding:10px 20px"><div id="hs_cos_wrapper_module_17684640712761" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module"><div id="hs_cos_wrapper_module_17684640712761_" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_rich_text" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="rich_text"><h2 style="margin:0; line-height:175%; font-size:22px">Something Powerful</h2>
<h3 style="font-size:17px; margin:0; line-height:175%">Tell The Reader More</h3>
<p style="line-height:175%">The headline and subheader tells us what you're <a href="#" style="color:#00a4bd" data-hs-link-id="2" target="_blank">offering</a>, and the form header closes the deal. Over here you can explain why your offer is so great it's worth filling out a form for.</p>
<p style="line-height:175%">Remember:</p>
<ul style="line-height:175%">
<li>Bullets are great</li>
<li>For spelling out <a href="#" style="color:#00a4bd" data-hs-link-id="3" target="_blank">benefits</a> and</li>
<li>Turning visitors into leads.</li>
</ul></div></div></td></tr></tbody></table>
 <table role="presentation" cellpadding="0" cellspacing="0" width="100%" class="" style=""><tbody><tr><td class="hs_padded" style="font-family:Arial, sans-serif; font-size:15px; color:#23496d; word-break:break-word; padding:10px 20px"><div id="hs_cos_wrapper_module-1-0-0" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module"><table role="presentation" class="hse-footer hse-secondary" width="100%" cellpadding="0" cellspacing="0" style="font-family:Arial, sans-serif; font-size:12px; line-height:135%; color:#23496d; margin-bottom:0; padding:0">
 <tbody>
 <tr>
 <td align="center" valign="top" style="font-family:Arial, sans-serif; font-size:15px; color:#23496d; word-break:break-word; text-align:center; margin-bottom:0; line-height:135%; padding:10px 20px">
 <p style="font-family:Arial, sans-serif;font-size:12px;font-weight:normal;text-decoration:none;font-style:normal;color:#23496d">
 test, test, test, test
 </p>
 <p>
 <a data-unsubscribe="true" href="lorem" style="font-family:Helvetica,Arial,sans-serif; font-size:12px; color:#00a4bd; font-weight:normal; text-decoration:underline; font-style:normal" data-hs-link-id="0" data-hs-link-id-v2="ipsum" target="_blank">Unsubscribe</a>
 <a data-unsubscribe="true" href="lorem" style="font-family:Helvetica,Arial,sans-serif; font-size:12px; color:#00a4bd; font-weight:normal; text-decoration:underline; font-style:normal" data-hs-link-id="0" data-hs-link-id-v2="ipsum" target="_blank">Manage preferences</a>
 </p>
 </td>
 </tr>
 </tbody>
</table></div></td></tr></tbody></table>
</div>
<!--[if gte mso 9]></table><![endif]-->
<!--[if (mso)|(IE)]></td><![endif]-->

 <!--[if (mso)|(IE)]></tr></table><![endif]-->

 </div>

With some HubL it may become something like this

<div class="hse-column-container hse-border-bottom hse-border hse-border-top hse-border-bottom-m hse-border-m hse-border-top-m" style="min-width:280px; max-width:600px; margin:0 auto; border-left:1px solid #cccccc; border-right:1px solid #cccccc; box-sizing:border-box; border-bottom:1px solid #cccccc; border-top:1px solid #cccccc; background-color:#ffffff" bgcolor="#ffffff">

 <!--[if (mso)|(IE)]>
 <table align="center" style="width:600px;border-bottom:1px solid #cccccc;border-left:1px solid #cccccc;border-right:1px solid #cccccc;border-top:1px solid #cccccc" cellpadding="0" cellspacing="0" role="presentation" class="hse-border-bottom hse-border hse-border-top hse-border-bottom-m hse-border-m hse-border-top-m" width="600" bgcolor="#ffffff">
{% for single_row in module.rows %}
 <tr style="background-color:#ffffff;">
 <![endif]-->

 <!--[if (mso)|(IE)]>
 <td valign="top" style="width:299px;">
<![endif]-->
<!--[if gte mso 9]>
 <table role="presentation" width="299" cellpadding="0" cellspacing="0" style="width:299px">
<![endif]-->
<div id="column_1770967726516_0" class="hse-column hse-size-6">
 <table role="presentation" cellpadding="0" cellspacing="0" width="100%" class="" style=""><tbody><tr><td class="hs_padded" style="font-family:Arial, sans-serif; font-size:15px; color:#23496d; word-break:break-word; padding:10px 20px"><div id="hs_cos_wrapper_module_17709677265051" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module">
{{ row.left_column_content }}</div></td></tr></tbody></table>
</div>
<!--[if gte mso 9]></table><![endif]-->
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]>
 <td valign="top" style="width:299px;">
<![endif]-->
<!--[if gte mso 9]>
 <table role="presentation" width="299" cellpadding="0" cellspacing="0" style="width:299px">
<![endif]-->
<div id="column-0-0" class="hse-column hse-size-6">
 <table role="presentation" cellpadding="0" cellspacing="0" width="100%" class="" style=""><tbody><tr><td class="hs_padded" style="font-family:Arial, sans-serif; font-size:15px; color:#23496d; word-break:break-word; padding:10px 20px"><div id="hs_cos_wrapper_module_17684640712761" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module">
{{ row.right_column_content }}</div></td></tr></tbody></table>
 </div></td></tr></tbody></table>
</div>
<!--[if gte mso 9]></table><![endif]-->
<!--[if (mso)|(IE)]></td><![endif]-->

 <!--[if (mso)|(IE)]></tr>
{% endfor %}</table><![endif]-->

 </div>

(it’s quite a messy code but it’s just to outline the procedure)

hope this helps

Best,

Anton