Table
Basic Usage
Role Name | Date | Status |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | Success |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
<template>
<spr-table action :headers="headers" :data-table="data">
<div>Customize your content here!</div>
<template #action="{ row }">
<spr-lozenge :label="row.status.title" :tone="row.status.title.toLowerCase()" />
</template>
<template #action-name> Status </template>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headers = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: true, hasSubtext: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Shift',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
]);
</script>
No Slot and Action
Role Name | Date |
---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
<template>
<spr-table :headers="headers" :data-table="data" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headers = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: true, hasSubtext: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Shift',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
]);
</script>
Table Actions
The implementation for the search field here is to emit and trigger an API call to update the data-table
Role Name | Date | |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
<template>
<spr-table
action
:headers="headers"
:data-table="data"
:table-actions="tableActions"
v-model:searchModel="searchModel"
/>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const headers = ref([
// Fill this with your headers
]);
const data = ref([
// Fill this with your data
]);
const tableActions = ref({
// Toggle this to true if you need search field
search: true,
// Toggle this to t rue if you need filter field
filter: true,
// Toggle this to true if you need option field
option: true,
});
const searchModel = ref('');
watch(searchModel, (newValue) => {
console.log(newValue);
// On change of the search variable trigger a fetch
// fetchApiHere();
});
</script>
Table Action Slot
Role Name | Date | |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
<template>
<spr-table
action
:headers="headers"
:data-table="data"
:table-actions="tableActions"
v-model:searchModel="searchModel"
>
<div>
<div class="spr-text-color-strong spr-font-size-400 spr-font-weight-medium">Table Name</div>
<div>table description</div>
</div>
<template #tableActionSection>
<spr-button>Button</spr-button>
</template>
</spr-table>
</template>
Multi Select
Allows the selection of multiple or all rows in the table.
WARNING
- If table is paginated, only the rows on the current page will be selected.
- selectedKeyId prop must be provided.
- title value of the table data object mapped with selectedKeyId in the table data must be unique.
TIP
Set the prop returnCompleteSelectedProperties to false to return the selected data as an array of objects with the selectedKeyId value only.
[]
Role Name | Date | Status | |
---|---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | Success | |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending | |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending | |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending | |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending | |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
<template>
<spr-table
action
:headers="headers"
:data-table="data"
variant="white"
:is-multi-select="true"
:selected-key-id="'name'"
:return-complete-selected-properties="false"
@update:selectedData="handleSelectedData"
>
<template #action="{ row }">
<spr-lozenge :label="row.status.title" :tone="row.status.title.toLowerCase()" />
</template>
<template #action-name> Status </template>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const selectedTableData = ref([]);
const headers = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: true, hasSubtext: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Shift',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
{
name: {
title: 'Shift 1',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 01, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Pending',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
{
name: {
title: 'Shift 2',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 01, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Pending',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
{
name: {
title: 'Shift 3',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 01, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Pending',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
{
name: {
title: 'Shift 4',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 01, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Pending',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
{
name: {
title: 'Shift 5',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 01, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Pending',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
]);
const handleSelectedData = (data) => {
// do anything with the selected data
selectedTableData.value = [...data];
}
</script>
Custom Column
You can customize the column of the table by using the dynamically named slot column field (the field property in the headers object).
Role Name | Date | |
---|---|---|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis autem minus fugiat culpa ad magnam nisi ex facilis ducimus sit neque modi porro optio cupiditate iusto, blanditiis asperiores aperiam veritatis mollitia laboriosam? Consectetur, deserunt? Reprehenderit ipsa, debitis eaque accusamus ducimus quasi deserunt laborum asperiores ea, nemo, optio corporis rerum! Veniam ex voluptatibus eveniet consequuntur saepe doloribus sint laboriosam eligendi sequi esse vero, quam consectetur iste inventore aliquam soluta quibusdam at perferendis ratione, voluptatum accusantium amet dignissimos perspiciatis. Aspernatur, voluptate amet. | Nov 30, 2025 | |
Shift 1 | Nov 01, 2025 |
<template>
<spr-table
action
:headers="headers"
:data-table="data"
>
<template #name="{ row }"> //slot name is the field property value in the headers object
<spr-tooltip :has-max-width="true" :text="String(row.name.title)">
<div
ref="rowTitleRef"
class="spr-truncate spr-text-sm"
:style="`max-width: ${getCellMaxWidth}`"
>
{{ row.name.title }}
</div>
</spr-tooltip>
<span class="spr-text-color-base spr-text-xs spr-font-normal">{{ row.name.subtext }}</span>
</template>
<template #action>
<div class="spr-flex spr-flex-auto spr-justify-end spr-gap-2">
<spr-button variant="primary" tone="success" size="small"> Action 1 </spr-button>
<spr-button variant="primary" tone="danger" size="small"> Action 2 </spr-button>
</div>
</template>
</spr-table>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
const rowTitleRef = ref<HTMLElement|null>(null);
const cellWidth = ref(0)
const getCellMaxWidth = computed(() => {
return cellWidth.value > 0 ? `${cellWidth.value}px` : '100%'
})
const headers = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: true, hasSubtext: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis autem minus fugiat culpa ad magnam nisi ex facilis ducimus sit neque modi porro optio cupiditate iusto, blanditiis asperiores aperiam veritatis mollitia laboriosam? Consectetur, deserunt? Reprehenderit ipsa, debitis eaque accusamus ducimus quasi deserunt laborum asperiores ea, nemo, optio corporis rerum! Veniam ex voluptatibus eveniet consequuntur saepe doloribus sint laboriosam eligendi sequi esse vero, quam consectetur iste inventore aliquam soluta quibusdam at perferendis ratione, voluptatum accusantium amet dignissimos perspiciatis. Aspernatur, voluptate amet.',
subtext: 'Lorem ipsectetur adipiscing elit.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
{
name: {
title: 'Shift 1',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 01, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Pending',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
}
]);
</script>
Custom Tailwind Classes for Headers
You can customize the header cells with Tailwind CSS classes by using the customTailwindClasses
property in the header configuration.
WARNING
Utilizing this field will remove all of the existing tailwind classes on the header only (th) the classes of the main DIV which holds the name, icon and badge will still take effect. Additionally, the only tested tailwind classes for this is the spr- tailwind classes. If you're using your own tailwind configurations, the component may not behave as expected. Additionally, changing the class of the multi-select header is currently not supported.
Role Name | Date |
---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
<template>
<spr-table :headers="headers" :data-table="data">
<template #header="{ column }">
<div :class="column.customTailwindClasses">
{{ column.name }}
</div>
</template>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headers = ref([
{
field: 'name',
name: 'Role Name',
sort: true,
hasAvatar: true,
hasSubtext: true,
customTailwindClasses: 'spr-bg-tomato-500'
},
{
field: 'lastUpdate',
name: 'Date',
sort: true,
hasAvatar: false,
hasSubtext: false,
customTailwindClasses: 'spr-bg-blueberry-500'
},
]);
</script>
Table Footer
The implementation for the pagination here is to emit and trigger an API call to update the data-table
Role Name | Date | |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
<template>
<spr-table action :headers="headers" :data-table="data">
<template #footer>
<spr-table-pagination
v-model:selected-row-count="selectedRowCount"
:total-items="totalItems"
:current-page="currentPage"
:dropdown-selection="dropdownSelection"
@previous="handlePrevious"
@next="handleNext"
:version="'backend'"
/>
</template>
</spr-table>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const headers = ref([
// Fill this with your headers
]);
const data = ref([
// Fill this with your data
]);
const totalItems = ref(100);
const currentPage = ref(2);
const dropdownSelection = [
{ text: 10, value: 10 },
{ text: 20, value: 20 },
{ text: 30, value: 30 },
];
const selectedRowCount = ref(10);
watch(selectedRowCount, (newValue) => {
console.log('Selected Row Count:', newValue);
// On change of the selectedRowCount variable trigger a fetch
// fetchApiHere();
});
const handlePrevious = () => {
console.log('clicked previous');
if (currentPage.value > 1) {
currentPage.value--;
}
fetchItems(currentPage.value);
};
const handleNext = () => {
console.log('clicked next');
if (currentPage.value * selectedRowCount.value < totalItems.value) {
currentPage.value++;
}
fetchItems(currentPage.value);
};
const fetchItems = computed((page) => {
const response = apiCall(page);
data.value = parse(response);
});
</script>
Cells
Lozenge
You can change the title of a cell to a lozenge
Role Name | Date | |
---|---|---|
![]() Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | ![]() |
<template>
<spr-table action :headers="headersLozenge" :data-table="dataLozenge" variant="surface" @onSort="handleSort">
<div>Customize your content here!</div>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headersLozenge = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: false, hasSubtext: true, hasLozengeTitle: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false, hasLozengeTitle: true },
]);
const lozengeTitle = {
title: 'Active',
tone: 'success',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
fill: true,
lozengeIcon: 'ph:building'
}
const lozengeSecondTitle = {
title: 'Lozenge',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
}
const dataLozenge = [
{
name: {
title: lozengeTitle,
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
lozengeIcon: 'ph:user',
},
lastUpdate: {
title: lozengeSecondTitle,
subtext: 'Lorem ipsum dolor ',
lozengeFill: true,
lozengeAvatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
];
</script>
Chips
You can change the title of a cell to a chip
Role Name | Date | |
---|---|---|
![]() Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | ![]() 2 |
<template>
<spr-table action :headers="headersChips" :data-table="dataChips" variant="surface" @onSort="handleSort">
<div>
Customize your content here!
</div>
</spr-table>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
const headersChips = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: false, hasSubtext: true, hasChipTitle: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false, hasChipTitle: true },
])
const chipsTitle = {
title: 'Active',
icon: 'ph:building',
iconWeight: 'regular',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
avatarVariant: 'image',
}
const chipsSecondTitle = {
title: 'Second Title',
icon: 'ph:users-three',
iconWeight: 'regular',
badge: true,
badgeText: '2',
badgeVariant: 'brand',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
avatarVariant: 'image',
}
const dataChips = [
{
name: {
title: chipsTitle,
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
lozengeIcon: 'ph:user'
},
lastUpdate: {
title: chipsSecondTitle,
subtext: 'Lorem ipsum dolor ',
lozengeFill: true,
lozengeAvatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg'
},
},
]
</script>
Multiple Chips and Lozenges
You can change the title to multiple chips and lozenges
Role Name | Date | |
---|---|---|
![]() ![]() Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | ![]() 2 ![]() |
<template>
<spr-table action :headers="headersMultiple" :data-table="dataMultiple" variant="surface" @onSort="handleSort">
<div>
Customize your content here!
</div>
</spr-table>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
const headersMultiple = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: false, hasSubtext: true, hasLozengeTitle: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false, hasChipTitle: true },
])
const lozengeCell = {
title: 'Active',
tone: 'success',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
fill: true,
lozengeIcon: 'ph:building',
};
const chipCell = [
{
title: 'Active',
icon: 'ph:building',
iconWeight: 'regular',
badge: true,
badgeText: '2',
badgeVariant: 'brand',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
avatarVariant: 'image',
},
{
title: 'Active',
icon: 'ph:building',
iconWeight: 'regular',
badge: true,
badgeText: '2',
badgeVariant: 'brand',
avatarUrl: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
avatarVariant: 'image',
}
]
const dataMultiple = [
{
name: {
title: lozengeCell,
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: chipCell,
subtext: 'Lorem ipsum dolor ',
chipTitle: chipCell
},
},
]
</script>
Variant
You can customize the header background of the table using the variant
attribute. The available options are white
and surface
.
Role Name | Date | |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 |
<template>
<spr-table action :headers="headers" :data-table="data">
<div>Customize your content here!</div>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headers = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: true, hasSubtext: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Shift',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
]);
const handleSort = (e) => {
console.log('sort', e);
};
</script>
Sorting
Role Name | Date | Status |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | Success |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
<template>
<spr-table action :headers="headers" :data-table="data" variant="white" @onSort="getSortOrder()" sortOrder>
<div>Customize your content here!</div>
<template #action="{ row }">
<spr-lozenge :label="row.status.title" :tone="row.status.title.toLowerCase()" />
</template>
<template #action-name> Status </template>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headers = ref([
{ field: 'name', name: 'Role Name', sort: true, hasAvatar: true, hasSubtext: true },
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Shift',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
]);
const sortOrder = ref('asc');
const getSortOrder = () => {
sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc';
console.log(sortOrder.value);
return sortOrder.value;
};
</script>
Badge
Badges can have different color schemes to indicate various statuses, such as disabled
, brand
, danger
, or information
.
Role Name 1 | Date 2 | Status |
---|---|---|
S Shift Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 30, 2025 | Success |
S1 Shift 1 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S2 Shift 2 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S3 Shift 3 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S4 Shift 4 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
S5 Shift 5 Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam. | Nov 01, 2025 | Pending |
<template>
<spr-table action :headers="headersWithBadge" :data-table="data">
<div>Customize your content here!</div>
<template #action="{ row }">
<spr-lozenge :label="row.status.title" :tone="row.status.title.toLowerCase()" />
</template>
<template #action-name> Status </template>
</spr-table>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const headersWithBadge = ref([
{
field: 'name',
name: 'Role Name',
sort: true,
hasAvatar: true,
hasSubtext: true,
badgeText: '1',
badgeVariant: 'brand',
},
{ field: 'lastUpdate', name: 'Date', sort: true, hasAvatar: false, hasSubtext: false },
]);
const data = ref([
{
name: {
title: 'Shift',
subtext: 'Lorem ipsectetur adipiscing elit. Sed etiam, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
lastUpdate: {
title: 'Nov 30, 2025',
subtext: 'Lorem ipsum dolor ',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
status: {
title: 'Success',
subtext: 'Lorem ipsum dolor sit amet, consectetur, sed etiam.',
image: 'https://media.sproutsocial.com/uploads/2022/06/profile-picture.jpeg',
},
},
]);
</script>
API Reference
Props
Name | Description | Type | Default |
---|---|---|---|
action | Enables an action column at the end of the table. When set to true, it allows custom content to be placed in an action column for each row. | boolean | false |
dataTable | Array of data objects to display in the table. Each object represents a row, with properties corresponding to columns defined in the headers. | Array<TableData> | [] |
headers | Array of header configuration objects defining the columns of the table. Each header object includes field name, display name, and various display options. | Array<Header> | [] |
emptyState | Configuration for the empty state displayed when the table has no data. Customize description, sub-description, image, and size. | EmptyState | { description: 'No results found', subDescription: 'Try a different search term', image: 'location', size: 'large' } |
loading | When set to true, displays a loading state instead of the table data or empty state. | boolean | false |
tableActions | Configuration for table actions displayed above the table. Includes options for search, filter, and additional options. | { search: boolean, filter: boolean, option: boolean } | { search: false, filter: false, option: false } |
searchModel | Used with v-model:searchModel to bind the search input value. When using the search feature, this prop enables two-way binding. | string | '' |
sortOrder | Default sort order for the table. Can be 'asc' (ascending) or 'desc' (descending). | 'asc' | 'desc' | 'asc' |
variant | Controls the background color of the table header. Options are 'white' and 'surface'. | 'surface' | 'white' | 'surface' |
isRowClickable | When true, enables clicking on rows to trigger the onRowClick event. Useful for interactive tables where rows can be selected or expanded. | boolean | false |
fullHeight | When true, the table will take up the full available height of its container. | boolean | true |
removeHeaderOnEmpty | When true, the table headers will not be displayed if the table has no data. | boolean | false |
isMultiSelect | Enables multi-select functionality with checkboxes in the first column of the table. | boolean | false |
selectedKeyId | Specifies which key in the table data object will be used as an identifier for selected rows. Required when using isMultiSelect. | string | '' |
returnCompleteSelectedProperties | Controls the structure of the data emitted when rows are selected. If true, the full row data object is emitted; if false, only the value of the property specified by selectedKeyId is emitted. | boolean | false |
Events
Name | Description | Parameters |
---|---|---|
update:searchModel | Emitted when the search input value changes. Used with v-model:searchModel for two-way binding. | (value: string) |
onSort | Emitted when a sortable column header is clicked. Provides the field name and the new sort order. | ({ field: string, sortOrder: 'asc' | 'desc' }) |
onRowClick | Emitted when a row is clicked (if isRowClickable is true). Provides the row data and row index. | (rowData: TableData, rowIndex: number) |
onHover | Emitted when the mouse enters or leaves a row. Provides the active state and the row data. | ({ active: boolean, data: TableData }) |
update:selectedData | Emitted when row selection changes in multi-select mode. Provides an array of selected items based on the returnCompleteSelectedProperties setting. | (selectedItems: TableData[] | any[]) |
Slots
Name | Description | Props |
---|---|---|
default | Content displayed above the table. Typically used for a title or description. | - |
tableActionSection | Custom content for the table actions area. Typically used to add buttons or other controls next to the search, filter, and options. | - |
action | Content for the action column in each row. Only available when the action prop is true. | { row: TableData } |
action-name | Header content for the action column. Only available when the action prop is true. | - |
empty-state | Custom content to display when the table has no data. | - |
loading | Custom loading state content. Displayed when the loading prop is true. | - |
footer | Content displayed at the bottom of the table. Typically used for pagination or summary information. | - |
[field] | Dynamic slots based on the field names in the headers. Allows custom rendering for specific columns. | { row: TableData, rowIndex: number } |
Header Object Properties
Name | Description | Type | Required |
---|---|---|---|
field | Unique identifier for the column. Maps to the property name in the data objects. | string | Yes |
name | Display name for the column header. | string | Yes |
sort | Whether the column is sortable. | boolean | No |
hasAvatar | Whether to display an avatar in the column cells. If true, the data object should include an image property. | boolean | No |
hasIcon | Whether to display an icon in the column cells. If true, the data object should include an icon property. | boolean | No |
hasSubtext | Whether to display subtext in the column cells. If true, the data object should include a subtext property. | boolean | No |
hasLozengeTitle | Whether to display the title as a lozenge. If true, the title property should be a LozengeTitle object or array. | boolean | No |
hasChipTitle | Whether to display the title as a chip. If true, the title property should be a ChipTitle object or array. | boolean | No |
badgeText | Text to display in a badge next to the column header. | string | No |
badgeVariant | Variant of the badge to display next to the column header. Options include 'disabled', 'brand', 'danger', and 'information'. | string | No |
avatarVariant | Variant of the avatar to display in the column cells. Used with hasAvatar. | string | No |
customTailwindClasses | Custom Tailwind CSS classes to apply to the column cells. | string | No |