UIPackage

Table

Vue data-display
Edit on GitHub

Plain HTML table primitives — `<Table>`, `<TableHeader>`, `<TableRow>`, `<TableCell>` — with the registry’s borders, padding, and tokens already applied. Use this when Data Table is too heavy.

Also available for React ->

Installation

$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/table.json

Or with the named registry: npx shadcn-vue@latest add @uipkge/table

Examples

Props

Name Type / Values Default Required
class HTMLAttributes['class'] optional
density
'compact''cozy''comfortable'
optional

Dependencies

Used by

Files (11)

  • app/components/ui/table/Table.vue 1.1 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { computed } from 'vue'
    import { cn } from '@/lib/utils'
    
    // Match DataTable's density enum so consumers can opt into the same
    // runtime feel without wiring DataTable. `cozy` is the canonical default
    // for bare tables (cells: py-2, head: h-10); the other two are escape
    // hatches for high-density admin lists and roomy patient-facing views.
    const props = defineProps<{
      class?: HTMLAttributes['class']
      density?: 'compact' | 'cozy' | 'comfortable'
    }>()
    
    const densityClass = computed(() => {
      if (props.density === 'compact') return '[&_td]:py-1.5 [&_td]:text-xs [&_th]:h-8 [&_th]:text-xs'
      if (props.density === 'comfortable') return '[&_td]:py-3 [&_th]:h-12'
      // cozy is the new TableCell/TableHead baseline (py-2 / h-10) -- no override.
      return ''
    })
    </script>
    
    <template>
      <div
        data-uipkge
        data-slot="table-container"
        class="relative w-full overflow-auto"
      >
        <table
          data-uipkge
          data-slot="table"
          :class="cn('w-full caption-bottom text-sm', densityClass, props.class)"
        >
          <slot />
        </table>
      </div>
    </template>
  • app/components/ui/table/TableBody.vue 0.3 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <tbody data-uipkge data-slot="table-body" :class="cn('[&_tr:last-child]:border-0', props.class)">
        <slot />
      </tbody>
    </template>
  • app/components/ui/table/TableCaption.vue 0.3 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <caption data-uipkge data-slot="table-caption" :class="cn('text-muted-foreground mt-4 text-sm', props.class)">
        <slot />
      </caption>
    </template>
  • app/components/ui/table/TableCell.vue 0.5 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <td
        data-uipkge
        data-slot="table-cell"
        :class="
          cn(
            'px-3 py-2 align-middle text-sm whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
            props.class,
          )
        "
      >
        <slot />
      </td>
    </template>
  • app/components/ui/table/TableEmpty.vue 0.7 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { cn } from '@/lib/utils'
    import TableCell from './TableCell.vue'
    import TableRow from './TableRow.vue'
    
    const props = withDefaults(
      defineProps<{
        class?: HTMLAttributes['class']
        colspan?: number
      }>(),
      {
        colspan: 1,
      },
    )
    
    const delegatedProps = reactiveOmit(props, 'class')
    </script>
    
    <template>
      <TableRow>
        <TableCell
          :class="cn('text-foreground p-4 align-middle text-sm whitespace-nowrap', props.class)"
          v-bind="delegatedProps"
        >
          <div class="flex items-center justify-center py-10">
            <slot />
          </div>
        </TableCell>
      </TableRow>
    </template>
  • app/components/ui/table/TableFooter.vue 0.4 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <tfoot
        data-uipkge
        data-slot="table-footer"
        :class="cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', props.class)"
      >
        <slot />
      </tfoot>
    </template>
  • app/components/ui/table/TableHead.vue 0.5 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <th
        data-uipkge
        data-slot="table-head"
        :class="
          cn(
            'text-foreground h-10 px-3 text-left align-middle text-sm font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
            props.class,
          )
        "
      >
        <slot />
      </th>
    </template>
  • app/components/ui/table/TableHeader.vue 0.3 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <thead data-uipkge data-slot="table-header" :class="cn('bg-muted/50 [&_tr]:border-b', props.class)">
        <slot />
      </thead>
    </template>
  • app/components/ui/table/TableRow.vue 0.4 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    </script>
    
    <template>
      <tr
        data-uipkge
        data-slot="table-row"
        :class="cn('hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors duration-150', props.class)"
      >
        <slot />
      </tr>
    </template>
  • app/components/ui/table/index.ts 0.5 kB
    export { default as Table } from './Table.vue'
    export { default as TableBody } from './TableBody.vue'
    export { default as TableCaption } from './TableCaption.vue'
    export { default as TableCell } from './TableCell.vue'
    export { default as TableEmpty } from './TableEmpty.vue'
    export { default as TableFooter } from './TableFooter.vue'
    export { default as TableHead } from './TableHead.vue'
    export { default as TableHeader } from './TableHeader.vue'
    export { default as TableRow } from './TableRow.vue'
  • app/components/ui/table/utils.ts 0.3 kB
    import type { Updater } from '@tanstack/vue-table'
    
    import type { Ref } from 'vue'
    import { isFunction } from '@tanstack/vue-table'
    
    export function valueUpdater<T>(updaterOrValue: Updater<T>, ref: Ref<T>) {
      ref.value = isFunction(updaterOrValue) ? updaterOrValue(ref.value) : updaterOrValue
    }

Raw manifest: https://uipkge.dev/r/vue/table.json