> For the complete documentation index, see [llms.txt](https://help.sdocs.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.sdocs.com/quick-start/template-building/adding-tables-like-products-or-contacts-to-your-document.md).

# Adding Tables (Like Products or Contacts) to Your Document

### Example output

Your finished document can look like this:

| Opportunity           | Stage                | Close Date | Amount |
| --------------------- | -------------------- | ---------- | -----: |
| Renewal - Northwind   | Proposal/Price Quote | 2026-05-15 |  25000 |
| Expansion - Northwind | Negotiation/Review   | 2026-06-01 |  40000 |
| Services Add-On       | Qualification        | 2026-06-20 |  12000 |

## What You'll Learn

* Build a related list table from a parent record
* Query `Opportunity` records from an `Account` template
* Match table headers to `<column>` tags in the right order
* Test the output before adding filters
* Reuse the same pattern with other object pairs

## What You'll Build

In this tutorial, you will build an account pipeline summary that lists related opportunities in a table.

This walkthrough uses `Account` as the base object and `Opportunity` as the child object.

The finished template will include:

* An account summary header
* A four-column opportunity table
* A related list query tied to the current account
* One safe variation that only shows open opportunities

**Estimated time:** 15 minutes

## Prerequisites

Before you start, make sure you have:

* Access to **S-Docs Templates** in Salesforce
* Permission to create or edit templates
* A template related to `Account`
* A test account with at least two related `Opportunity` records
* Basic familiarity with merge fields such as `{{!Account.Name}}`

If you want a shorter intro first, use [Insert Data Tables](/sdocs/advanced-template-logic/related-lists.md).

{% stepper %}
{% step %}

### Step 1: Open or create the template

Start with an `Account` template so the query can use the current account ID.

1. Go to **S-Docs Templates**.
2. Open an existing template or click **New Template**.
3. Set **Related To Type** to `Account`.
4. Open the template editor.

You now have a base template that can query child records from the current account.
{% endstep %}

{% step %}

### Step 2: Add the account summary header

Add a small header first.

This confirms your base merge fields resolve before you add the related list.

{% code title="account-summary-header.html" %}

```html
<h1>Account opportunity summary</h1>
<p>Account: {{!Account.Name}}</p>
<p>Owner: {{!Account.Owner.Name}}</p>
```

{% endcode %}

What you just did:

* Confirmed the template runs from `Account`
* Added visible fields to test the base record context
* Created a simple header above the opportunity table
  {% endstep %}

{% step %}

### Step 3: Add the table shell

Create the visible table structure first.

Then place the query block inside the table body.

{% code title="opportunity-table-shell.html" %}

```html
<table class="table1">
  <thead>
    <tr>
      <th>Opportunity</th>
      <th>Stage</th>
      <th>Close Date</th>
      <th>Amount</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>
```

{% endcode %}

What you just did:

* Defined the columns the reader will see
* Left space for the related list query
* Created a reusable table layout for child records
  {% endstep %}

{% step %}

### Step 4: Add the related list block

This example uses `Opportunity` as the child object.

It pulls records related to the current `Account`.

{% code title="account-opportunity-related-list.xml" %}

```xml
<table class="table1">
  <thead>
    <tr>
      <th>Opportunity</th>
      <th>Stage</th>
      <th>Close Date</th>
      <th>Amount</th>
    </tr>
  </thead>
  <tbody><!--{{!
<lineitemsSOQL>
  <class>table1</class>
  <soql>
    SELECT Name, StageName, CloseDate, Amount
    FROM Opportunity
    WHERE AccountId = '{{!Account.Id}}'
    ORDER BY CloseDate DESC
  </soql>
  <column>Name</column>
  <column>StageName</column>
  <column>CloseDate</column>
  <column>Amount</column>
</lineitemsSOQL>
}}--></tbody>
</table>
```

{% endcode %}

What this does:

* Queries `Opportunity` records for the current account
* Ties the query to the base record with `WHERE AccountId = '{{!Account.Id}}'`
* Sorts the rows by `CloseDate`
* Prints one field per `<column>` tag

How to read the block:

* `<lineitemsSOQL>` starts the related list query
* `<class>` controls the output style
* `<soql>` defines which child rows to return
* Each `<column>` prints one value in the row
  {% endstep %}

{% step %}

### Step 5: Match the headers and columns

The table only works cleanly when the header order matches the `<column>` order.

In this example:

* `Opportunity` maps to `Name`
* `Stage` maps to `StageName`
* `Close Date` maps to `CloseDate`
* `Amount` maps to `Amount`

Rules to follow:

* Every displayed value needs a matching `<column>` tag
* Every `<column>` value must appear in the `SELECT` clause
* The first header must match the first `<column>`

If the order does not match, the wrong values appear under the wrong headers.
{% endstep %}

{% step %}

### Step 6: Save and test the template

Test the base version before you add any variations.

1. Click **Save**.
2. Open a test account with related opportunities.
3. Generate the document.
4. Compare the document rows with the related records in Salesforce.

Verify these results:

* The header shows the expected account
* The table shows one row per related opportunity
* The rows belong to the current account only
* The values appear under the correct headers

Once this works, you have a reusable starting pattern.
{% endstep %}

{% step %}

### Step 7: Try one simple variation

Now add one safe filter.

This version only returns open opportunities.

{% code title="filtered-account-opportunity-related-list.xml" %}

```xml
<lineitemsSOQL>
  <class>table1</class>
  <soql>
    SELECT Name, StageName, CloseDate, Amount
    FROM Opportunity
    WHERE AccountId = '{{!Account.Id}}'
    AND IsClosed = false
    ORDER BY CloseDate ASC
  </soql>
  <column>Name</column>
  <column>StageName</column>
  <column>CloseDate</column>
  <column>Amount</column>
</lineitemsSOQL>
```

{% endcode %}

What changed:

* Added `AND IsClosed = false`
* Kept the same table structure
* Narrowed the output without changing the base pattern

Change one thing at a time.

Then generate again and confirm the result.
{% endstep %}
{% endstepper %}

## Common Issues and Solutions

<details>

<summary>"My table shows no rows"</summary>

Check these items:

* Confirm the template runs from `Account`
* Confirm the test account has related `Opportunity` records
* Verify the `WHERE` clause uses `AccountId = '{{!Account.Id}}'`
* Test with a record you know has child records

</details>

<details>

<summary>"My values are under the wrong headers"</summary>

Check these items:

* Compare the header order with the `<column>` order
* Make sure each header has one matching query field
* Add fields one at a time when expanding the table

</details>

<details>

<summary>"One column is blank"</summary>

Check these items:

* Verify that field appears in the `SELECT` clause
* Verify the same field appears in the matching `<column>` tag
* Confirm the returned records actually contain data in that field

</details>

<details>

<summary>"The wrong records are returned"</summary>

Check these items:

* Review the relationship field in the `WHERE` clause
* Confirm the base merge field points to the current record
* Remove extra filters and test the base query first

</details>

<details>

<summary>"How do I adapt this to another object pair?"</summary>

Use this swap pattern:

* Change the base template object
* Change the child object in `FROM`
* Change the relationship field in `WHERE`
* Change the selected fields, headers, and `<column>` tags together

Keep the overall table structure the same.

</details>

## What You've Learned

✅ Built a related list table from an `Account` template\
✅ Queried `Opportunity` records with `<lineitemsSOQL>`\
✅ Tied the query to the current base record\
✅ Matched headers to `<column>` tags in the correct order\
✅ Reused one example pattern that can scale to other objects

## Next Steps

### Adapt the pattern

* Replace `Opportunity` with `Contact` on an `Account` template
* Change the visible columns for a different use case
* Add one more field and test it by itself

### Combine it with other features

* Use [Getting Started with Conditional Logic](/quick-start/template-building/conditional-logic-tutorial.md) to show the table only when related opportunities exist
* Use [Template Attributes Tutorial](/quick-start/template-building/how-to-format-text-dates-and-currency.md) to format dates, numbers, and output values inside the table
* Use [Creating Your First Named Query](/quick-start/template-building/creating-your-first-named-query-tutorial.md) when you need one reusable result outside a table

## Practice Exercise

Build an `Account` template that includes:

1. A header with `Account.Name` and `Account.Owner.Name`
2. A four-column related list table for opportunities
3. One query tied to the current account
4. A test run against an account with related opportunities
5. One filtered variation that only shows open opportunities

Bonus challenges:

* Rebuild the same pattern for `Account` and `Contact`
* Change the sort order
* Add one more displayed field


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://help.sdocs.com/quick-start/template-building/adding-tables-like-products-or-contacts-to-your-document.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
