Create Dynamic Tables with Conditional Logic in Dochly

Updated June 2026 9 min read Conditional Logic
Dynamic tables adapt their content to the data on each Salesforce record — showing only the rows that meet a condition, displaying columns relevant to the deal type, switching cell content based on field values, and handling empty tables gracefully. This guide covers every technique for building tables that filter, adapt, and summarise based on live Salesforce data.

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.

Static table (no logic)

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.

Dynamic table (with logic)

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.

Row filtering — only active product lines shown (Inactive lines removed)
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 Hidden Status = Inactive
Implementation Services 3 £800 £2,400 Shown Status = Active
Internal Tracking Code 1 £0 £0 Hidden 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.

1

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.

2

Open Conditional Logic in the row properties panel

In the right-hand properties panel with the row selected, click Conditional LogicAdd Condition.

3

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.

4

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.

When to hide a column

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.

Important: apply to both header and data cells

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.
Hide the whole table when empty

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.

Show a "no data" message

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

Row filter

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.

Row filter

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.

Column

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.

Empty state

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.

Totals

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.

Row filter

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

Yes. Sorting is configured separately from filtering in the related list table settings. In the table's properties panel, set the sort field and direction (e.g. sort by Product Name ascending, or by Sort_Order__c ascending). Filtering then applies after sorting — so the rows that remain after filtering are already in the correct order. Set sort order on the related list configuration, not on individual rows.
Row conditions evaluate fields on the child/related record, not the parent. For example, in an Opportunity Products table, you can only directly use fields on the OpportunityLineItem — not on the parent Opportunity. To filter rows based on a parent field, create a formula field on the child object that pulls the parent value — e.g. a formula on OpportunityLineItem that copies Opportunity.Account.Type. Then use that formula field in the row condition.
If you're using a Salesforce rollup summary field or a formula on the parent record for the total, it includes all child records — not just those passing the row filter. The visual table filtering in Dochly only affects what rows are displayed; it doesn't change the Salesforce field values. For accurate filtered totals, either use a Dochly table aggregate function (SUM of visible rows) configured in the table settings, or create a separate formula field on the parent that sums only the records you want included.
Yes. Add multiple conditions to the row's Conditional Logic panel and set them to AND or OR. For example: Show row when Quantity > 0 AND Product_Status__c == "Active" — both must be true. Or: Show row when Line_Type__c == "Product" OR Line_Type__c == "Bundle" — either line type is shown. Combine as many conditions as needed, but keep the logic readable — if you need more than 3–4 conditions on a single row, consider using a Salesforce formula field to pre-filter on the object.

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.

Dochly
Salesforce AppExchange — UTECH HUB Install Dochly on AppExchange

Rated 5 stars · Native Salesforce app · Free to install