Data Tables
UtilityA set of utility features for creating template-driven data tables.
Import
Types
Package
Source
Docs
WAI-ARIA
Examples
ID | User | Title | Body | ||
---|---|---|---|---|---|
1 | sunt aut facere repellat provident occaecati excepturi optio reprehenderit | quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto | |||
2 | qui est esse | est rerum tempore vitae sequi sint nihil reprehenderit dolor beatae ea dolores neque fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis qui aperiam non debitis possimus qui neque nisi nulla | |||
3 | ea molestias quasi exercitationem repellat qui ipsa sit aut | et iusto sed quo iure voluptatem occaecati omnis eligendi aut ad voluptatem doloribus vel accusantium quis pariatur molestiae porro eius odio et labore et velit aut | |||
4 | eum et est occaecati | ullam et saepe reiciendis voluptatem adipisci sit amet autem assumenda provident rerum culpa quis hic commodi nesciunt rem tenetur doloremque ipsam iure quis sunt voluptatem rerum illo velit | |||
5 | nesciunt quas odio | repudiandae veniam quaerat sunt sed alias aut fugiat sit autem sed est voluptatem omnis possimus esse voluptatibus quis est aut tenetur dolor neque |
Usage
What are Data Tables?
Within the context of Skeleton, data tables are not a singular feature, but rather a collection of utilities. These utilty features are opt-in, meaning you can progressively enhance any native HTML table to meet your requirements. This is one of the most complex features Skeleton provides, so please read carefully.
Getting Started
Let's start by importing all the utility features we'll need. We'll cover each of these in greater detail below.
import {
// Types
type DataTableModel,
// Utilities
dataTableHandler,
dataTableSelect,
dataTableSelectAll,
dataTableSort,
// Svelte Actions
tableInteraction,
tableA11y
} from '@skeletonlabs/skeleton';
We need data to populate the table. For simplicity, let's create this locally. In a real world app you might fetch this from an external API.
const sourceData = [
{ position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
{ position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
{ position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
{ position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
{ position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
{ position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },
{ position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },
{ position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },
{ position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },
{ position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' }
];
We'll make use of a few Tailwind Element table classes to provide base styles to our native HTML table element. These are optional, but recommended.
<div class="table-container">
<table class="table table-hover">
<thead>
<tr>
<th>Heading 1</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell 1</td>
</tr>
</tbody>
</table>
</div>
Data Table Model
To unlock the power of our data tables, we'll need to create what we'll refer to as a data table model. Create a new Svelte
writable store, if you're using Typescript set the type to DataTableModel
, then pass the store to the
dataTableHandler
method.
const dataTableModel: Writable<DataTableModel> = writable({
// The original unfiltered source data.
source: sourceData,
// The filtered source data, shown in UI.
filtered: sourceData,
// Optional: An array of selected row objects.
selection: [],
// Optional: The current search term.
search: '',
// Optional: The current sort key.
sort: '',
// Optional: The Paginator component settings.
pagination: { offset: 0, limit: 5, size: 0, amounts: [1, 2, 5, 10] }
});
// Automatically handles search, sort, etc when the model updates.
dataTableModel.subscribe((v) => dataTableHandler(v));
Next, we'll update our table markup to display our model data on the page. Add each desired heading paired with a matching body cell
value. We'll use an #each loop to create each table body row. Note we use $dataTableModel.filtered
as our loop source. The features below will modify this data.
<thead>
<tr>
<th>Position</th>
<th>Name</th>
<!-- ... --->
</tr>
</thead>
<tbody>
{#each $dataTableModel.filtered as row, rowIndex}
<tr>
<td>{row.position}</td>
<td>{row.name}</td>
<!-- ... --->
</tr>
{/each}
</tbody>
Search
To implement search, bind $dataTableModel.search
to any search input. You can place this input anywhere on the page.
<input bind:value={$dataTableModel.search} type="search" placeholder="Search..." />
Sort
We'll use dataTableSort
to automatically set $dataTableModel.sort
when a table heading is tapped. Add the following
to your table head element.
<thead on:click={(e) => { dataTableSort(e, dataTableModel) }} on:keypress>
Add a data-sort="(key)"
attribute to each heading you wish to be sortable. Tapping a heading will set the
$dataTableModel.sort
value and update the UI. Tapping a heading repeatedly will toggle between ascending and descending sort order.
<th data-sort="position">Position</th>
<th data-sort="name">Name</th>
<!-- ... -->
While sort is working, we're lacking a visual UI indicator. To handle this, implement the Svelte Action called tableInteraction
to your table element. This adds the appropriate CSS classes that show ↑ and ↓ sort arrows.
<table ... use:tableInteraction>
Selection
Per Row
To handle row selection, we'll add a new heading column. Keep the comment shown, as we'll replace it in a following step.
<th><!-- selection --></th>
Pair this with a matching table body cell that includes a checkbox input. Append bind:dataTableChecked
to the input to
extend the row object source data. When checked on/off, the dataTableHandler
will automatically include/exclude the
entire row object in
$dataTableModel.selection
.
<td><input type="checkbox" bind:checked={row.dataTableChecked} /></td>
If you wish to visually highlight the row selection, Tailwind Elements includes a semantic class for this. Append this to your table body row element.
<tr class:table-row-checked={row.dataTableChecked}>
Pre-Selected
You may wish to pre-select certain table rows. We've provided a utility method to handle this. Pass your model, the key to query against, and a whitelist of values. Any object that matches the conditions will be selected. Trigger this multiple times for multipe selection queries.
// Select all objects with a position value of 1 or 2:
dataTableSelect(dataTableModel, 'position', [1,2]);
Select All
If you wish to add a select all feature, replace <th><!-- selection --></th>
with the following.
<th><input type="checkbox" on:click={(e) => { dataTableSelectAll(e, dataTableModel) }} /></th>
Pagination
Please refer to the Paginators component to learn more about this feature. For data tables, use
$dataTableModel.pagination
to ensures the model updates reactively. The wrapping if statement is required.
{#if $dataTableModel.pagination}<Paginator bind:settings={$dataTableModel.pagination} />{/if}
Accessibility
Since data tables make use of native HTML table elements, you will need to implement accessibility features directly. However, we've
simplified this by providing a Svelte Action called tableA11y
. This implements the required event listeners for
keyboard interaction. Start by appending role and action to your table element.
<table ... role="grid" use:tableA11y>
Implement the aria-rowindex
attribute. This starts at 1 and increments per tr row. We can
utilize the #each loop index value, named rowIndex
.
<tr ... aria-rowindex={rowIndex + 1}>
Implement three attributes per table body td cell. role
and tabindex
are static, while
aria-colindex
starts at 1 and increments per cell.
<td ... role="gridcell" aria-colindex={1} tabindex="0">...</td>
<td ... role="gridcell" aria-colindex={2} tabindex="0">...</td>
<!-- ... -->
Reference the Keyboard tab section at the top of this page for a list of available keyboard interactions.
View Reference
If you wish to see a complete data table, we recommend tapping the Doc Source link at the top of this page. This will allow you to inspect how the featured example at the top of this page was constructed. This implements every available data table feature.
Table Components
Looking for a simpler data-driven table component? Visit the Table documentation.