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
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.
Anton
February 13, 2026, 8:06pm
4
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