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-attributeprop.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 eitherascordescand 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' } }" />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...' }" />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.' }" />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 beascordesc.
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." } }}