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 = 5
const 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."
}
}
}