Changelog Azalea Kit UI 101 Usage Customizing Utilities Form controls Laravel Vue FHIR Types Accessibility Icons Actions Button Button group Layout and structure Card Card section Care summary Care timeline Divider Empty state Footer Header Layout Stack Table Selection and input Calendar Checkbox Checkbox group Color picker Combobox Combobox option Input Input mask Memorable date Number input Option Phone input Radio Radio button Radio group Range Select SSN input Tag Textarea Toggle ZIP code input Images and icons Avatar Icon Icon button Logo Feedback indicators Badge Banner Progress bar Skeleton Skeleton body text Skeleton display text Skeleton thumbnail Spinner Typography Text Navigation Breadcrumb Breadcrumb item Dropdown Menu Menu item Menu label Navigation Nav group Nav item Nav toggle Pagination Tab Tab group Tab panel Top bar Tree Tree item User menu Overlays Dialog Popover Tooltip Utilities Collapsible FHIR address FHIR human name FHIR quantity FHIR questionnaire Format bytes Format date Format number Relative time Resize observer Split panel Tooltip Border Color Shadow Spacing Transition Typography Z-index Contributing Visual Tests Light Dark System

Table

<ahi-table> | AhiTable

Tables are used to organize and display information from a data set. Provides table styles in addition to features like striped rows and support for narrow viewports.

Toggle all
Amount Status Description Method Date Actions
Select row
Success Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Card
Actions
Select row
Warning Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Card
Actions
Select row
Success Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Card
Actions
Select row
Success Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Card
Actions
<ahi-card title="Recent payments" style="width: 100%">
  <div class="ahi-row ahi-gap-2 ahi-align-items-center" slot="header-actions" style="visibility: hidden">
    <span class="ahi-text-body message" aria-live="polite" aria-atomic="true">0 items selected</span>
    <ahi-button variant="primary" size="small">Export</ahi-button>
  </div>

  <ahi-table style="margin-top: var(--ahi-space-4)">
    <table>
      <thead>
        <tr>
          <th>
            <div class="ahi-table-actions">
              <ahi-checkbox>
                <span class="ahi-visually-hidden">Toggle all</span>
              </ahi-checkbox>
            </div>
          </th>
          <th class="ahi-table-align-right">Amount</th>
          <th>Status</th>
          <th>Description</th>
          <th>Method</th>
          <th class="ahi-table-align-right">Date</th>
          <th class="ahi-table-align-right">Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>
            <div class="ahi-table-actions">
              <ahi-checkbox>
                <span class="ahi-visually-hidden">Select row</span>
              </ahi-checkbox>
            </div>
          </td>
          <td class="ahi-table-align-right">
            <ahi-format-number type="currency" value="350.00"></ahi-format-number>
          </td>
          <td>
            <ahi-badge status="success">Success</ahi-badge>
          </td>
          <td class="ahi-table-ellipsis">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
            nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
            anim id est laborum.
          </td>
          <td>Card</td>
          <td class="ahi-table-align-right">
            <ahi-format-date precision="date" date="2021-03-20" time-zone="America/New_York"></ahi-format-date>
          </td>
          <td>
            <div class="ahi-table-actions">
              <ahi-button size="small">
                <span class="ahi-visually-hidden">Actions</span>
                <ahi-icon name="dots"></ahi-icon>
              </ahi-button>
            </div>
          </td>
        </tr>
        <tr>
          <td>
            <div class="ahi-table-actions">
              <ahi-checkbox>
                <span class="ahi-visually-hidden">Select row</span>
              </ahi-checkbox>
            </div>
          </td>
          <td class="ahi-table-align-right">
            <ahi-format-number type="currency" value="29.99"></ahi-format-number>
          </td>
          <td>
            <ahi-badge status="warning">Warning</ahi-badge>
          </td>
          <td class="ahi-table-ellipsis">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
            nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
            anim id est laborum.
          </td>
          <td>Card</td>
          <td class="ahi-table-align-right">
            <ahi-format-date precision="date" date="2021-03-20" time-zone="America/New_York"></ahi-format-date>
          </td>
          <td>
            <div class="ahi-table-actions">
              <ahi-button size="small">
                <span class="ahi-visually-hidden">Actions</span>
                <ahi-icon name="dots"></ahi-icon>
              </ahi-button>
            </div>
          </td>
        </tr>
        <tr>
          <td>
            <div class="ahi-table-actions">
              <ahi-checkbox>
                <span class="ahi-visually-hidden">Select row</span>
              </ahi-checkbox>
            </div>
          </td>
          <td class="ahi-table-align-right">
            <ahi-format-number type="currency" value="50.00"></ahi-format-number>
          </td>
          <td>
            <ahi-badge status="success">Success</ahi-badge>
          </td>
          <td class="ahi-table-ellipsis">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
            nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
            anim id est laborum.
          </td>
          <td>Card</td>
          <td class="ahi-table-align-right">
            <ahi-format-date precision="date" date="2021-03-19" time-zone="America/New_York"></ahi-format-date>
          </td>
          <td>
            <div class="ahi-table-actions">
              <ahi-button size="small">
                <span class="ahi-visually-hidden">Actions</span>
                <ahi-icon name="dots"></ahi-icon>
              </ahi-button>
            </div>
          </td>
        </tr>
        <tr>
          <td>
            <div class="ahi-table-actions">
              <ahi-checkbox>
                <span class="ahi-visually-hidden">Select row</span>
              </ahi-checkbox>
            </div>
          </td>
          <td class="ahi-table-align-right">
            <ahi-format-number type="currency" value="49.00"></ahi-format-number>
          </td>
          <td>
            <ahi-badge status="success">Success</ahi-badge>
          </td>
          <td class="ahi-table-ellipsis">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
            nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
            anim id est laborum.
          </td>
          <td>Card</td>
          <td class="ahi-table-align-right">
            <ahi-format-date precision="date" date="2021-03-17" time-zone="America/New_York"></ahi-format-date>
          </td>
          <td>
            <div class="ahi-table-actions">
              <ahi-button size="small">
                <span class="ahi-visually-hidden">Actions</span>
                <ahi-icon name="dots"></ahi-icon>
              </ahi-button>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </ahi-table>
</ahi-card>

<script>
  const { tHead, tBodies: [tBody] } = document.querySelector("table");
  const checkboxes = tBody.querySelectorAll("ahi-checkbox");
  const toggleAll = tHead.querySelector("ahi-checkbox");
  const actions = document.querySelector("[slot=header-actions]");
  const button = actions.querySelector("ahi-button");
  const message = actions.querySelector(".message");

  function render() {
    let count = 0;

    checkboxes.forEach(checkbox => {
      checkbox.closest("tr").classList.toggle("ahi-row-selected", checkbox.checked);
      if (checkbox.checked) count++;
    })

    toggleAll.indeterminate = count > 0 && count < checkboxes.length;
    toggleAll.checked = count === checkboxes.length;

    message.innerText = `${count} ${count === 1 ? "item" : "items"} selected`;
    actions.style.visibility = count === 0 ? "hidden" : "visible";
  }

  tBody.addEventListener("ahi-change", render);
  toggleAll.addEventListener("ahi-change", () => {
    checkboxes.forEach(checkbox => (checkbox.checked = toggleAll.checked));
    render();
  })
</script>

Row states

Rows highlight on hover and darken while pressed. Both are automatic — there is nothing to opt into.

The hover is behind @media (hover: hover), so it never appears on a touch device. A touch browser reports a hover on whatever was tapped last and keeps reporting it, which would otherwise leave the tapped row highlighted long after the finger lifted. The press state is not gated and is the feedback a tap gets instead.

.ahi-row-selected is a state you set, not a pointer position, so it is outside that gate and paints on every device. Add and remove it yourself, as the selection example above does.

If your app dims a row — an inactive patient, a cancelled order — note that the hover restores full-strength text for as long as the pointer is over it. That only happens on a device that can hover; the dimming is untouched on a phone.

Narrow viewports

A table’s natural width is the sum of its columns, which on a phone means it scrolls sideways. Three options address that, each independent of the others and all off by default.

Stacked rows

stacked turns each row into a card once the table’s own box is too narrow for columns, so it reads as a list instead of scrolling. Each value is labelled with its column heading, which the component copies from the table’s <thead> — your markup needs nothing but the attribute.

The switch is a container query on the table’s own width, not the viewport’s, so a table inside a narrow column stacks even on a wide screen.

Medication Dose Prescriber
Lisinopril 10 mg daily Dr. Jane Doe
Metformin 500 mg twice daily Dr. Jane Doe
<div style="max-inline-size: 380px; resize: horizontal; overflow: auto; padding-block-end: 1rem;">
  <ahi-table stacked>
    <table>
      <thead>
        <tr>
          <th>Medication</th>
          <th>Dose</th>
          <th>Prescriber</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Lisinopril</td>
          <td>10 mg daily</td>
          <td>Dr. Jane Doe</td>
        </tr>
        <tr>
          <td>Metformin</td>
          <td>500 mg twice daily</td>
          <td>Dr. Jane Doe</td>
        </tr>
      </tbody>
    </table>
  </ahi-table>
</div>

Drag the handle in the corner to widen the container and the cards become columns again.

To label a cell differently from its column heading — spelling out an abbreviation, or naming a column headed by an icon — set data-label on the cell yourself and the component will leave it alone.

<td data-label="Date of birth">1984-02-11</td>

A cell carrying .ahi-table-ellipsis keeps its full value once the table stacks. The utility truncates a long value to one line so it does not widen its column, and a card has no column and no sideways room to save, so there is nothing left for it to do — the value wraps instead. Nothing to change in your markup: the same cell truncates with columns and wraps as a card.

Sticky first column

sticky-column pins the first column while the rest scrolls, so a row keeps its identifier as the reader scrolls right to reach a value. It has no effect while a table is stacked, where each card is already its own identifier.

Patient MRN Last seen Primary care Insurer Next appointment
Jane Doe 884120 12 Aug 2026 Dr. Jane Doe Blue Ridge Health 04 Sep 2026
John Smith 884121 28 Jul 2026 Dr. John Smith Cardinal Mutual 11 Sep 2026
<ahi-table sticky-column>
  <table>
    <thead>
      <tr>
        <th>Patient</th>
        <th>MRN</th>
        <th>Last seen</th>
        <th>Primary care</th>
        <th>Insurer</th>
        <th>Next appointment</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Jane Doe</td>
        <td>884120</td>
        <td>12 Aug 2026</td>
        <td>Dr. Jane Doe</td>
        <td>Blue Ridge Health</td>
        <td>04 Sep 2026</td>
      </tr>
      <tr>
        <td>John Smith</td>
        <td>884121</td>
        <td>28 Jul 2026</td>
        <td>Dr. John Smith</td>
        <td>Cardinal Mutual</td>
        <td>11 Sep 2026</td>
      </tr>
    </tbody>
  </table>
</ahi-table>

Scroll cue

Any table that overflows its box shows a shadow at the edge with content beyond it, which fades away as the reader reaches that end. It needs no attribute and costs nothing when the table fits — it exists because the platform’s overlay scrollbar is invisible until you are already scrolling, so nothing told the reader the remaining columns were there.

Advanced use-cases

The table component itself does not provide any functionality, only styles. This works well for simple or static tables, but there are instances where functionality such as sorting, resizing, or column dragging are required. This kind of functionality is out of scope for the table component, as the spectrum of possible use-cases is too broad for a single component to cover effectively. Whilst the table component is limited out of the box, these same limitations make it easy to integrate with third-party libraries which offer advanced functionality.

For these advanced use-cases we recommend using Tanstack Table. Tanstack table offers integrations for many UI frameworks, such as Vue and React. It is headless so it provides functions, state, utilities and event listeners but does not include any HTML or styles. You can use it to render a plain, semantic <table> wrapped in an <ahi-table>, and you will get all the necessary styles.

Best practices

  • The table component acts as a lightweight and un-opinionated wrapper component for enhancing tabular data. It is up to the user of this component to make sure that the table markup they use is accessible.
  • Despite the name the table component does not supplement the HTML <table> element. Please ensure that a <table> element is a direct descendant of the table component.
  • It’s important to pay close attention to semantics when authoring tables. The markup in the examples can be used as a starting point. However, be aware that HTML tables have a large feature set which cannot be fully covered in this documentation.

Tables should:

  • Be used to display tabular data
  • Have a <table> element directly within the component
  • Be used inside an <ahi-card> and given a title
  • Paginate when the current list contains more than 25 items.

Tables should NOT:

  • Be used to display list data
  • Be used to display basic key and value pairs, consider using a description list instead
  • Be used for layout
  • Use a primary button in every row

Content guidelines

  • Headers in a table should be clear, accurate and predictable. When writing headers, always write them in sentence case, not title case. The first word should be capitalized and the rest lowercase (unless a proper noun)
  • Include units of measurement symbols in the table header so they aren’t repeated throughout every single column ie “Temperature °C”
  • Avoid unnecessary words and articles in table headers, such as “the”, “an” or “a”
  • Keep decimals consistent. For example, don’t use 3 decimals in one row and 2 in others

Importing

Script Bundler

To import this component using a script tag:

<script type="module" src="/path/to/azalea-kit-ui/dist/components/table/table.js"></script>

To import this component using a bundler:

import '@azaleahealth/azalea-kit-ui/dist/components/table/table.js';

Slots

Name Description
(default) Default slot which holds the HTML <table> element.

Learn more about using slots.

Properties

Name Description Type Default
density Controls the density of the table’s rows and headers. Relaxed increases space, condensed reduces space. 'condensed' | 'default' | 'relaxed' 'default'
scrollSnap
scroll-snap
Enables scroll-snapping, meaning the scroll position is always column-aligned. boolean false
striped Controls whether to use zebra striping on tables, which can improve readability. boolean false
stacked Stacks each row into a card once the table itself is too narrow for columns, so a phone-width table reads as a list instead of scrolling sideways. Each cell is labelled with its column heading, taken from the table’s own <thead>; set data-label on a cell to override it. The switch is a container query on the table’s width rather than the viewport’s, so a table in a narrow column stacks even on a wide screen. boolean false
stickyColumn
sticky-column
Pins the first column while the rest of the table scrolls horizontally, so a row keeps its identifier as the reader scrolls to reach a value. Has no effect while the table is stacked, where there are no columns to pin. boolean false
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about properties and attributes.