Help Center Document Generation Generate from Related Lists

Generate Documents from Salesforce Related Lists with Dochly

Updated June 2026 9 min read Document Generation
Related lists are what make Salesforce document generation dynamic at scale. Instead of listing a fixed number of line items, contacts, or cases in your template, a related list table expands automatically to include however many related records exist — one product, ten products, fifty tasks. This guide covers how to pull data from any Salesforce related list into your Dochly templates using the REPEAT block syntax — including sorting, filtering, and handling empty lists gracefully.

What are related lists in documents?

In Salesforce, a related list is a child object linked to a parent record — for example, Opportunity Products linked to an Opportunity, or Contacts linked to an Account. When you generate a document from the parent record, you can include a dynamic table that repeats one row for each child record in the related list.

This is fundamentally different from a merge field. A merge field pulls one value from one field on one record. A related list table pulls multiple fields from multiple records and renders them as a structured table — automatically expanding or contracting based on how many child records exist.

What related list tables do

Generate one table row per related child record. The table expands automatically — if an Opportunity has 3 products, you get 3 rows. If it has 15 products, you get 15 rows. No manual counting or blank rows.

What they don't do

They don't create static tables with a fixed number of rows. If you add a static 5-row table and the Opportunity has 7 products, 2 products won't appear. Always use REPEAT blocks for variable-length data.


Common related lists to include in documents


The REPEAT syntax

Dochly uses a {REPEAT} block to mark which rows in a table should repeat for each related record. Everything between {REPEAT} and {END REPEAT} is generated once per related record.

Basic REPEAT structure
Header Row — column names here (OUTSIDE the repeat block)

{REPEAT RelatedListAPIName}
Data row — one row per related record
{{RelatedObject.Field1}} | {{RelatedObject.Field2}} | {{RelatedObject.Field3}}
{END REPEAT}

Totals row — OUTSIDE the repeat block

Key rule: the header row and totals row always go outside the REPEAT block. Only the repeating data row goes inside. If you put the header inside the repeat block, a new header row appears before every data row — one of the most common related list mistakes.

Correct vs incorrect structure
✓ CORRECT — header outside, data row inside:
Product | Qty | Price ← header OUTSIDE
{REPEAT OpportunityLineItems}
{{OpportunityLineItem.Name}} | {{OpportunityLineItem.Quantity}} | {{OpportunityLineItem.UnitPrice}}
{END REPEAT}

✗ INCORRECT — header inside repeat (repeats for every row):
{REPEAT OpportunityLineItems}
Product | Qty | Price ← header INSIDE — wrong
{{OpportunityLineItem.Name}} | {{OpportunityLineItem.Quantity}} | {{OpportunityLineItem.UnitPrice}}
{END REPEAT}

Step-by-step: adding a related list table to your template

1

Identify the related list API name

The REPEAT block requires the API name of the related list — not its display label. For standard Salesforce related lists, the names are well known. For custom related lists, find the API name in Salesforce Object Manager.

To find a custom related list API name: Setup → Object Manager → [Child Object] → Fields & Relationships → find the lookup field to the parent → the Child Relationship Name is the value to use in your REPEAT block.

REPEAT nameRelated listUsed for
OpportunityLineItemsOpportunity ProductsProposals, quotes, invoices
ContactsAccount ContactsAccount summaries, org docs
CasesAccount CasesSupport summaries, reviews
ActivityHistoriesActivity HistoryMeeting notes, account reviews
ContractLineItemsContract Line ItemsRenewals, amendments
CustomObject__rCustom child objectAny custom relationship
2

Add the table structure in the template editor

In the Dochly template editor, insert a table at the position where the related list data should appear. Set up the columns you need — typically you'll have 3–6 columns depending on the data. Set the header row with column labels.

Example for a line items table with 5 columns:

Description | Quantity | Unit Price | Discount | Total

Apply any styling you want to the header row now — background color, bold text, font size. The header row is outside the REPEAT block so it won't repeat.

3

Add the REPEAT block around the data row

Below the header row, add the data row. Wrap the data row with the REPEAT and END REPEAT tags — place them directly before and after the data row cells, not around the entire table.

Description | Quantity | Unit Price | Discount | Total ← header row, no repeat
{REPEAT OpportunityLineItems}
[data row cells go here — step 4]
{END REPEAT}
4

Add merge fields inside the REPEAT block

Inside the data row, insert merge fields for each column using the related object's field API names. Use the merge field picker in the toolbar and navigate to the related object to find the correct fields.

Description | Quantity | Unit Price | Discount | Total
{REPEAT OpportunityLineItems}
{{OpportunityLineItem.Name}} | {{OpportunityLineItem.Quantity}} | {{OpportunityLineItem.UnitPrice | currency}} | {{OpportunityLineItem.Discount}}% | {{OpportunityLineItem.TotalPrice | currency}}
{END REPEAT}

Always apply | currency formatting to money fields and right-align numeric columns for professional presentation.

5

Add totals and summary rows

Below the END REPEAT tag, add a totals row. This row is outside the repeat block — it appears once at the bottom of the table regardless of how many line items there are. Pull total values from the parent record fields (not the line items):

{END REPEAT}
Subtotal: {{Opportunity.Amount | currency}}
Discount: {{Opportunity.TotalOpportunityQuantity}}
Tax: {{Opportunity.Tax_Amount__c | currency}}
Total Due: {{Opportunity.GrandTotal__c | currency}}
6

Handle empty related lists gracefully

If the related list has zero records, the REPEAT block generates zero rows — the table header appears but the body is empty. For documents where an empty table looks unprofessional, wrap the entire table in a conditional block:

{IF Opportunity.HasLineItems = true}
[Entire table including header, REPEAT block, and totals]
{ELSE}
No products have been added to this opportunity.
{END IF}

This hides the empty table completely and shows a fallback message when no related records exist.

7

Test and preview

Preview the template from a record that has multiple related records in the list — at least 3 to verify the table expands correctly. Test with these scenarios:

  • A record with 1 related record — confirm single row renders correctly
  • A record with 5+ related records — confirm all rows appear, no records are truncated
  • A record with 0 related records — confirm the empty state is handled gracefully

For troubleshooting table issues: Troubleshooting document generation errors


Field reference by related list

These are the most commonly used fields from the most common Salesforce related lists. Use these as a reference when building your repeating table columns.

Opportunity Products (OpportunityLineItems)
Merge fieldLabelNotes
{{OpportunityLineItem.Name}}Product NameName of the product or line item
{{OpportunityLineItem.Quantity}}QuantityNumber of units — format as integer
{{OpportunityLineItem.UnitPrice}}Unit PricePrice per unit — apply | currency
{{OpportunityLineItem.TotalPrice}}Total PriceQty × Unit Price — apply | currency
{{OpportunityLineItem.Discount}}Discount %Discount percentage — append % symbol
{{OpportunityLineItem.Description}}DescriptionProduct description — may be blank
{{OpportunityLineItem.ServiceDate}}DateService/delivery date — apply date format
Account Contacts
Merge fieldLabelNotes
{{Contact.FirstName}}First NameContact first name
{{Contact.LastName}}Last NameContact last name
{{Contact.Title}}TitleJob title — may be blank
{{Contact.Email}}EmailPrimary email address
{{Contact.Phone}}PhoneDirect phone number

Sorting and filtering related records

By default, related records appear in the order they are returned by Salesforce — typically insertion order. You can control the order and optionally filter which records appear using REPEAT block modifiers.

Sort by a field (ascending)
{REPEAT OpportunityLineItems ORDERBY="Name" DIRECTION="ASC"}
{{OpportunityLineItem.Name}} | {{OpportunityLineItem.TotalPrice | currency}}
{END REPEAT}
Sort by price (highest first)
{REPEAT OpportunityLineItems ORDERBY="TotalPrice" DIRECTION="DESC"}
{{OpportunityLineItem.Name}} | {{OpportunityLineItem.TotalPrice | currency}}
{END REPEAT}
Filter to show only specific records
{REPEAT Cases WHERE="Status='Open'"}
{{Case.CaseNumber}} | {{Case.Subject}} | {{Case.CreatedDate | date: "MMM d, yyyy"}}
{END REPEAT}

Sorting and filtering use the Salesforce field API name — not the field display label. Always verify the API name in Object Manager before using it in a WHERE or ORDERBY clause. Invalid field names cause the REPEAT block to fail silently.


Common related list errors

Header row repeats for every data row

The header row is inside the REPEAT block instead of outside it. Move the header row above the {REPEAT} tag — it should appear once regardless of how many records exist.

Table shows no rows despite related records existing

The related list API name in the REPEAT tag is incorrect. Check Setup → Object Manager → [Child Object] → Fields & Relationships → lookup field → Child Relationship Name.

Only one row appears regardless of record count

The REPEAT and END REPEAT tags are missing or misplaced — the data row is not inside the repeat block. Confirm both tags are present and correctly wrapping the data row.

Blank rows appear at the bottom of the table

Extra empty rows were added inside the REPEAT block. Only one data row should exist between {REPEAT} and {END REPEAT}. Delete any extra rows inside the repeat block.


Frequently asked questions

Yes. You can include as many REPEAT blocks as needed in a single template — each one pulling from a different related list. For example, a contract template might include a Products table (OpportunityLineItems), a Contacts table, and a Payment Schedule table (a custom related object), all in the same document.
Nested REPEAT blocks are not supported. Each REPEAT block must be at the same level — you cannot put one REPEAT inside another. If you need to represent nested data, consider flattening it or using a custom formula field on the child object to aggregate the grandchild data.
Dochly can handle large related lists — hundreds of rows in a single table. For very large related lists (1,000+ records), generation time increases and the document may span many pages. Consider adding a WHERE filter to limit the records returned if performance is a concern.
You can access fields from the parent record inside a REPEAT block using standard merge field syntax — for example, {{Opportunity.Name}} works inside an OpportunityLineItems repeat block. You cannot traverse further up the relationship chain within a REPEAT block.

You can now include dynamic related list tables in any Dochly template. Next in this series: Generate a document from any Salesforce record — how generation works across all standard and custom objects beyond Opportunities.

Dochly
Salesforce AppExchange — UTECH HUB Install Dochly on AppExchange

Rated 5 stars · Native Salesforce app · Free to install