A list is the right starting point because it's the smallest complete loop the engine runs: SQL goes in, a row template gets repeated once per result, HTML comes out. Nothing else on the platform is simpler than this, and almost everything else is built from the same idea.

The component for this is dynamiclist — a repeating HTML structure driven by data. You write real HTML tags and mark two of them: the container with type="dynamiclist", the per-row template with type="row". Add a SQL query, and the engine runs it and renders your template once per row returned, with that row's values substituted in.

1 The XML

Here's a complete, working dynamiclist block for a Contacts list — nothing trimmed:

emxml_blkContactsList.xml
<block>

    <div type="dynamiclist" class="contact-grid">

        <div type="row" class="contact-card">
            <div class="contact-avatar">%initials%</div>
            <div>
                <div class="contact-name">%contact_name%</div>
                <div class="contact-role">%job_title%</div>
                <div class="contact-meta">%email%</div>
            </div>
        </div>

        <!-- Inside a dynamiclist, the query follows the row template -->
        <sql>
            <query name="contacts_list"><![CDATA[
                SELECT contact_id,
                       contact_name,
                       job_title,
                       email,
                       UPPER(LEFT(contact_name,1)) AS initials
                FROM   contacts
                WHERE  dept_id = '%deptid%'
                ORDER BY contact_name ASC
            ]]></query>
        </sql>

    </div>

</block>

That's the whole thing. No loop to write, no result-set iteration, no string concatenation to build up a row of markup — just a template and a query.

What each piece is doing

<block>
Every block file's root element is <block>, not the dynamiclist itself. It's transparent — it never emits a tag of its own, so the list content sits one level in and the first real tag in the output is the one you wrote.
type="dynamiclist"
The behaviour marker, written on a real HTML tag you choose. There's no separate wrapper element: the <div> carrying it is the outer container the whole list renders inside — here a grid, styled by the contact-grid class.
type="row"
The template for one row — again a real tag of your choosing, marked with type="row". It's processed as a full element tree, so anything you can build elsewhere on the platform (nested containers, conditional blocks, other blocks) works inside it.
%tokens%
Column values from the query, substituted per row. Row tokens are scoped to the type="row" subtree and nowhere else — a sibling element outside the list can't see them, and neither can the next row. That scoping is enforced by the engine, not by convention.
Where <sql> goes
Inside a dynamiclist, the query follows the row template, as above. A block-level <sql> — one feeding the whole block rather than a list — is the opposite: it must be the first child of <block>, or the tokens it produces aren't resolved yet when the elements above it render.
2 What the engine generates

The engine runs contacts_list, gets back however many rows match, and renders the type="row" template once per row with that row's tokens substituted. For four contacts, the output looks like this:

Generated HTML
<div class="contact-grid">

    <div class="contact-card">
        <div class="contact-avatar">S</div>
        <div>
            <div class="contact-name">Sarah Whitfield</div>
            <div class="contact-role">Operations Manager</div>
            <div class="contact-meta">s.whitfield@example.com</div>
        </div>
    </div>

    <!-- … repeated once per row, no two the same — the template is fixed, the data isn't … -->

</div>

Every card is the identical markup structure — only the token values change. That's the entire value of a dynamiclist: write the shape once, let row count vary freely.

3 How it actually renders

And here's that generated HTML, live, in a real browser — not a mockup:

app.example.com/manager/contacts
S
Sarah Whitfield
Operations Manager
s.whitfield@example.com
D
David Okonkwo
Procurement Lead
d.okonkwo@example.com
M
Maria Santos
Finance Controller
m.santos@example.com
J
James Cooper
Warehouse Supervisor
j.cooper@example.com
Four rows or four hundred, the XML above doesn't change. The row count is the database's problem, not the developer's.

Why not just write a PHP loop?

Why should you? Because doing this manually means backend code, escaping values, wiring templates, and keeping everything in sync — work you don't need to do. dynamiclist lets you define the structure and the SQL together, and the engine handles the repetitive part.

In the next post, we'll look at dynamiclisttable — the component built specifically for tabular, column-based data — and how it extends the same idea for spreadsheet-like layouts.