Create Dynamic Tables with Conditional Logic in Dochly

Updated June 2026 9 min read Conditional Logic
The table element in a Dochly template pulls its rows directly from a Salesforce related list — one row per related record, arranged in a fixed set of columns you configure once. It's a straightforward way to list line items, related contacts, or any child records on a document without typing them in manually. This guide explains exactly how the table behaves, what it does and doesn't support, and how to build one correctly.

What the table element does

A table in a Dochly template is bound to a Salesforce related list (for example, Opportunity Line Items on an Opportunity, or Contacts on an Account). When a document is generated, Dochly repeats the table's data row once for every record in that related list, filling in the merge fields you've mapped to each column.

The table is flat: every record in the related list becomes a row, and every configured column appears on every row. There is no per-row condition, no per-cell logic, and no way to conditionally hide a column — the table simply reflects whatever is in the related list, laid out in the columns you set up.

What it does

Lists every record from the connected related list as a row, using the column layout and merge fields you configured when you built the table. Reordering, adding, or removing columns is done once, in the template editor, for the whole table.

What it doesn't do

It can't show or hide individual rows based on a field value, hide a column when it's not relevant, switch a cell's content based on other data in the row, display a custom message when the related list is empty, or calculate a conditional totals row.


Building a table from a related list

Every table in a Dochly template is connected to a related list on the record being generated — for example, Opportunity Line Items on an Opportunity, or Contacts on an Account. You choose the related list once when you build the table, then map each column to a field on those related records.

When the document generates, Dochly reads every record currently in that related list and renders one row per record, in the column layout you defined. There's no row-by-row decision-making — if a record is in the related list, it appears as a row.

Example — every record in the related list becomes a row
Product Name Qty Unit Price Total
Enterprise Licence 10 £500 £5,000
Premium Support 1 £1,200 £1,200
Legacy Add-on (Discontinued) 0 £0 £0
Implementation Services 3 £800 £2,400
Internal Tracking Code 1 £0 £0

Notice that the discontinued line and the internal tracking code still appear in the table above — the table itself has no way to exclude them. If a related list contains records you don't want on the document, they need to be removed, reassigned, or excluded from that related list in Salesforce before the document is generated — Dochly's table will always render whatever it finds.


Step-by-step: adding a related list table

This example adds a line item table to a quote template, listing every Opportunity Product on the Opportunity being generated.

1

Insert a table into the template

Open the template in the editor and insert a table element at the point where the line items should appear.

2

Connect the table to a related list

In the table's properties panel, choose the related list to pull rows from — for example, Opportunity Products on the Opportunity. This tells Dochly which child object to read records from when the document generates.

3

Map columns to fields

Add a column for each piece of data you want to show — Product Name, Quantity, Unit Price, Total — and map each one to a field on the related object. This column layout is fixed once you save it: it applies the same way to every row and every generated document.

4

Save and preview with a real Opportunity

Save the template, then preview using an Opportunity that has several products attached. Confirm one row appears per product, with the correct value in each column. Whatever records exist in the related list at generation time is exactly what appears in the table — there's no filtering step to check.

Column fields come from the related list record, not the parent record. In an Opportunity Products table, the fields available for columns are on the OpportunityLineItem object. In a Contacts table on an Account, the fields come from the Contact object. If you need a parent field displayed in the table, add a formula field on the child object that copies the parent value, and map a column to that formula field.


Columns are fixed, not conditional

The set of columns in a table is defined once, when you build the table, and it does not change based on the data in any particular record. Every column you add appears in every generated document that uses that template — whether or not a given row has meaningful data for that column.

There is no option to hide a column when it isn't relevant to a specific deal, customer, or record type. If a column has nothing to show for a row, the cell simply renders blank (or shows the field's default value) rather than the column disappearing.

If you don't want a column on some documents

Since the column set can't change per record, the usual approach is to maintain a separate template with a different column layout for the cases where a column genuinely shouldn't appear — for example, one quote template for negotiated pricing (with a Discount column) and one for standard pricing (without it) — and choose the right template when generating the document.

Not currently supported

A single table that shows or hides a column automatically based on a field value (e.g. "only show Discount when a discount applies") is not something the table element supports today.


Cell content is a direct field mapping

Each cell in a table row displays the value of the single field it's mapped to for that row — nothing more. There is no if/else logic inside a table cell, so a cell can't switch between two different pieces of content (for example, showing a price for one line type and the word "Included" for another) based on other data in the row.

Formatters that transform a single value — such as displaying a number as currency, or a date in a particular format — are supported on a cell's mapped field. What isn't supported is branching logic that changes which field, or which wording, appears based on a condition.

If your related list mixes row types that genuinely need different content per column (for example, products that should show a price and services that should show "Included"), the table itself can't make that distinction per row. Consider separate fields on the child object that already contain the display-ready value (so the mapping is still a direct one field → one cell relationship), or a separate table/template for each row type.


When a related list has no records

If the related list connected to a table has no records at all, the table still renders its header row, with no data rows beneath it. There is no built-in "no records" message, no variable that reports the row count, and no way to conditionally hide the whole table when it would otherwise be empty.

This is a known limitation, not a configurable option: if a template includes a table and the record being generated has no related records for that table, the generated document will show a header with no rows beneath it. Preview your templates against a record with an empty related list before relying on them, so you know what that document looks like and can decide whether it's acceptable for your use case.


Totals and summary rows

The table element doesn't calculate or conditionally display totals, subtotals, discount lines, or tax lines — it only lists rows from the related list in fixed columns. There's no aggregate function inside the table and no way to add a summary row that appears or changes based on a condition.

If a document needs a total, subtotal, or tax figure, calculate it in Salesforce — for example with a rollup summary field or a formula field on the parent record — and place that value as a regular merge field in the document, outside the table structure. Whether that merge field (or the paragraph containing it) appears at all can be controlled with a section-level show/hide condition or if/else logic — see Show or hide sections and Use if/else conditions — but that logic applies to a block of content next to the table, not to a row inside it.


Pattern library

Common, supported ways to use the table element. None of these rely on row, column, or cell conditions — they work because the underlying related list already contains the right records and fields.

Line items

List all Opportunity Products on a quote

Connect the table to the Opportunity Products related list, with columns for Product Name, Quantity, Unit Price, and Total. Every product line on the Opportunity appears as a row. If you don't want a particular line on the quote, remove it from the Opportunity before generating the document.

Related records

List related Contacts on an Account document

Connect the table to the Contacts related list on the Account, with columns for Name, Title, Email, and Phone. Every Contact linked to the Account appears as a row — useful for account summaries or onboarding packs.

Two templates

Use separate templates when the column set genuinely differs

Since a single table can't change its columns based on data, maintain two template versions — for example, a standard quote template and a negotiated-pricing quote template with an extra Discount column — and pick the appropriate one when generating the document.

Totals outside the table

Show a subtotal, tax, and grand total beneath the table

Add a rollup summary or formula field on the parent record (Opportunity, Invoice, etc.) for each total you need, then place those as merge fields in a paragraph below the table — not inside it. Use a section-level condition if a particular total line (like tax) should only appear for some records.


Frequently asked questions

No. The table always lists every record currently in the related list it's connected to — there's no row-level condition in the table itself. If certain records shouldn't appear on the document, they need to be removed from the related list, reassigned, or excluded in Salesforce (for example, via a filtered list view used elsewhere in your process) before the document is generated.
No. The column set is fixed when you build the table and applies to every document generated from that template. If you need different columns for different scenarios, build separate templates with different column layouts and choose the right one when generating the document.
No. A cell simply displays the value of the field it's mapped to. There's no if/else branching inside a table cell. If you need display-ready text that varies by row, that text needs to already exist in a field on the related record — the table can display it, but can't compute it conditionally.
No. The table doesn't calculate totals or support a conditional summary row. Calculate any totals in Salesforce (rollup summary or formula field on the parent record) and place them as merge fields outside the table. Whether that surrounding content appears at all can be controlled with a section-level show/hide or if/else condition, covered in the other guides in this series.

You now know exactly what a Dochly table can and can't do: it lists every record from a connected related list in a fixed set of columns, with no per-row, per-column, or per-cell logic. Next in this series: Conditional formatting in Dochly — what's actually supported today for adapting a document's appearance and content.

Dochly
Get Early Access Contact Us to Get Started with Dochly

Not on AppExchange yet — contact us and we'll set you up