data
Table
Display data in a table.
Usage
Use the rows
prop to set the data to display in the table. By default, the table will display all the fields of the rows.
Id | Name | Title | Role | |
---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<script setup>const people = [{ id: 1, name: 'Lindsay Walton', title: 'Front-end Developer', email: 'lindsay.walton@example.com', role: 'Member'}, { id: 2, name: 'Courtney Henry', title: 'Designer', email: 'courtney.henry@example.com', role: 'Admin'}, { id: 3, name: 'Tom Cook', title: 'Director of Product', email: 'tom.cook@example.com', role: 'Member'}, { id: 4, name: 'Whitney Francis', title: 'Copywriter', email: 'whitney.francis@example.com', role: 'Admin'}, { id: 5, name: 'Leonard Krasner', title: 'Senior Designer', email: 'leonard.krasner@example.com', role: 'Owner'}, { id: 6, name: 'Floyd Miles', title: 'Principal Designer', email: 'floyd.miles@example.com', role: 'Member'}]</script><template> <UTable :rows="people" /></template>
Columns
Use the columns
prop to configure which columns to display. It's an array of objects with the following properties:
label
- The label to display in the table header. Can be changed through thecolumn-attribute
prop.key
- The field to display from the row data.sortable
- Whether the column is sortable. Defaults tofalse
.direction
- The sort direction to use on first click. Defaults toasc
.class
- The class to apply to the column cells.
ID | User name | Job position | ||
---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<script setup>const columns = [{ key: 'id', label: 'ID'}, { key: 'name', label: 'User name'}, { key: 'title', label: 'Job position'}, { key: 'email', label: 'Email'}, { key: 'role'}]const people = [...]</script><template> <UTable :columns="columns" :rows="people" /></template>
You can easily use the SelectMenu component to change the columns to display.
ID | Name | Title | Role | |
---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<script setup>const columns = [{ key: 'id', label: 'ID'}, { key: 'name', label: 'Name'}, { key: 'title', label: 'Title'}, { key: 'email', label: 'Email'}, { key: 'role', label: 'Role'}]const selectedColumns = ref([...columns])const people = [...]</script><template> <div> <USelectMenu v-model="selectedColumns" :options="columns" multiple placeholder="Columns" /> <UTable :columns="selectedColumns" :rows="people" /> </div></template>
Sortable
You can make the columns sortable by setting the sortable
property to true
in the column configuration.
ID | Role | |||
---|---|---|---|---|
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
<script setup>const columns = [{ key: 'id', label: 'ID'}, { key: 'name', label: 'Name', sortable: true}, { key: 'title', label: 'Title', sortable: true}, { key: 'email', label: 'Email', sortable: true, direction: 'desc'}, { key: 'role', label: 'Role'}]const people = [...]</script><template> <UTable :columns="columns" :rows="people" :sort="{ column: 'title' }" /></template>
You can specify the default direction of each column through the direction
property. It can be either asc
or desc
and defaults to asc
.
You can specify a default sort for the table through the sort
prop. It's an object with the following properties:
column
- The column to sort by.direction
- The sort direction. Can be eitherasc
ordesc
and defaults toasc
.
sortable
.Use the sort-button
prop to customize the sort button in the header. You can pass all the props of the Button component to customize it through this prop or globally through ui.table.default.sortButton
. Its icon defaults to i-heroicons-arrows-up-down-20-solid
.
ID | Role | |||
---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<UTable sort-asc-icon="i-heroicons-arrow-up-20-solid" sort-desc-icon="i-heroicons-arrow-down-20-solid" :sort-button="{ icon: 'i-heroicons-sparkles-20-solid', color: 'primary', variant: 'outline', size: '2xs', square: false, ui: { rounded: 'rounded-full' } }" class="w-full" :columns="[{ key: 'id', label: 'ID' }, { key: 'name', label: 'Name', sortable: true }, { key: 'title', label: 'Title', sortable: true }, { key: 'email', label: 'Email', sortable: true }, { key: 'role', label: 'Role' }]" :rows="[{ id: 1, name: 'Lindsay Walton', title: 'Front-end Developer', email: 'lindsay.walton@example.com', role: 'Member' }, { id: 2, name: 'Courtney Henry', title: 'Designer', email: 'courtney.henry@example.com', role: 'Admin' }, { id: 3, name: 'Tom Cook', title: 'Director of Product', email: 'tom.cook@example.com', role: 'Member' }, { id: 4, name: 'Whitney Francis', title: 'Copywriter', email: 'whitney.francis@example.com', role: 'Admin' }, { id: 5, name: 'Leonard Krasner', title: 'Senior Designer', email: 'leonard.krasner@example.com', role: 'Owner' }, { id: 6, name: 'Floyd Miles', title: 'Principal Designer', email: 'floyd.miles@example.com', role: 'Member' }]"/>
Use the sort-asc-icon
prop to set a different icon or change it globally in ui.table.default.sortAscIcon
. Defaults to i-heroicons-bars-arrow-up-20-solid
.
Use the sort-desc-icon
prop to set a different icon or change it globally in ui.table.default.sortDescIcon
. Defaults to i-heroicons-bars-arrow-down-20-solid
.
Selectable
Use a v-model
to make the table selectable. The v-model
will be an array of the selected rows.
Id | Name | Title | Role | ||
---|---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member | |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin | |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member | |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin | |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner | |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<script setup>const people = [...]const selected = ref([people[1]])</script><template> <UTable v-model="selected" :rows="people" /></template>
by
prop to compare objects by a field instead of comparing object instances. We've replicated the behavior of Headless UI Combobox.You can also add a select
listener on your Table to make the rows clickable. The function will receive the row as the first argument.
You can use this to navigate to a page, open a modal or even to select the row manually.
Id | Name | Title | Role | ||
---|---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member | |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin | |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member | |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin | |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
<script setup>const people = [...]function select (row) { const index = selected.value.findIndex((item) => item.id === row.id) if (index === -1) { selected.value.push(row) } else { selected.value.splice(index, 1) }}const selected = ref([people[1]])</script><template> <UTable v-model="selected" :rows="people" @select="select" /></template>
Searchable
You can easily use the Input component to filter the rows.
ID | Name | Title | Role | |
---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
6 | Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<script setup>const people = [...]const q = ref('')const filteredRows = computed(() => { if (!q.value) { return people } return people.filter((person) => { return Object.values(person).some((value) => { return String(value).toLowerCase().includes(q.value.toLowerCase()) }) })})</script><template> <div> <UInput v-model="q" placeholder="Filter people..." /> <UTable :rows="filteredRows" /> </div></template>
Paginable
You can easily use the Pagination component to paginate the rows.
Id | Name | Title | Role | |
---|---|---|---|---|
1 | Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member |
2 | Courtney Henry | Designer | courtney.henry@example.com | Admin |
3 | Tom Cook | Director of Product | tom.cook@example.com | Member |
4 | Whitney Francis | Copywriter | whitney.francis@example.com | Admin |
5 | Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner |
<script setup>const people = [...]const page = ref(1)const pageCount = 5const rows = computed(() => { return people.slice((page.value - 1) * pageCount, (page.value) * pageCount)})</script><template> <div> <UTable :rows="rows" /> <UPagination v-model="page" :page-count="pageCount" :total="people.length" /> </div></template>
Loading
Use the loading
prop to display a loading state.
Use the loading-state
prop to customize the icon
and label
or change them globally in ui.table.default.loadingState
.
You can also set it to null
to hide the loading state.
ID | Name | Title | Role | |
---|---|---|---|---|
Loading... |
<UTable loading :loading-state="{ icon: 'i-heroicons-arrow-path-20-solid', label: 'Loading...' }" class="w-full" :columns="[{ key: 'id', label: 'ID' }, { key: 'name', label: 'Name' }, { key: 'title', label: 'Title' }, { key: 'email', label: 'Email' }, { key: 'role', label: 'Role' }]"/>
This can be easily used with Nuxt useAsyncData
composable.
<script setup>const columns = [...]const { pending, data: people } = await useLazyAsyncData('people', () => $fetch('/api/people'))</script><template> <UTable :rows="people" :columns="columns" :loading="pending" /></template>
Empty
An empty state will be displayed when there are no results.
Use the empty-state
prop to customize the icon
and label
or change them globally in ui.table.default.emptyState
.
You can also set it to null
to hide the empty state.
ID | Name | Title | Role | |
---|---|---|---|---|
No items. |
<UTable :empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'No items.' }" class="w-full" :columns="[{ key: 'id', label: 'ID' }, { key: 'name', label: 'Name' }, { key: 'title', label: 'Title' }, { key: 'email', label: 'Email' }, { key: 'role', label: 'Role' }]"/>
Styling New
You can apply styles to tr
and td
elements by passing a class
to rows.
Also, you can apply styles to th
elements by passing a class
to columns.
# | Quantity | Name |
---|---|---|
1 | 100 | Apple |
2 | 0 | Orange |
3 | 30 | Banana |
4 | 5 | Mango |
<script setup>const columns = [{ key: 'id', label: '#'}, { key: 'quantity', label: 'Quantity', class: 'italic' // Apply style to column header}, { key: 'name', label: 'Name'}]const items = [{ id: 1, name: 'Apple', quantity: { value: 100, class: 'bg-green-500/50 dark:bg-green-400/50' } // Apply style to td}, { id: 2, name: 'Orange', quantity: { value: 0 }, class: 'bg-red-500/50 dark:bg-red-400/50 animate-pulse' // Apply style to tr}, { id: 3, name: 'Banana', quantity: { value: 30, class: 'bg-green-500/50 dark:bg-green-400/50' }}, { id: 4, name: 'Mango', quantity: { value: 5, class: 'bg-green-500/50 dark:bg-green-400/50' }}]</script><template> <UTable :rows="items" :columns="columns"> <template #quantity-data="{ row }"> {{ row.quantity.value }} </template> </UTable></template>
Slots
You can use slots to customize the header and data cells of the table.
<column>-header
Use the #<column>-header
slot to customize the header cell of a column. You will have access to the column
, sort
and on-sort
properties in the slot scope.
The sort
property is an object with the following properties:
field
- The field to sort by.direction
- The direction to sort by. Can beasc
ordesc
.
The on-sort
property is a function that you can call to sort the table and accepts the column as parameter.
<column>-data
Use the #<column>-data
slot to customize the data cell of a column. You will have access to the row
, column
and getRowData
properties in the slot scope.
You can for example create an extra column for actions with a Dropdown component inside or change the color of the rows based on a selection.
Name | Title | Role | |||
---|---|---|---|---|---|
Lindsay Walton | Front-end Developer | lindsay.walton@example.com | Member | ||
Courtney Henry | Designer | courtney.henry@example.com | Admin | ||
Tom Cook | Director of Product | tom.cook@example.com | Member | ||
Whitney Francis | Copywriter | whitney.francis@example.com | Admin | ||
Leonard Krasner | Senior Designer | leonard.krasner@example.com | Owner | ||
Floyd Miles | Principal Designer | floyd.miles@example.com | Member |
<script setup>const columns = [..., { key: 'actions'}]const people = [...]const items = (row) => [ [{ label: 'Edit', icon: 'i-heroicons-pencil-square-20-solid', click: () => console.log('Edit', row.id) }, { label: 'Duplicate', icon: 'i-heroicons-document-duplicate-20-solid' }], [{ label: 'Archive', icon: 'i-heroicons-archive-box-20-solid' }, { label: 'Move', icon: 'i-heroicons-arrow-right-circle-20-solid' }], [{ label: 'Delete', icon: 'i-heroicons-trash-20-solid' }]]const selected = ref([people[1]])</script><template> <UTable v-model="selected" :rows="people" :columns="columns"> <template #name-data="{ row }"> <span :class="[selected.find(person => person.id === row.id) && 'text-primary-500 dark:text-primary-400']">{{ row.name }}</span> </template> <template #actions-data="{ row }"> <UDropdown :items="items(row)"> <UButton color="gray" variant="ghost" icon="i-heroicons-ellipsis-horizontal-20-solid" /> </UDropdown> </template> </UTable></template>
loading-state
Use the #loading-state
slot to customize the loading state.
Name | Title | Role | ||
---|---|---|---|---|
<script setup>const columns = [...]const people = []const pending = ref(true)</script><template> <UTable :rows="people" :columns="columns" :loading="pending"> <template #loading-state> <div class="flex items-center justify-center h-32"> <i class="loader --6" /> </div> </template> </UTable></template><style scoped>/* https://codepen.io/jenning/pen/YzNmzaV */</style>
empty-state
Use the #empty-state
slot to customize the empty state.
Name | Title | Role | ||
---|---|---|---|---|
No one here! |
<script setup>const columns = [...]const people = [...]</script><template> <UTable :rows="people" :columns="columns"> <template #empty-state> <div class="flex flex-col items-center justify-center py-6 gap-3"> <span class="italic text-sm">No one here!</span> <UButton label="Add people" /> </div> </template> </UTable></template>
Props
{}
undefined
null
[]
defaultComparator
config.default.loadingState
config.default.emptyState
null
"label"
config.default.sortButton as Button
config.default.sortAscIcon
config.default.sortDescIcon
false
Preset
{ "wrapper": "relative overflow-x-auto", "base": "min-w-full table-fixed", "divide": "divide-y divide-gray-300 dark:divide-gray-700", "thead": "", "tbody": "divide-y divide-gray-200 dark:divide-gray-800", "tr": { "base": "", "selected": "bg-gray-50 dark:bg-gray-800/50", "active": "hover:bg-gray-50 dark:hover:bg-gray-800/50 cursor-pointer" }, "th": { "base": "text-left rtl:text-right", "padding": "px-3 py-3.5", "color": "text-gray-900 dark:text-white", "font": "font-semibold", "size": "text-sm" }, "td": { "base": "whitespace-nowrap", "padding": "px-3 py-4", "color": "text-gray-500 dark:text-gray-400", "font": "", "size": "text-sm" }, "checkbox": { "padding": "ps-4" }, "loadingState": { "wrapper": "flex flex-col items-center justify-center flex-1 px-6 py-14 sm:px-14", "label": "text-sm text-center text-gray-900 dark:text-white", "icon": "w-6 h-6 mx-auto text-gray-400 dark:text-gray-500 mb-4 animate-spin" }, "emptyState": { "wrapper": "flex flex-col items-center justify-center flex-1 px-6 py-14 sm:px-14", "label": "text-sm text-center text-gray-900 dark:text-white", "icon": "w-6 h-6 mx-auto text-gray-400 dark:text-gray-500 mb-4" }, "default": { "sortAscIcon": "i-heroicons-bars-arrow-up-20-solid", "sortDescIcon": "i-heroicons-bars-arrow-down-20-solid", "sortButton": { "icon": "i-heroicons-arrows-up-down-20-solid", "trailing": true, "square": true, "color": "gray", "variant": "ghost", "class": "-m-1.5" }, "loadingState": { "icon": "i-heroicons-arrow-path-20-solid", "label": "Loading..." }, "emptyState": { "icon": "i-heroicons-circle-stack-20-solid", "label": "No items." } }}