Create Dynamic Tables with Conditional Logic in Dochly
What dynamic tables are
A static table in a Dochly template shows every row and column every time — regardless of the data. A dynamic table uses conditional logic to decide which rows appear, which columns are shown, and what content fills each cell — based on the live values from the Salesforce record being generated.
Every product line item appears in a quote, including discontinued items, zero-quantity lines, and internal service codes never intended for customers. Every column appears even when it contains no relevant data for this deal type.
Only active product lines appear. Zero-quantity lines are hidden. The "Discount" column only appears when a discount has been applied. Service lines appear in a separate section from product lines. The table always looks intentional — never over-populated.
Conditional row filtering
Row filtering is the most common dynamic table pattern. A condition is applied to the repeating data row in the template — rows where the condition is false are excluded from the generated document entirely.
In a Dochly template, the repeating row in a related list table has a row-level condition builder. Set the condition once on the repeating row — it applies to every instance as Dochly iterates through the related list records.
| Product Name | Qty | Unit Price | Total | Status in template |
|---|---|---|---|---|
| Enterprise Licence | 10 | £500 | £5,000 | Shown Status = Active |
| Premium Support | 1 | £1,200 | £1,200 | Shown Status = Active |
| Legacy Add-on (Discontinued) | 0 | £0 | £0 | Status = Inactive |
| Implementation Services | 3 | £800 | £2,400 | Shown Status = Active |
| Internal Tracking Code | 1 | £0 | £0 | Status = Internal |
// Row-level condition on the repeating data row
// Applied in the row's Conditional Logic panel in the editor
Show row when: Product_Status__c == "Active"
// Or equivalently in syntax form on the row:
{{if Product_Status__c == "Active"}}
[row content — merge fields for Name, Qty, Price, Total]
{{/if}}
Step-by-step: adding a row filter condition
This example filters a quote line item table to show only rows where Quantity is greater than zero — hiding zero-quantity lines that are sometimes present on Salesforce Opportunity Products.
Click the repeating data row in the template editor
Open the template. In the table, the repeating row — the row that contains merge fields like {{Product_Name__c}}, {{Quantity}} — is the data row. Click anywhere on this row to select it. The row highlight confirms selection.
Do not select the header row — the condition applies only to the data rows that repeat per related record.
Open Conditional Logic in the row properties panel
In the right-hand properties panel with the row selected, click Conditional Logic → Add Condition.
Set the condition
- Field:
Quantity— this refers to the field on the related list record (Opportunity Product), not the parent Opportunity - Operator:
greater than - Value:
0
Set the Action to Show when condition is true.
Save and preview with a real Opportunity
Save the template. Preview using an Opportunity that has at least one product with Quantity = 0 and at least one with Quantity > 0. Confirm the zero-quantity row is absent from the generated table and the non-zero rows appear correctly.
Row conditions evaluate fields on the related list record — not the parent record. In an Opportunity Products table, the condition fields come from the OpportunityLineItem object. In a Contacts table on an Account, the condition fields come from the Contact object. The parent object's fields are not directly available in row conditions — use a formula field on the child object if you need parent data in a row filter.
Conditional columns
Hiding an entire column from a table requires applying a show/hide condition to the column header cell and the same column's data cell in the repeating row — both must be hidden together for the column to disappear cleanly.
Hide the "Discount" column when no lines have a discount applied — Discount_Pct__c > 0 across any row. Hide the "Tax" column when the customer is tax-exempt. Hide the "Partner Reference" column when Account.Type is not "Partner". Column hiding keeps tables clean for customers who don't need that data.
Apply the same condition to the column header cell AND the data cell in the repeating row. If you only hide the header, the data column still appears with no heading. If you only hide the data cell, the header column remains with empty rows. Both must use identical conditions to hide cleanly.
// Conditional column — apply to BOTH header cell and data cell
// Header cell condition (visual builder):
Show when: Opportunity.Has_Discount__c == true
// Data cell in repeating row (same condition):
Show when: Opportunity.Has_Discount__c == true
// Has_Discount__c is a formula checkbox on Opportunity:
// = IF(MAX(OpportunityLineItems.Discount) > 0, TRUE, FALSE)
The cleanest approach for conditional columns uses a parent-level formula field as the condition — e.g. a Has_Discount__c checkbox formula on Opportunity that returns TRUE when any line has a discount. This lets you use a parent field condition on both the header and data cells, keeping the condition identical and easy to maintain.
Conditional cell content
Individual cells within a table row can contain if/else logic — switching their displayed value or label based on row-level field values. This is particularly useful for status indicators, labels, and calculated display values.
// Cell switching — show different text based on line type
// Inside a table cell in the data row:
{{if Line_Type__c == "Product"}}{{Unit_Price__c | currency}}{{else}}Included{{/if}}
// Products show a price. Service/bundle items show "Included".
// No price column needed for "Included" service lines.
// Status cell — show coloured label based on status field
{{if Status__c == "Active"}}✓ Active
{{else if Status__c == "Pending"}}⏳ Pending setup
{{else}}— Not started
{{/if}}
Handling empty tables gracefully
When all rows in a related list are filtered out by row conditions — or when the related list has no records at all — the table renders with only a header row. This looks broken. Dochly provides an empty-state pattern to handle this gracefully.
// Empty table fallback — show a message when no rows pass the filter
// Place this row immediately below the data row in the template:
{{if @rowCount == 0}}
No active line items for this order.
{{/if}}
// @rowCount is a built-in variable that resolves to the number of
// rows rendered after filtering. When 0, the fallback row appears.
For tables that should be completely invisible when there's no data, apply a show/hide condition to the entire table element using the @rowCount == 0 check as a "hide when true" condition. The table header and all rows disappear — leaving no orphaned heading in the document.
For tables where the reader should know data was expected (e.g. line items on an invoice), show a "No items to display" row. This is clearer than a blank table and signals that the empty state is intentional, not a generation error.
Always test your tables with a record that would result in zero rendered rows — for example, an Opportunity where all products have Quantity = 0. Without an empty-state handler, the document generates with a hanging table header that looks like a layout error.
Conditional totals and summary rows
Total rows at the bottom of a table can also be conditional — showing a discount line only when a discount applies, showing a tax line only for taxable orders, or showing a subtotal when there are multiple line types.
// Conditional total rows — each total row is a separate template row
// with its own show/hide condition applied via the visual builder
// Subtotal row — always shown
Subtotal {{Subtotal | currency}}
// Discount row — only when a discount exists
// [Condition on row: Discount_Amount__c > 0]
Discount ({{Discount_Pct__c}}%) -{{Discount_Amount__c | currency}}
// Tax row — only when Tax_Applicable__c is true
// [Condition on row: Tax_Applicable__c == true]
VAT (20%) {{Tax_Amount__c | currency}}
// Grand total — always shown
Total {{Total_Amount__c | currency}}
Each total row is a separate row in the template's totals section, with a show/hide condition applied via the visual builder. The same approach works for any conditional summary row — shipping charges, handling fees, bundle savings, or loyalty credits.
Pattern library
Show only Active products in a quote
Condition on data row: Product2.IsActive == true. Show when true. Removes discontinued or deactivated products from customer-facing quotes automatically — no manual line editing needed before sending.
Separate product and service line items into two tables
Create two tables from the same related list. Table 1 data row condition: Line_Type__c == "Product". Table 2 data row condition: Line_Type__c == "Service". Each table shows only its relevant line type — products with unit price columns, services with a "Included" value column. Use the empty-state handler on each table in case all lines are one type.
Show the Discount column only when discounts apply
Condition on both header and data cells: Opportunity.Has_Discount__c == true (formula checkbox = TRUE when any line has a discount > 0). The Discount column appears in quotes with negotiated pricing and is invisible on standard-price quotes. Keeps standard quotes clean without a blank column.
Show "No items added yet" when a related list is empty
Add a row below the data row in the table with condition: @rowCount == 0. Show when true. Merge the row's cells and fill with "No items have been added to this order." The table always looks intentional — never shows a bare header with no data beneath it.
Show a tax line only for non-tax-exempt customers
Condition on the tax row in the totals section: Account.Tax_Exempt__c == false. Show when true (or equivalently, hide when Tax_Exempt__c == true). The VAT or sales tax line appears only on invoices for taxable customers — tax-exempt customers see Subtotal → Total with no tax line.
Exclude zero-quantity and zero-price rows from invoices
Two conditions on data row with AND logic: Quantity > 0 AND UnitPrice > 0. Show when all true. Removes test lines, placeholder SKUs, and internal tracking codes that sometimes appear on Opportunity Products but should never be visible to customers on invoices.
Frequently asked questions
Your tables now dynamically filter and adapt to every record's data. Next in this series: Apply conditional formatting to documents in Salesforce with Dochly — change font colour, bold, background, and borders based on field values to create visually intelligent documents.
Rated 5 stars · Native Salesforce app · Free to install