Table
Tables lay out rows of data, with variants for responsive, sortable, selectable and sticky-header tables.
Basic table
The basic table design has light padding and the presented data is separated with horizontal dividers. It helps provide users with all the necessary information, without overwhelming them with visuals.
The .table class adds basic styling to a table:
| Name | Title | Role | ||
|---|---|---|---|---|
| Paweł Kuna | UI Designer, Training | [email protected] | User | Edit |
| Jeffie Lewzey | Chemical Engineer, Support | [email protected] | Admin | Edit |
| Mallory Hulme | Geologist IV, Support | [email protected] | User | Edit |
| Dunn Slane | Research Nurse, Sales | [email protected] | Owner | Edit |
| Emmy Levet | VP Product Management, Accounting | [email protected] | Admin | Edit |
<div class="table-responsive">
<table class="table table-vcenter">
<thead>
<tr>
<th>Name</th>
<th>Title</th>
<th>Email</th>
<th>Role</th>
<th class="w-1"></th>
</tr>
</thead>
<tbody>
<tr>
<td>Paweł Kuna</td>
<td class="text-secondary">UI Designer, Training</td>
<td class="text-secondary">
<a href="#" class="text-reset">
<a href="mailto:[email protected]">[email protected]</a>
</a>
</td>
<td class="text-secondary">User</td>
<td>
<a href="#">Edit</a>
</td>
</tr>
<tr>
<td>Jeffie Lewzey</td>
<td class="text-secondary">Chemical Engineer, Support</td>
<td class="text-secondary">
<a href="#" class="text-reset">
<a href="mailto:[email protected]">[email protected]</a>
</a>
</td>
<td class="text-secondary">Admin</td>
<td>
<a href="#">Edit</a>
</td>
</tr>
<tr>
<td>Mallory Hulme</td>
<td class="text-secondary">Geologist IV, Support</td>
<td class="text-secondary">
<a href="#" class="text-reset">
<a href="mailto:[email protected]">[email protected]</a>
</a>
</td>
<td class="text-secondary">User</td>
<td>
<a href="#">Edit</a>
</td>
</tr>
<tr>
<td>Dunn Slane</td>
<td class="text-secondary">Research Nurse, Sales</td>
<td class="text-secondary">
<a href="#" class="text-reset">
<a href="mailto:[email protected]">[email protected]</a>
</a>
</td>
<td class="text-secondary">Owner</td>
<td>
<a href="#">Edit</a>
</td>
</tr>
<tr>
<td>Emmy Levet</td>
<td class="text-secondary">VP Product Management, Accounting</td>
<td class="text-secondary">
<a href="#" class="text-reset">
<a href="mailto:[email protected]">[email protected]</a>
</a>
</td>
<td class="text-secondary">Admin</td>
<td>
<a href="#">Edit</a>
</td>
</tr>
</tbody>
</table>
</div>Responsive tables
Use the .table-responsive class across each breakpoint for horizontal scrolling tables. If you want to create responsive tables up to a specific breakpoint, use .table-responsive{-sm|-md|-lg|-xl}. From that breakpoint and up, the table will behave normally, rather than scroll horizontally.
| # | Heading 1 | Heading 2 | Heading 3 | Heading 4 | Heading 5 | Heading 6 | Heading 7 | Heading 8 | Heading 9 | Heading 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Cell | Cell | Cell | Cell | Cell | Cell | Cell | Cell | Cell | Cell |
| 2 | Cell | Cell | Cell | Cell | Cell | Cell | Cell | Cell | Cell | Cell |
<table class="table table-responsive">
<thead>
<tr>
<th>#</th>
<th class="text-nowrap">Heading 1</th>
<th class="text-nowrap">Heading 2</th>
<th class="text-nowrap">Heading 3</th>
<th class="text-nowrap">Heading 4</th>
<th class="text-nowrap">Heading 5</th>
<th class="text-nowrap">Heading 6</th>
<th class="text-nowrap">Heading 7</th>
<th class="text-nowrap">Heading 8</th>
<th class="text-nowrap">Heading 9</th>
<th class="text-nowrap">Heading 10</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th>2</th>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
</table>No wrap
If you don't want the table cell content to wrap to another line, use the table-nowrap class.
| Name | Title | Role | |||
|---|---|---|---|---|---|
| Paweł Kuna | UI Designer, Training | [email protected] | User | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi, commodi cupiditate debitis deserunt expedita hic incidunt iste modi molestiae nesciunt non nostrum perferendis perspiciatis placeat praesentium quaerat quo repellendus, voluptates. | Edit |
<div class="table-responsive">
<table class="table table-vcenter table-nowrap">
<thead>
<tr>
<th>Name</th>
<th>Title</th>
<th>Email</th>
<th>Role</th>
<th></th>
<th class="w-1"></th>
</tr>
</thead>
<tbody>
<tr>
<td>Paweł Kuna</td>
<td class="text-secondary">UI Designer, Training</td>
<td class="text-secondary">
<a href="#" class="text-reset">
<a href="mailto:[email protected]">[email protected]</a>
</a>
</td>
<td class="text-secondary">User</td>
<td> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi, commodi cupiditate debitis deserunt expedita hic incidunt iste modi molestiae nesciunt non nostrum perferendis perspiciatis placeat praesentium quaerat quo repellendus, voluptates. </td>
<td>
<a href="#">Edit</a>
</td>
</tr>
</tbody>
</table>
</div>Table variants
Use contextual classes such as .table-primary or .table-danger on a <tr> element to color rows and highlight their meaning.
| Class | Heading | Heading |
|---|---|---|
| Default | Cell | Cell |
| Primary | Cell | Cell |
| Secondary | Cell | Cell |
| Success | Cell | Cell |
| Danger | Cell | Cell |
| Warning | Cell | Cell |
| Info | Cell | Cell |
| Light | Cell | Cell |
| Dark | Cell | Cell |
<table class="table">
<thead>
<tr>
<th scope="col">Class</th>
<th scope="col">Heading</th>
<th scope="col">Heading</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Default</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-primary">
<th scope="row">Primary</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-secondary">
<th scope="row">Secondary</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-success">
<th scope="row">Success</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-danger">
<th scope="row">Danger</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-warning">
<th scope="row">Warning</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-info">
<th scope="row">Info</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-light">
<th scope="row">Light</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-dark">
<th scope="row">Dark</th>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
</table>Table with sticky header
Add the .sticky-top class to the <thead> element to keep the header row visible while users scroll through a long table.
| Class | Heading | Heading |
|---|---|---|
| Default | Cell | Cell |
| Primary | Cell | Cell |
| Secondary | Cell | Cell |
| Success | Cell | Cell |
| Danger | Cell | Cell |
| Warning | Cell | Cell |
| Info | Cell | Cell |
| Light | Cell | Cell |
| Dark | Cell | Cell |
| Default | Cell | Cell |
| Primary | Cell | Cell |
| Secondary | Cell | Cell |
| Success | Cell | Cell |
| Danger | Cell | Cell |
| Warning | Cell | Cell |
| Info | Cell | Cell |
| Light | Cell | Cell |
| Dark | Cell | Cell |
<table class="table">
<thead class="sticky-top">
<tr>
<th scope="col">Class</th>
<th scope="col">Heading</th>
<th scope="col">Heading</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Default</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Primary</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Secondary</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Success</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Danger</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Warning</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Info</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Light</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-dark">
<th scope="row">Dark</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Default</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Primary</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Secondary</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Success</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Danger</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Warning</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Info</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th scope="row">Light</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr class="table-dark">
<th scope="row">Dark</th>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
</table>Sortable headers
Put a .table-sort button inside a th to get a sortable-looking header. The button shows a neutral arrow, and adding .asc or .desc marks the current direction.
Sorting itself is up to you - the classes only style the header. Tabler's own demo uses List.js and passes the field through data-sort.
| Elliot Fisher | Adelaide | 92 |
| Beverly Mills | Bristol | 78 |
<table class="table">
<thead>
<tr>
<th>
<button class="table-sort" data-sort="sort-name">Name</button>
</th>
<th>
<button class="table-sort asc" data-sort="sort-city">City</button>
</th>
<th>
<button class="table-sort" data-sort="sort-score">Score</button>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Elliot Fisher</td>
<td>Adelaide</td>
<td>92</td>
</tr>
<tr>
<td>Beverly Mills</td>
<td>Bristol</td>
<td>78</td>
</tr>
</tbody>
</table>Selectable rows
Add .table-selectable to the table and .table-selectable-check to each checkbox. A checked row gets a highlighted background, with no JavaScript involved.
Inside such a row you can swap content with .on-checked and .on-unchecked: the first only shows while the row is selected, the second while it is not.
| Task | Status | |
|---|---|---|
| Prepare the release notes | SelectedNot selected | |
| Review the pull request | SelectedNot selected |
<table class="table table-selectable">
<thead>
<tr>
<th class="w-1">
<input type="checkbox" class="form-check-input table-selectable-check" aria-label="Select all rows" />
</th>
<th>Task</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox" class="form-check-input table-selectable-check" aria-label="Select task" checked />
</td>
<td>Prepare the release notes</td>
<td>
<span class="on-checked">Selected</span>
<span class="on-unchecked text-secondary">Not selected</span>
</td>
</tr>
<tr>
<td>
<input type="checkbox" class="form-check-input table-selectable-check" aria-label="Select task" />
</td>
<td>Review the pull request</td>
<td>
<span class="on-checked">Selected</span>
<span class="on-unchecked text-secondary">Not selected</span>
</td>
</tr>
</tbody>
</table>Stacked table on small screens
A wide table can stack into a list instead of scrolling sideways. Use .table-mobile-{breakpoint} - .table-mobile-md stacks below md - and give every cell a data-label, which becomes the label above its value.
| Name | City | Score |
|---|---|---|
| Elliot Fisher | Adelaide | 92 |
| Beverly Mills | Bristol | 78 |
<table class="table table-mobile-md">
<thead>
<tr>
<th>Name</th>
<th>City</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Name">Elliot Fisher</td>
<td data-label="City">Adelaide</td>
<td data-label="Score">92</td>
</tr>
<tr>
<td data-label="Name">Beverly Mills</td>
<td data-label="City">Bristol</td>
<td data-label="Score">78</td>
</tr>
</tbody>
</table>Borderless, centered and transparent tables
.table-borderless removes the header background, .table-transparent clears the header and body backgrounds, and .table-center centers every cell.
| Plan | Seats | Price |
|---|---|---|
| Starter | 3 | $9 |
| Team | 10 | $29 |
<table class="table table-borderless table-center">
<thead>
<tr>
<th>Plan</th>
<th>Seats</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Starter</td>
<td>3</td>
<td>$9</td>
</tr>
<tr>
<td>Team</td>
<td>10</td>
<td>$29</td>
</tr>
</tbody>
</table>Truncated cell
A long value in one column stretches the whole table. Put td-truncate on that cell and text-truncate on the element inside it, and the text is cut with an ellipsis while the other columns keep their width.
| Name | Note |
|---|---|
| Elliot Fisher | A note long enough that it would otherwise push the first column out of the way and stretch the table past the width of the page. |
<table class="table">
<thead>
<tr>
<th class="w-1">Name</th>
<th>Note</th>
</tr>
</thead>
<tbody>
<tr>
<td>Elliot Fisher</td>
<td class="td-truncate">
<div class="text-truncate"> A note long enough that it would otherwise push the first column out of the way and stretch the table past the width of the page. </div>
</td>
</tr>
</tbody>
</table>Accessibility
A table is read cell by cell, so the structure has to say what each cell means.
- Use
thwithscope="col"orscope="row"for headers. Without a scope, a screen reader cannot tie a value back to its column. - Add a
captionwhen the table needs a title. It is announced first and stays tied to the table, unlike a heading above it. - In a selectable table, give every checkbox its own
aria-label- "Select task" is enough, and "Select all rows" for the one in the header. .table-sortis a realbutton, so it is reachable by keyboard. Once you wire up sorting, also setaria-sorton thethtoascending,descendingornone.- A stacked
.table-mobile-*still keeps its header cells in the markup, so the reading order stays correct while thedata-labeltext carries the column name visually.
SCSS variables
Use these SCSS variables to customize tables. The default values are:
$table-bg: transparent;
$table-color: inherit;
$table-accent-bg: transparent;
$table-cell-padding-x: 0.75rem;
$table-cell-padding-y: 0.75rem;
$table-cell-padding-y-sm: 0.25rem;
$table-cell-padding-x-sm: 0.25rem;
$table-cell-vertical-align: top;
$table-border-factor: 0.2;
$table-border-width: var(--border-width);
$table-border-color: var(--border-color-translucent);
$table-th-padding-x: $table-cell-padding-x;
$table-th-padding-y: 0.5rem;
$table-th-font-weight: null;
$table-th-bg: var(--bg-surface-tertiary);
$table-striped-color: $table-color;
$table-striped-bg-factor: 0.05;
$table-striped-order: even;
$table-striped-bg: var(--bg-surface-tertiary);
$table-striped-columns-order: even;
$table-group-separator-color: var(--border-color-translucent);
$table-active-color: $table-color;
$table-active-bg-factor: 0.1;
$table-active-bg: var(--active-bg);
$table-hover-color: $table-color;
$table-hover-bg-factor: 0.075;
$table-hover-bg: color-mix(in srgb, var(--emphasis-color) #{math.percentage($table-hover-bg-factor)}, transparent);
$table-caption-color: var(--secondary-color);
$table-bg-scale: -80%;
$table-variants: (
'primary': shift-color($primary, $table-bg-scale),
'secondary': shift-color($secondary, $table-bg-scale),
'success': shift-color($success, $table-bg-scale),
'info': shift-color($info, $table-bg-scale),
'warning': shift-color($warning, $table-bg-scale),
'danger': shift-color($danger, $table-bg-scale),
'light': $light,
'dark': $dark,
);
$table-sort-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1'><path d='M5 7l3 -3l3 3'/><path d='M5 10l3 3l3 -3'/></svg>");
$table-sort-asc-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1' d='M5 7l3 3l3 -3'/></svg>");
$table-sort-desc-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1' d='M5 10l3 -3l3 3'/></svg>");
$table-sort-icon-size: 1rem;
$table-sort-icon-margin-start: 0.25rem;Class reference
Every class this component ships, grouped by what it changes. A name in braces stands for a family — {color} is any base color, {breakpoint} any responsive step.
table | component | Container element |
td-truncate | part | Cell whose content is cut with an ellipsis instead of stretching the table |
table-striped | style | Shades every other row |
table-bordered | style | Border on every cell |
table-borderless | style | Removes all borders |
table-transparent | style | Removes the background |
table-responsive | modifier | Scrolls sideways; table-responsive-{breakpoint} only below that width |
table-mobile-{breakpoint} | modifier | Stacks rows into blocks below the breakpoint, using data-label on each cell |
table-vcenter | modifier | Centers cell content vertically |
table-center | modifier | Centers cell content horizontally |
table-nowrap | modifier | Stops cell text from wrapping |
table-hover | behavior | Highlights the row under the cursor |
table-selectable | behavior | Rows with a checkbox, highlighted when checked |
table-sort | behavior | Sortable header cell |
table-{color} | color | Tints a row or a cell, for example table-success |
table-sm | size | Tighter cell padding |
Related
On this page
