Skip to content
Magma Design System
Table<mds-table>Generated from @maggioli-design-system/magma@2.3.0dist/documentation.json. Encapsulation: shadow.
Common incorrect uses of <mds-table>. Each entry pairs the wrong form with the right one and a one-line reason. System-wide rules (boolean-as-string, shadow piercing, Tailwind color utilities, raw native event listening) live in agents/anti-patterns.md - they apply here too but are not repeated.
Do Not Replace Compound Subparts with Raw HTML Elements
<mds-table> orchestrates its children via internal Stencil mechanisms. Substituting any subpart (mds-table-header, mds-table-body, mds-table-row, mds-table-cell) with a raw <thead>, <tbody>, <tr>, or <td> breaks propagation of interactive, selectable, and overflow state.
<!-- INCORRECT -->
<mds-table>
<thead>
<tr><th>Nome</th><th>Email</th></tr>
</thead>
<tbody>
<tr><td>Mario Rossi</td><td>mario.rossi@example.com</td></tr>
</tbody>
</mds-table>
<!-- CORRECT -->
<mds-table>
<mds-table-header>
<mds-table-header-cell label="Nome"></mds-table-header-cell>
<mds-table-header-cell label="Email"></mds-table-header-cell>
</mds-table-header>
<mds-table-body>
<mds-table-row>
<mds-table-cell><mds-text typography="detail">Mario Rossi</mds-text></mds-table-cell>
<mds-table-cell><mds-text typography="detail">mario.rossi@example.com</mds-text></mds-table-cell>
</mds-table-row>
</mds-table-body>
</mds-table>
Do Not Slot Batch-Action Buttons Inside the Body
The slot="batch-action" must be a direct child of <mds-table>, not nested inside mds-table-body or any row. The component queries :scope > [slot="batch-action"] at load time and the bar will not appear if the slot is misplaced.
<!-- INCORRECT -->
<mds-table selectable>
<mds-table-body>
<mds-table-row>...</mds-table-row>
<mds-button slot="batch-action" label="Elimina" variant="error" tone="text"></mds-button>
</mds-table-body>
</mds-table>
<!-- CORRECT -->
<mds-table selectable>
<mds-table-header>...</mds-table-header>
<mds-table-body>
<mds-table-row>...</mds-table-row>
</mds-table-body>
<mds-button slot="batch-action" label="Elimina" variant="error" tone="text"></mds-button>
</mds-table>
Do Not Slot Row Action Buttons Inside mds-table-cell
Per-row action buttons use slot="action" on a direct child of mds-table-row, not wrapped inside an mds-table-cell. Placing them in a cell hides them (mds-table-cell has no action slot, so the button is not rendered) and breaks the overflow-detection logic that switches rows into overlay-action mode.
<!-- INCORRECT -->
<mds-table-row>
<mds-table-cell><mds-text typography="detail">Contratto.pdf</mds-text></mds-table-cell>
<mds-table-cell>
<mds-button slot="action" icon="mi/baseline/delete" title="Elimina" variant="error" tone="text"></mds-button>
</mds-table-cell>
</mds-table-row>
<!-- CORRECT -->
<mds-table-row>
<mds-table-cell><mds-text typography="detail">Contratto.pdf</mds-text></mds-table-cell>
<mds-button slot="action" icon="mi/baseline/delete" title="Elimina" variant="error" tone="text"></mds-button>
</mds-table-row>
Do Not Enable Batch Actions Without selectable
The batch-action bar only renders when selectable is set. Slotting batch-action buttons on a non-selectable table silently produces no bar and no selection count, causing the buttons to never appear.
<!-- INCORRECT -->
<mds-table>
<mds-table-header>...</mds-table-header>
<mds-table-body>...</mds-table-body>
<mds-button slot="batch-action" label="Esporta" variant="primary"></mds-button>
</mds-table>
<!-- CORRECT -->
<mds-table selectable>
<mds-table-header>...</mds-table-header>
<mds-table-body>...</mds-table-body>
<mds-button slot="batch-action" label="Esporta" variant="primary"></mds-button>
</mds-table>
Do Not Set interactive on Individual Rows
interactive is a table-level prop that propagates automatically to every mds-table-row. Setting it directly on individual rows bypasses the table’s state management and leads to inconsistent behavior when rows are added or removed at runtime.
<!-- INCORRECT -->
<mds-table>
<mds-table-body>
<mds-table-row interactive>...</mds-table-row>
<mds-table-row>...</mds-table-row>
</mds-table-body>
</mds-table>
<!-- CORRECT -->
<mds-table interactive>
<mds-table-body>
<mds-table-row>...</mds-table-row>
<mds-table-row>...</mds-table-row>
</mds-table-body>
</mds-table>
Do Not Listen for Native change Events to Track Selection
Selection state is communicated via the mdsTableSelectionChange custom event. Native change events from the internal checkboxes do not bubble out of the shadow DOM reliably; always use the documented Magma event.
<!-- INCORRECT -->
<mds-table selectable id="tbl">...</mds-table>
<script>
document.getElementById('tbl').addEventListener('change', (e) => {
// unreliable - native events do not bubble from shadow DOM
});
</script>
<!-- CORRECT -->
<mds-table selectable id="tbl">...</mds-table>
<script>
document.getElementById('tbl').addEventListener('mdsTableSelectionChange', (e) => {
console.log('Righe selezionate:', e.detail.rows);
});
</script>
Do Not Set selection Manually
selection is a reflected read-back attribute managed internally by the table. It becomes true when at least one row is selected and is intended for CSS attribute selectors or conditional logic in the host app. Setting it from outside does not change which rows are selected and will be overwritten on the next selection update. The table emits mdsTableSelectionChange before it updates selection, so inside that listener read e.detail.rows, not the attribute.
<!-- INCORRECT -->
<mds-table selectable selection>
...
</mds-table>
<!-- CORRECT - read it back, do not set it -->
<mds-table selectable id="tbl">
...
</mds-table>
<script>
document.getElementById('tbl').addEventListener('mdsTableSelectionChange', (e) => {
const hasSelection = e.detail.rows.length > 0;
document.getElementById('panel-azioni').hidden = !hasSelection;
});
</script>