<script setup lang="ts">
import { computed } from "vue";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
const demoRows = computed(() => rows.slice(0, 3));
</script>
<template>
<LayerCard style="padding: 0;">
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in demoRows" :key="row.id">
<Table.Cell>{{ row.subject }}</Table.Cell>
<Table.Cell>{{ row.from }}</Table.Cell>
<Table.Cell>{{ row.date }}</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Installation
Barrel
import { Table } from "@dicehub/phi";Granular
import { Table } from "@dicehub/phi/components/table";Usage
<script setup lang="ts">
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
</script>
<template>
<LayerCard style="padding: 0;">
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Name</Table.Head>
<Table.Head>Email</Table.Head>
<Table.Head>Role</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>John Doe</Table.Cell>
<Table.Cell>[email protected]</Table.Cell>
<Table.Cell>Admin</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Examples
With Checkboxes
Add row selection with Table.CheckHead and Table.CheckCell. Both emit @checked-change, which matches the underlying Checkbox state change.
<script setup lang="ts">
import { computed, ref } from "vue";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
const selectedIds = ref(new Set<string>(["2"]));
const visibleRows = computed(() => rows.slice(0, 3));
const allSelected = computed(() => visibleRows.value.every((row) => selectedIds.value.has(row.id)));
const someSelected = computed(() => visibleRows.value.some((row) => selectedIds.value.has(row.id)) && !allSelected.value);
const toggleRow = (id: string) => {
const next = new Set(selectedIds.value);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
selectedIds.value = next;
};
const toggleVisible = () => {
if (allSelected.value) {
selectedIds.value = new Set([...selectedIds.value].filter((id) => !visibleRows.value.some((row) => row.id === id)));
return;
}
selectedIds.value = new Set([...selectedIds.value, ...visibleRows.value.map((row) => row.id)]);
};
</script>
<template>
<LayerCard style="padding: 0;">
<Table>
<Table.Header>
<Table.Row>
<Table.CheckHead
aria-label="Select all rows"
:checked="allSelected"
:indeterminate="someSelected"
@checked-change="toggleVisible"
/>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in visibleRows" :key="row.id">
<Table.CheckCell
:aria-label="`Select ${row.subject}`"
:checked="selectedIds.has(row.id)"
@checked-change="toggleRow(row.id)"
/>
<Table.Cell>{{ row.subject }}</Table.Cell>
<Table.Cell>{{ row.from }}</Table.Cell>
<Table.Cell>{{ row.date }}</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Compact Header
Use variant="compact" on Table.Header for a more condensed header style.
<script setup lang="ts">
import { computed } from "vue";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
const demoRows = computed(() => rows.slice(0, 3));
</script>
<template>
<LayerCard style="padding: 0;">
<Table>
<Table.Header variant="compact">
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in demoRows" :key="row.id">
<Table.Cell>{{ row.subject }}</Table.Cell>
<Table.Cell>{{ row.from }}</Table.Cell>
<Table.Cell>{{ row.date }}</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Selected Row
Use variant="selected" on Table.Row to highlight selected rows.
<script setup lang="ts">
import { computed, ref } from "vue";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
const selectedIds = ref(new Set<string>(["2"]));
const visibleRows = computed(() => rows.slice(0, 3));
const allSelected = computed(() => visibleRows.value.every((row) => selectedIds.value.has(row.id)));
const someSelected = computed(() => visibleRows.value.some((row) => selectedIds.value.has(row.id)) && !allSelected.value);
const toggleRow = (id: string) => {
const next = new Set(selectedIds.value);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
selectedIds.value = next;
};
const toggleVisible = () => {
if (allSelected.value) {
selectedIds.value = new Set([...selectedIds.value].filter((id) => !visibleRows.value.some((row) => row.id === id)));
return;
}
selectedIds.value = new Set([...selectedIds.value, ...visibleRows.value.map((row) => row.id)]);
};
</script>
<template>
<LayerCard style="padding: 0;">
<Table>
<Table.Header>
<Table.Row>
<Table.CheckHead
aria-label="Select all rows"
:checked="allSelected"
:indeterminate="someSelected"
@checked-change="toggleVisible"
/>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row
v-for="row in visibleRows"
:key="row.id"
:variant="selectedIds.has(row.id) ? 'selected' : 'default'"
>
<Table.CheckCell
:aria-label="`Select ${row.subject}`"
:checked="selectedIds.has(row.id)"
@checked-change="toggleRow(row.id)"
/>
<Table.Cell>{{ row.subject }}</Table.Cell>
<Table.Cell>{{ row.from }}</Table.Cell>
<Table.Cell>{{ row.date }}</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Fixed Layout with Column Sizes
For precise control over column widths, set layout="fixed" and use colgroup with col elements.
<script setup lang="ts">
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
</script>
<template>
<LayerCard style="padding: 0;">
<Table layout="fixed">
<colgroup>
<col />
<col style="width: 150px;" />
<col style="width: 150px;" />
</colgroup>
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in rows" :key="row.id">
<Table.Cell>{{ row.subject }}</Table.Cell>
<Table.Cell>{{ row.from }}</Table.Cell>
<Table.Cell>{{ row.date }}</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Sticky Column
Pin a column to the left or right edge with sticky="left" or sticky="right" on Table.Head and Table.Cell.
<script setup lang="ts">
import { Badge } from "@dicehub/phi/components/badge";
import { Button } from "@dicehub/phi/components/button";
import { DropdownMenu } from "@dicehub/phi/components/dropdown";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
import { PhDotsThree, PhEye, PhPencilSimple, PhTrash } from "@phosphor-icons/vue";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
</script>
<template>
<LayerCard style="width: 100%; max-width: 28rem; overflow-x: auto; padding: 0;">
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
<Table.Head>Tags</Table.Head>
<Table.Head sticky="right"><span class="phi-sr-only">Actions</span></Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in rows" :key="row.id">
<Table.Cell style="white-space: nowrap;">{{ row.subject }}</Table.Cell>
<Table.Cell style="white-space: nowrap;">{{ row.from }}</Table.Cell>
<Table.Cell style="white-space: nowrap;">{{ row.date }}</Table.Cell>
<Table.Cell style="white-space: nowrap;">
<span v-if="!row.tags">-</span>
<span v-else style="display: inline-flex; gap: 0.25rem;">
<Badge v-for="tag in row.tags" :key="tag">{{ tag }}</Badge>
</span>
</Table.Cell>
<Table.Cell sticky="right" style="text-align: right;">
<DropdownMenu>
<DropdownMenu.Trigger>
<Button shape="square" size="sm" variant="ghost" :icon="PhDotsThree" aria-label="More options" />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item value="view" :icon="PhEye">View</DropdownMenu.Item>
<DropdownMenu.Item value="edit" :icon="PhPencilSimple">Edit</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item value="delete" :icon="PhTrash" variant="danger">Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Compact Header with Sticky Column
Combining variant="compact" on Table.Header with sticky columns.
<script setup lang="ts">
import { Badge } from "@dicehub/phi/components/badge";
import { Button } from "@dicehub/phi/components/button";
import { DropdownMenu } from "@dicehub/phi/components/dropdown";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
import { PhDotsThree, PhEye, PhPencilSimple, PhTrash } from "@phosphor-icons/vue";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
</script>
<template>
<LayerCard style="width: 100%; max-width: 28rem; overflow-x: auto; padding: 0;">
<Table>
<Table.Header variant="compact">
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
<Table.Head>Tags</Table.Head>
<Table.Head sticky="right"><span class="phi-sr-only">Actions</span></Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in rows" :key="row.id">
<Table.Cell style="white-space: nowrap;">{{ row.subject }}</Table.Cell>
<Table.Cell style="white-space: nowrap;">{{ row.from }}</Table.Cell>
<Table.Cell style="white-space: nowrap;">{{ row.date }}</Table.Cell>
<Table.Cell style="white-space: nowrap;">
<span v-if="!row.tags">-</span>
<span v-else style="display: inline-flex; gap: 0.25rem;">
<Badge v-for="tag in row.tags" :key="tag">{{ tag }}</Badge>
</span>
</Table.Cell>
<Table.Cell sticky="right" style="text-align: right;">
<DropdownMenu>
<DropdownMenu.Trigger>
<Button shape="square" size="sm" variant="ghost" :icon="PhDotsThree" aria-label="More options" />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item value="view" :icon="PhEye">View</DropdownMenu.Item>
<DropdownMenu.Item value="edit" :icon="PhPencilSimple">Edit</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item value="delete" :icon="PhTrash" variant="danger">Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>Full Example
Complete table with checkboxes, badges, action buttons, and fixed column widths.
<script setup lang="ts">
import { ref } from "vue";
import { Badge } from "@dicehub/phi/components/badge";
import { Button } from "@dicehub/phi/components/button";
import { DropdownMenu } from "@dicehub/phi/components/dropdown";
import { LayerCard } from "@dicehub/phi/components/layer-card";
import { Table } from "@dicehub/phi/components/table";
import { PhDotsThree, PhEnvelopeSimple, PhEye, PhPencilSimple, PhTrash } from "@phosphor-icons/vue";
const rows = [
{ id: "1", subject: "Phi v1.0.0 released", from: "Visal In", date: "5 seconds ago" },
{ id: "2", subject: "New Job Offer", from: "Cloudflare", date: "10 minutes ago" },
{ id: "3", subject: "Daily Email Digest", from: "Cloudflare", date: "1 hour ago", tags: ["promotion"] },
{ id: "4", subject: "GitLab - New Comment", from: "Rob Knecht", date: "1 day ago" },
{ id: "5", subject: "Out of Office", from: "Johnnie Lappen", date: "3 days ago" },
];
const selectedIds = ref(new Set<string>(["2"]));
const toggleRow = (id: string) => {
const next = new Set(selectedIds.value);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
selectedIds.value = next;
};
const toggleAll = () => {
selectedIds.value = rows.every((row) => selectedIds.value.has(row.id))
? new Set()
: new Set(rows.map((row) => row.id));
};
</script>
<template>
<LayerCard style="width: 100%; overflow-x: auto; padding: 0;">
<Table layout="fixed">
<colgroup>
<col style="width: 40px;" />
<col />
<col style="width: 150px;" />
<col style="width: 120px;" />
<col style="width: 50px;" />
</colgroup>
<Table.Header>
<Table.Row>
<Table.CheckHead
aria-label="Select all rows"
:checked="rows.every((row) => selectedIds.has(row.id))"
:indeterminate="rows.some((row) => selectedIds.has(row.id)) && !rows.every((row) => selectedIds.has(row.id))"
@checked-change="toggleAll"
/>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
<Table.Head />
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in rows" :key="row.id" :variant="selectedIds.has(row.id) ? 'selected' : 'default'">
<Table.CheckCell
:aria-label="`Select ${row.subject}`"
:checked="selectedIds.has(row.id)"
@checked-change="toggleRow(row.id)"
/>
<Table.Cell>
<span style="display: inline-flex; align-items: center; gap: 0.5rem;">
<PhEnvelopeSimple :size="16" aria-hidden="true" />
<span>{{ row.subject }}</span>
<Badge v-for="tag in row.tags" :key="tag">{{ tag }}</Badge>
</span>
</Table.Cell>
<Table.Cell>{{ row.from }}</Table.Cell>
<Table.Cell>{{ row.date }}</Table.Cell>
<Table.Cell style="text-align: right;">
<DropdownMenu>
<DropdownMenu.Trigger>
<Button shape="square" size="sm" variant="ghost" :icon="PhDotsThree" aria-label="More options" />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item value="view" :icon="PhEye">View</DropdownMenu.Item>
<DropdownMenu.Item value="edit" :icon="PhPencilSimple">Edit</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item value="delete" :icon="PhTrash" variant="danger">Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
</template>API Reference
Table
Root table component. Renders a semantic table element.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
layout | "auto" | "fixed" | "auto" | Table layout algorithm. |
default slot | unknown | - | Table.Header, Table.Body, Table.Footer, colgroup, and other table children. |
className | string | - | Additional CSS classes applied to the table. |
$attrs | TableHTMLAttributes | - | Native table attributes. |
Table.Header
Table header section. Renders thead.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
variant | "default" | "compact" | "default" | Header density and background style. |
sticky | boolean | false | Pins header cells to the top of the scroll container. |
default slot | unknown | - | Table.Row children. |
className | string | - | Additional CSS classes applied to the thead. |
Table.Body
Table body section. Renders tbody.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
default slot | unknown | - | Table.Row children. |
className | string | - | Additional CSS classes applied to the tbody. |
Table.Row
Borderless table row. Default rows alternate between semantic base and elevated backgrounds.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
variant | "default" | "selected" | "default" | Row visual variant. Selected rows always use the semantic tint background. |
default slot | unknown | - | Table.Head, Table.Cell, Table.CheckHead, or Table.CheckCell children. |
className | string | - | Additional CSS classes applied to the tr. |
Table.Head
Header cell. Renders th.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
sticky | "left" | "right" | - | Pins the header cell to an edge of the horizontal scroll container. |
default slot | unknown | - | Header cell content. |
className | string | - | Additional CSS classes applied to the th. |
Table.Cell
Body cell. Renders td.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
sticky | "left" | "right" | - | Pins the cell to an edge of the horizontal scroll container. |
default slot | unknown | - | Cell content. |
className | string | - | Additional CSS classes applied to the td. |
Table.CheckHead
Header cell with checkbox for select-all behavior.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
checked | boolean | - | Controlled checkbox state. |
indeterminate | boolean | - | Renders the mixed checkbox state. |
label | string | "Select all rows" | Accessible label for the checkbox. Native aria-label is also supported. |
disabled | boolean | - | Disables the checkbox. |
className | string | - | Additional CSS classes applied to the th. |
@update:checked | (checked: boolean) => void | - | v-model:checked update event. |
@checked-change | (details: TableCheckboxChangeDetails) => void | - | Emitted when the checkbox state changes. |
@value-change | (checked: boolean) => void | - | Deprecated alias for checked changes. |
Table.CheckCell
Body cell with checkbox for row selection.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
checked | boolean | - | Controlled checkbox state. |
indeterminate | boolean | - | Renders the mixed checkbox state. |
label | string | "Select row" | Accessible label for the checkbox. Native aria-label is also supported. |
disabled | boolean | - | Disables the checkbox. |
className | string | - | Additional CSS classes applied to the td. |
@update:checked | (checked: boolean) => void | - | v-model:checked update event. |
@checked-change | (details: TableCheckboxChangeDetails) => void | - | Emitted when the checkbox state changes. |
@value-change | (checked: boolean) => void | - | Deprecated alias for checked changes. |
Table.ResizeHandle
Button handle for column resizing integrations.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes applied to the button. |
$attrs | ButtonHTMLAttributes | - | Native button attributes and mouse/touch listeners. |
TanStack Table Integration
For advanced features like sorting, filtering, and resizable columns, integrate withTanStack Table. The Table component is designed to work with TanStack's headless API.
<script setup lang="ts" generic="TData">
import { FlexRender, getCoreRowModel, useVueTable, type ColumnDef } from "@tanstack/vue-table";
import { Table } from "@dicehub/phi";
const props = defineProps<{
data: TData[];
columns: ColumnDef<TData>[];
}>();
const table = useVueTable<TData>({
get data() {
return props.data;
},
get columns() {
return props.columns;
},
getCoreRowModel: getCoreRowModel(),
columnResizeMode: "onChange",
});
</script>
<template>
<Table layout="fixed">
<colgroup>
<col
v-for="column in table.getAllColumns()"
:key="column.id"
:style="{ width: `${column.getSize()}px` }"
/>
</colgroup>
<Table.Header>
<Table.Row v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<Table.Head v-for="header in headerGroup.headers" :key="header.id">
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
<Table.ResizeHandle
@mousedown="header.getResizeHandler()($event)"
@touchstart="header.getResizeHandler()($event)"
/>
</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row v-for="row in table.getRowModel().rows" :key="row.id">
<Table.Cell v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</template>Accessibility
Semantic HTML
Table uses semantic table, thead, tbody, th, and td elements for screen reader navigation.
Checkbox Labels
Always provide aria-label or label for Table.CheckHead and Table.CheckCell.
Keyboard Navigation
Tab moves focus through interactive elements. Checkboxes respond to Space.