Table

A responsive table for displaying tabular data.
A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
Total$750.00
Installation
Expand

npx @left4code/cosmic-ui-cli@latest add table
 
Expand

This writes the component and everything it imports, then installs the packages it needs. Run npx @left4code/cosmic-ui-cli@latest initfirst if you haven't already (it detects Vue automatically).

No state machine here - a table doesn't need one, it's semantic HTML (table > thead/tbody/tfoot) wrapped in a horizontally scrolling container so wide tables don't blow out the layout on small screens. Vue SFCs can't export multiple components from one file, so Table is a folder instead of a single file — copy all of it into your project.

components/ui/table/TableRoot.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

// See the Breadcrumb docs page: without this, Astro's slot="preview" prop
// (used to route this island into a named slot) falls through onto this
// root DOM node inconsistently between SSR and hydration, and Vue flags it
// as a mismatch on every demo page.
defineOptions({ inheritAttrs: false });
const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <div class="relative w-full overflow-x-auto">
    <table :class="twMerge(['w-full border-collapse', className])">
      <slot />
    </table>
  </div>
</template>
 
Expand
components/ui/table/TableHeader.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <thead :class="twMerge(['border-b border-primary/30', className])">
    <slot />
  </thead>
</template>
 
Expand
components/ui/table/TableBody.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <tbody :class="twMerge(['[&_tr:last-child]:border-0', className])">
    <slot />
  </tbody>
</template>
 
Expand
components/ui/table/TableFooter.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <tfoot :class="twMerge(['border-t border-primary/30 font-medium', className])">
    <slot />
  </tfoot>
</template>
 
Expand
components/ui/table/TableRow.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <tr :class="twMerge(['border-b border-primary/10 hover:bg-primary/5', className])">
    <slot />
  </tr>
</template>
 
Expand
components/ui/table/TableHead.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <th :class="twMerge(['h-10 px-4 text-left align-middle font-medium text-foreground/70', className])">
    <slot />
  </th>
</template>
 
Expand
components/ui/table/TableCell.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className, colspan } = defineProps<{ class?: string; colspan?: number }>();
</script>

<template>
  <td :colspan="colspan" :class="twMerge(['p-4 align-middle', className])">
    <slot />
  </td>
</template>
 
Expand
components/ui/table/TableCaption.vue
Expand

<script setup lang="ts">
import { twMerge } from "tailwind-merge";

const { class: className } = defineProps<{ class?: string }>();
</script>

<template>
  <caption :class="twMerge(['mt-4 opacity-70', className])">
    <slot />
  </caption>
</template>
 
Expand
components/ui/table/index.ts
Expand

export { default as TableRoot } from "./TableRoot.vue";
export { default as TableHeader } from "./TableHeader.vue";
export { default as TableBody } from "./TableBody.vue";
export { default as TableFooter } from "./TableFooter.vue";
export { default as TableRow } from "./TableRow.vue";
export { default as TableHead } from "./TableHead.vue";
export { default as TableCell } from "./TableCell.vue";
export { default as TableCaption } from "./TableCaption.vue";
 
Expand

Update the import paths to match your project setup.

Usage
Expand

import {
  TableRoot,
  TableHeader,
  TableBody,
  TableFooter,
  TableRow,
  TableHead,
  TableCell,
  TableCaption,
} from "@/components/ui/table";
 
Expand
Expand

<TableRoot>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Role</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Ada</TableCell>
      <TableCell>Engineer</TableCell>
    </TableRow>
  </TableBody>
</TableRoot>
 
Expand
Powered by synthetic caffeine · Deployed by Left4code · Signal traceable on GitHub.