Table

Stable v1.0.2

A data table built for real datasets: grouped rows, inline editing, and a rendering approach that stays fast even as rows pile up.

1. Interactive Playground (Employee List)

Showcases Custom UI Slots for Selection, Drag Handles, and Accordions to perfectly construct complex cell layouts without breaking column limits.

Features
Appearance
Data & Sizing
Table Size:
Small
Max Height:
Empty & Row State
Emp ID
Employee Profile
Department
Location
Status
Rating
Contact Email
Actions
EMP-0001
avatar
Marcus ChenLogistics Coordinator
Operations
Singapore
On Leave
Exceeds Expectations
EMP-0002
avatar
Emily ThorneHR Manager
Human Resources
London, UK
Deployed
Meets Expectations
EMP-0003
avatar
John DoeVessel Captain
Maritime
Tokyo, JP
Inactive
Needs Improvement
EMP-0004
avatar
Alicia KeysDeck Cadet
Finance
Dubai, UAE
Active
Outstanding
EMP-0005
avatar
Robert BaratheonSenior Developer
Engineering
Manila, PH
On Leave
Exceeds Expectations
EMP-0006
avatar
Sarah JenkinsLogistics Coordinator
Operations
Singapore
Deployed
Meets Expectations
EMP-0007
avatar
Marcus ChenHR Manager
Human Resources
London, UK
Inactive
Needs Improvement
EMP-0008
avatar
Emily ThorneVessel Captain
Maritime
Tokyo, JP
Active
Outstanding
EMP-0009
avatar
John DoeDeck Cadet
Finance
Dubai, UAE
On Leave
Exceeds Expectations
EMP-0010
avatar
Alicia KeysSenior Developer
Engineering
Manila, PH
Deployed
Meets Expectations

1 to 10 of 45 records

10 / page
Go to
Awaiting platform click events...
Template
<Table
  :records="employeeRecords"
  :columns="employeeColumns"
  rowKey="id"
  :maxHeight="400"
  rowSelection
  expandableRows
  reorderableColumns
  reorderableRows
  @row-click="handleRowClick"
>
  <template #cell-selection="{ row, checked, toggle }">
    <Checkbox :modelValue="checked" @update:modelValue="toggle" />
  </template>

  <template #cell-drag-handle="{ row }">
    <img :src="row.avatar" alt="Example user avatar" class="h-9 w-9 rounded-full" />
    <span>{{ row.name }}</span>
  </template>

  <template #cell-status="{ row }">
    <span :class="getStatusBadgeVariant(row.status)">{{ row.status }}</span>
  </template>

  <template #expanded-row="{ row }">
    Contact: {{ row.email }}
  </template>

  <template #pagination>
    <Pagination v-model:currentPage="currentPage" v-model:rowsPerPage="rowsPerPage" :totalRecords="employeeRecords.length" />
  </template>
</Table>

2. Crew Change Batches (Full-Width Row Override)

Manning layout utilizing the #row slot. It intercepts records containing children arrays and renders a full-width header spanning all columns.

Crew Member Name
Rank
Nationality
Passport Exp
Manila Embarkation - Vessel Alpha(2 Crew)
Oct 12, 2026Approved
Singapore Disembarkation - Vessel Beta(3 Crew)
Oct 15, 2026Pending

1 to 2 of 2 records

10 / page
Go to
Template
<Table :records="crewRecords" :columns="crewColumns" treeNodeKey="crewList">
  <!-- treeNodeKey tells Table which key holds nested rows - this dataset uses
       "crewList" instead of the library's default "children" -->
  <!-- #row intercepts the whole <tr>, so a record with a nested crewList
       array can render a full-width, colspan-ed group header instead of normal cells -->
  <template #row="{ row, depth, expanded, toggle, colspan }">
    <template v-if="row.batchName">
      <td :colspan="colspan" @click="toggle">
        <Icon :icon="expanded ? 'lucide:chevron-down' : 'lucide:chevron-right'" />
        {{ row.batchName }} ({{ row.crewList.length }} Crew)
        <span>{{ row.status }}</span>
      </td>
    </template>

    <template v-else>
      <td :style="{ paddingLeft: `${depth * 20 + 16}px` }">{{ row.name }}</td>
      <td>{{ row.rank }}</td>
      <td>{{ row.nationality }}</td>
      <td>{{ row.passport }}</td>
    </template>
  </template>
</Table>

3. Vehicle Order Form (Inline Editing)

Injects standard InputText and InputNumber components directly into the cells via named slots for rapid data entry.

Vehicle Type
Plate Number
Quantity
Rate (PHP)
Line Total
4-Wheeler Closed Van
₱30,000.00
6-Wheeler Forward
₱22,500.00
10-Wheeler Wing Van
₱0.00

1 to 3 of 3 records

10 / page
Go to
Template
<Table :records="vofRecords" :columns="vofColumns">
  <template #cell-plateNumber="{ row }">
    <InputText v-model="row.plateNumber" size="sm" />
  </template>

  <template #cell-qty="{ row }">
    <InputNumber v-model="row.qty" :min="0" size="sm" />
  </template>

  <template #cell-rate="{ row }">
    <InputNumber v-model="row.rate" mode="currency" currency="PHP" size="sm" />
  </template>

  <template #cell-total="{ row }">
    {{ (row.qty * row.rate).toLocaleString('en-PH', { style: 'currency', currency: 'PHP' }) }}
  </template>
</Table>

4. Accounting Table (Footer Aggregation)

Leveraging the #footer slot to display computed properties across the entire dataset.

Invoice ID
Description / Particulars
Issue Date
Amount (USD)
INV-2026-001
Port Dues - Manila South Harbor
2026-06-01
$4,500.00
INV-2026-002
Bunker Fuel Surcharge
2026-06-03
$12,500.50
INV-2026-003
Pilotage Fees
2026-06-05
$850.75
Grand Total$17,851.25

1 to 3 of 3 records

10 / page
Go to
Template
<Table :records="accountingRecords" :columns="accountingColumns" stripedRows>
  <template #cell-amount="{ row }">
    {{ row.amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' }) }}
  </template>

  <!-- #footer spans the full width beneath every row, ideal for aggregation -->
  <template #footer>
    <div class="flex justify-end gap-8">
      <span>Grand Total</span>
      <span>{{ totalAmount }}</span>
    </div>
  </template>
</Table>

5. Rates Entry (Floating Column Filters & Collapsibles)

Combines tree-node grouping with the #filter-{key} slot to build AG-Grid style header inputs.

Trade Route
Container
Base Freight
BAF
Total USD
Asia to US West Coast
Various
-
-
-
Asia to Europe Base Ports
Various
-
-
-

1 to 2 of 2 records

10 / page
Go to
Template
<Table :records="ratesRecords" :columns="ratesColumns">
  <!-- #filter-{key} renders an input directly in that column's header cell -->
  <template #filter-route>
    <InputText v-model="filters.route" placeholder="Filter Routes..." icon="lucide:search" />
  </template>

  <template #filter-containerType>
    <InputText v-model="filters.containerType" placeholder="Filter Type..." />
  </template>

  <template #cell-baseRate="{ row }">
    {{ row.baseRate ? `$${row.baseRate}` : '-' }}
  </template>

  <template #cell-total="{ row }">
    {{ row.total ? `$${row.total}` : '-' }}
  </template>
</Table>

API Reference

Complete specifications for components, props, events, and dynamic slots.

Props

Name
Type / Payload
Default
Description
records
any[]
[] (v-model)
The flat array of data objects to render. Natively supports nested children matrices.
columns
TableColumn[]
[]
Array of column definitions specifying keys, headers, alignments, and widths.
rowKey
string
"id"
The unique property identifier used for tracking expanded, selected, and dragged rows.
sortKey
string
""
The currently active sorted column key.
sortDirection
"asc" | "desc" | null
null
The currently active sort direction modifier.
maxHeight
string | number
"auto"
Limits table height and enforces vertical scrolling. Accepts px (number) or string (vh/rem).
rowSelection
boolean
false
Injects a pinned left column containing bulk selection checkboxes.
reorderableColumns
boolean
false
Allows drag-and-drop structural reorganization of the header columns.
reorderableRows
boolean | function
false
Enables row dragging. Supply a function `(row) => boolean` to conditionally lock rows.
expandableRows
boolean
false
Injects a pinned left column allowing row accordion dropdowns.
loading
boolean
false
Mounts an overlay indicating pending network requests.
loadingMessage
string
"Loading records..."
Custom text displayed during the loading state.
emptyMessage
string
"No records found."
Text displayed when the `records` array is empty.
emptyStateIcon
string
"lucide:inbox"
Iconify key used in the empty state splash.
treeNodeKey
string
"children"
The object key used to identify hierarchical nested sub-rows.
rowClass
string | array | object | fn
undefined
Applies dynamic Tailwind classes to specific rows. Functions receive `(row, index)`.
stripedRows
boolean
true
Applies alternating background colors for easier row scanning.
bordered
boolean
false
Renders explicit vertical and horizontal borders between all matrix cells.
seamless
boolean
false
Removes the outer table border wrapper for clean nested integration.
size
"xs" | "sm" | "md" | "lg" | "xl"
"sm"
Modifies the total vertical and horizontal padding across all table cells.
highlightOnHover
boolean
true
Enables standard row background highlighting on mouse hover.
rtlDirection
boolean
false
Forces Right-To-Left layout logic.

Column Definition (TableColumn)

Name
Type / Payload
Default
Description
key
string
Required
The data key in your records object to bind to this column.
header
string
Required
The display text for the column header.
width
string | number
auto
Fixed width in px (number) or CSS string. Required for pinned columns.
sortable
boolean
false
Enables click-to-sort functionality for this specific column.
pinned
"left" | "right"
undefined
Pins the column to the edge of the scrolling viewport.
align
"left" | "center" | "right"
"left"
Alignment of both the header and data cells.
hidden
boolean
false
Toggles column visibility without removing it from the array.
headerClass
string
""
Custom classes strictly applied to the `<th>` cell.
cellClass
string | fn
""
Custom classes applied to the `<td>`. Functions receive `(row)`.
children
TableColumn[]
undefined
Array of nested columns to build multi-level AG-Grid style header groups.

Events (Emits)

Name
Type / Payload
Default
Description
row-click
(row, event)
-
Fired when a user clicks anywhere on a row that is not an interactive button or input.
row-contextmenu
(row, event)
-
Intercepts right-clicks on a row to trigger the `#context-menu` slot layout.
selection-change
(selectedRows[])
-
Fired whenever a user toggles the selection status of any checkboxes.
sort-change
({ key, direction })
-
Dispatched when a sortable header is clicked. Executed logic is headless.
column-drop
(sourceField, targetField)
-
Dispatched when a column completes a drag-and-drop horizontal reordering action.
row-drop
(sourceId, targetId)
-
Dispatched when a row completes a vertical drag-and-drop reordering action.

Slots

Slot Name
Exposed Bindings
Description
toolbar
-
Injects full-width components completely above the table (e.g. search bars, batch actions).
empty
-
Overrides the standard icon/text splash screen when 0 records are provided.
header-expand
-
Overrides the empty `<th>` cell directly above the accordion column.
header-selection
{ checked, indeterminate, toggle }
Overrides the "Select All" checkbox in the header. Exposes full toggle state.
header-drag-handle
-
Overrides the empty `<th>` cell directly above the row drag column.
header-{key}
{ column }
Dynamically targets a specific column key to inject custom typography or sort icons.
header-action
-
Overrides the default "Actions" text in the pinned right-side column.
filter-{key}
{ column }
Creates a sub-header row dedicated to injecting standard form inputs for column filtering.
tbody-start
{ colspan }
Injects persistent static rows at the absolute top of the table body.
row
{ row, index, expanded, toggle, colspan }
Triggered when a row has an array of children. Bypasses standard `<td>` loops, granting total <tr> structure control.
cell-expand
{ row, expanded, toggle }
Overrides the accordion expansion button in the system column.
cell-selection
{ row, checked, toggle }
Overrides the row selection checkboxes in the system column.
cell-drag-handle
{ row }
Overrides the grab handle in the system column.
body-tree-toggle
{ row, expanded, toggle }
Overrides the expand toggle button on hierarchical tree nodes within the first data column.
cell-{key}
{ row, column, index }
The most heavily utilized slot. Replaces the text output of a cell with interactive UI elements.
action
{ row, index }
Populates the pinned right-side cell for dropdown menus or edit buttons.
expanded-row
{ row, index }
Populates the dynamic accordion container injected directly beneath a toggled row.
tbody-end
{ colspan }
Injects persistent static rows at the absolute bottom of the table body.
footer
-
A dedicated area spanning all columns for statistical aggregation or sums.
context-menu
{ row, close }
A hidden layout zone activated strictly on right-click for advanced nested actions.
pagination
-
A protected zone beneath the entire table footprint designated for injecting Pagination controls.