Data Tables

Utility

A set of utility features for creating template-driven data tables.

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

Getting Started

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.

ts
import {
	// Utilities
	createDataTableStore,
	dataTableHandler,
	// 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. We demonstrate this in the example on this page. See the use of PageData in the source.

ts
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 Skeleton's table element classes to provide base styles to our native HTML table element. These are optional, but recommended.

html
<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 with createDataTableStore, then pass the model of our store to the dataTableHandler method.

ts
const dataTableStore = createDataTableStore(
	// Pass your source data here:
	sourceData,
	// Provide optional settings:
	{
		// The current search term.
		search: '',
		// The current sort key.
		sort: '',
		// Paginator component settings.
		pagination: { offset: 0, limit: 5, size: 0, amounts: [1, 2, 5, 10] }
	}
);

// This automatically handles search, sort, etc when the model updates.
dataTableStore.subscribe((model) => dataTableHandler(model));

Next, we'll update our table markup to display our model data. Implement matching parent headings and body cells. We'll use an #each loop to generate each table row. Note we use $dataTableStore.filtered as our loop source. This represents the data as modified by search, sort, pagination, and more.

ts
<thead>
	<tr>
		<th>Position</th>
		<th>Name</th>
		<!-- ... --->
	</tr>
</thead>
<tbody>
	{#each $dataTableStore.filtered as row, rowIndex}
		<tr>
			<td>{row.position}</td>
			<td>{row.name}</td>
			<!-- ... --->
		</tr>
	{/each}
</tbody>

Updating the Source

To update the content of the data table, just call the dataTableStore.updateSource() method and pass in your new source data.

ts
dataTableStore.updateSource(newSourceData)

Search

To implement search, bind $dataTableStore.search to any search input. You may add this anywhere as long as it has scope of your table model (the store).

html
<input bind:value={$dataTableStore.search} type="search" placeholder="Search..." />

Sort

We'll use the dataTableStore.sort() method to automatically set $dataTableStore.sort when a table heading is tapped. Add the following click method once to your table's thead element.

html
<thead on:click={(e) => { dataTableStore.sort(e) }} on:keypress>

Add a data-sort="(key)" attribute to each heading you wish to be sortable. Tapping a heading will set the $dataTableStore.sort value and update the UI. Tapping a heading repeatedly will toggle between ascending and descending sort order.

html
<th data-sort="position">Position</th>
<th data-sort="name">Name</th>
<!-- ... -->

While sort is working, there's currently no visual UI indicator. To handle this, implement the Svelte Action called tableInteraction to your table element. This will toggle the appropriate CSS classes and show ↑ and ↓ sort arrows.

html
<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.

html
<th><!-- selection --></th>

Pair this with a matching table body cell that includes a checkbox input. Append bind:checked 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 $dataTableStore.selection.

html
<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.

html
<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 in the key to query against, and a whitelist of values. Any object that matches the conditions will be selected. Trigger this multiple times for multiple selection queries.

ts
// Selects all objects with a position value of 1 or 2:
dataTableStore.select('position', [1,2]);

Select All

If you wish to add a select all feature, replace <th><!-- selection --></th> with the following.

html
<th><input type="checkbox" on:click={(e) => { dataTableStore.selectAll(e.currentTarget.checked) }} /></th>

Pagination

Please refer to the Paginators component to learn more about this feature. For data tables, use $dataTableStore.pagination to ensures the model updates reactively. The wrapping if statement is required.

html
{#if $dataTableStore.pagination}<Paginator bind:settings={$dataTableStore.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.

html
<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.

html
<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.

html
<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.

View Tables