A responsive table component.

Basic
A list of your recent invoices.
Invoice Status Method Amount
INV001 Paid Credit Card $250.00
INV002 Pending PayPal $150.00
INV003 Unpaid Bank Transfer $350.00
With Footer
A list of your recent invoices.
Invoice Status Method Amount
INV001 Paid Credit Card $250.00
INV002 Pending PayPal $150.00
INV003 Unpaid Bank Transfer $350.00
Total $2,500.00
Simple
Name Email Role
Sarah Chen sarah.chen@acme.com Admin
Marc Rodriguez marcus.rodriguez@acme.com User
Emily Watson emily.watson@acme.com User
With Badges
Task Status Priority
Design homepage Completed High
Implement API In Progress Medium
Write tests Pending Low
With Actions
Product Price Actions
Wireless Mouse $29.99
Mechanical Keyboard $129.99
USB-C Hub $49.99
With Select
Task Assignee Status
Design homepage In Progress
Implement API Pending
Write tests Not Started
With Input
Product Quantity Price
Wireless Mouse $29.99
Mechanical Keyboard $129.99
USB-C Hub $49.99

Installation

Install the dependencies

pnpm add cn class-variance-authority

Copy the component

Copy the following files into src/lib/components/ui/table.

src/lib/components/ui/table/Table.svelte
<script lang="ts">
  import type { HTMLTableAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLTableAttributes = $props();
</script>
<div data-slot="table-container" class="relative w-full overflow-x-auto">
  <table data-slot="table" {...props} class={cn('w-full caption-bottom text-sm', classProp)}>
    {@render children?.()}
  </table>
</div>
src/lib/components/ui/table/TableBody.svelte
<script lang="ts">
  import type { HTMLAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLAttributes<HTMLTableSectionElement> = $props();
</script>
<tbody data-slot="table-body" {...props} class={cn('[&_tr:last-child]:border-0', classProp)}>
  {@render children?.()}
</tbody>
src/lib/components/ui/table/TableCaption.svelte
<script lang="ts">
  import type { HTMLAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLAttributes<HTMLElement> = $props();
</script>
<caption data-slot="table-caption" {...props} class={cn('text-muted-foreground mt-4 text-sm', classProp)}>
  {@render children?.()}
</caption>
src/lib/components/ui/table/TableCell.svelte
<script lang="ts">
  import type { HTMLTdAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLTdAttributes = $props();
</script>
<td data-slot="table-cell" {...props} class={cn('p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0', classProp)}>
  {@render children?.()}
</td>
src/lib/components/ui/table/TableFooter.svelte
<script lang="ts">
  import type { HTMLAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLAttributes<HTMLTableSectionElement> = $props();
</script>
<tfoot data-slot="table-footer" {...props} class={cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', classProp)}>
  {@render children?.()}
</tfoot>
src/lib/components/ui/table/TableHead.svelte
<script lang="ts">
  import type { HTMLThAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLThAttributes = $props();
</script>
<th data-slot="table-head" {...props} class={cn('text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0', classProp)}>
  {@render children?.()}
</th>
src/lib/components/ui/table/TableHeader.svelte
<script lang="ts">
  import type { HTMLAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLAttributes<HTMLTableSectionElement> = $props();
</script>
<thead data-slot="table-header" {...props} class={cn('[&_tr]:border-b', classProp)}>
  {@render children?.()}
</thead>
src/lib/components/ui/table/TableRow.svelte
<script lang="ts">
  import type { HTMLAttributes } from 'svelte/elements';
  import { cn } from 'cn';
  let { children, class: classProp, ...props }: HTMLAttributes<HTMLTableRowElement> = $props();
</script>
<tr data-slot="table-row" {...props} class={cn('hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors has-aria-expanded:bg-muted/50', classProp)}>
  {@render children?.()}
</tr>
src/lib/components/ui/table/index.ts
export { default as Table } from './Table.svelte';
export { default as TableHeader } from './TableHeader.svelte';
export { default as TableBody } from './TableBody.svelte';
export { default as TableFooter } from './TableFooter.svelte';
export { default as TableRow } from './TableRow.svelte';
export { default as TableHead } from './TableHead.svelte';
export { default as TableCell } from './TableCell.svelte';
export { default as TableCaption } from './TableCaption.svelte';

Update the import paths

Change the imports to match your project setup.

Usage

<script lang="ts">
  import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '$lib/components/ui/table';
</script>
<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead class="w-24">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead class="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell class="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell class="text-right">$250.00</TableCell>
    </TableRow>
  </TableBody>
</Table>