Skip to main content
DataTable is a feature-rich table component built on TanStack Table that provides sorting, filtering, pagination, row selection, column resizing, and more.

Import

import { DataTable } from 'popui'

Basic Usage

<script lang="ts">
  import { DataTable } from 'popui'
  
  type User = {
    id: string
    name: string
    email: string
    status: string
  }
  
  const data: User[] = [
    { id: '1', name: 'John Doe', email: '[email protected]', status: 'active' },
    { id: '2', name: 'Jane Smith', email: '[email protected]', status: 'inactive' }
  ]
  
  const columns = [
    {
      id: 'name',
      accessorKey: 'name',
      header: 'Name',
      cellType: 'text'
    },
    {
      id: 'email',
      accessorKey: 'email',
      header: 'Email',
      cellType: 'text'
    },
    {
      id: 'status',
      accessorKey: 'status',
      header: 'Status',
      cellType: 'tag',
      cellConfig: {
        options: [
          { value: 'active', label: 'Active', color: 'green' },
          { value: 'inactive', label: 'Inactive', color: 'grey' }
        ]
      }
    }
  ]
</script>

<DataTable {data} {columns} />

Column Types

DataTable supports multiple built-in cell types:

Text Cell

{
  id: 'name',
  accessorKey: 'name',
  header: 'Name',
  cellType: 'text',
  cellConfig: {
    className: 'font-bold'
  }
}

Boolean Cell

Display icons based on boolean values:
<script>
  import { Check } from '@invopop/ui-icons'
</script>

{
  id: 'verified',
  accessorKey: 'verified',
  header: 'Verified',
  cellType: 'boolean',
  cellConfig: {
    icon: Check,
    iconClass: 'size-4 text-green-500',
    showWhenTrue: true,
    showWhenFalse: false,
    hintWhenTrue: 'Verified user',
    hintWhenFalse: 'Not verified'
  }
}

Tag Cell

Display status tags with colors:
{
  id: 'status',
  accessorKey: 'status',
  header: 'Status',
  cellType: 'tag',
  cellConfig: {
    options: [
      { value: 'active', label: 'Active', color: 'green' },
      { value: 'pending', label: 'Pending', color: 'yellow' },
      { value: 'inactive', label: 'Inactive', color: 'grey' }
    ],
    showDot: true
  }
}

Date Cell

{
  id: 'createdAt',
  accessorKey: 'createdAt',
  header: 'Created At',
  cellType: 'date',
  cellConfig: {
    className: 'text-sm'
  }
}

Currency Cell

{
  id: 'amount',
  accessorKey: 'amount',
  header: 'Amount',
  cellType: 'currency',
  cellConfig: {
    className: 'font-semibold'
  }
}

UUID Cell

{
  id: 'id',
  accessorKey: 'id',
  header: 'ID',
  cellType: 'uuid',
  cellConfig: {
    prefixLength: 4,
    suffixLength: 4,
    full: false,
    disabled: false,
    onCopy: (value) => {
      console.log('Copied:', value)
    }
  }
}

Custom Cell Renderer

You can provide custom cell renderers using a function or Snippet:
<script lang="ts">
  import { DataTable } from 'popui'
  import CustomComponent from './CustomComponent.svelte'
  import { renderComponent } from 'popui/data-table'
  
  const columns = [
    {
      id: 'custom',
      accessorKey: 'name',
      header: 'Custom',
      cell: (value, row) => {
        return renderComponent(CustomComponent, { value, data: row })
      }
    }
  ]
</script>

Sorting

Enable sorting on columns:
<script lang="ts">
  const columns = [
    {
      id: 'name',
      accessorKey: 'name',
      header: 'Name',
      cellType: 'text',
      enableSorting: true // Enable sorting (default: true)
    }
  ]
</script>

<DataTable 
  {data} 
  {columns}
  initialSortColumn="name"
  initialSortDirection="asc"
/>

Manual Sorting (Server-Side)

<script lang="ts">
  function handleSortingChange(columnId: string, direction: 'asc' | 'desc' | '') {
    // Fetch sorted data from server
    console.log('Sort by:', columnId, direction)
  }
</script>

<DataTable 
  {data} 
  {columns}
  manualSorting={true}
  onSortingChange={handleSortingChange}
/>

Pagination

<DataTable 
  {data} 
  {columns}
  initialPageSize={25}
  initialPage={0}
/>

Manual Pagination (Server-Side)

<script lang="ts">
  let currentPage = 0
  let pageSize = 10
  let totalPages = 10
  let totalRows = 100
  
  function handlePageChange(pageIndex: number) {
    currentPage = pageIndex
    // Fetch data for new page
  }
  
  function handlePageSizeChange(newPageSize: number) {
    pageSize = newPageSize
    currentPage = 0
    // Fetch data with new page size
  }
</script>

<DataTable 
  {data} 
  {columns}
  manualPagination={true}
  pageCount={totalPages}
  rowCount={totalRows}
  initialPage={currentPage}
  initialPageSize={pageSize}
  onPageChange={handlePageChange}
  onPageSizeChange={handlePageSizeChange}
/>

Row Selection

<script lang="ts">
  function handleSelectionChange(selectedRows) {
    console.log('Selected:', selectedRows)
  }
</script>

<DataTable 
  {data} 
  {columns}
  onSelectionChange={handleSelectionChange}
/>
To disable selection:
<DataTable {data} {columns} disableSelection={true} />

Row Actions

<script lang="ts">
  import { Edit, Trash } from '@invopop/ui-icons'
  
  const rowActions = [
    { label: 'Edit', value: 'edit', icon: Edit },
    { label: 'Delete', value: 'delete', icon: Trash, destructive: true }
  ]
  
  function handleRowAction(action, row) {
    console.log('Action:', action, 'Row:', row)
  }
</script>

<DataTable 
  {data} 
  {columns}
  {rowActions}
  onRowAction={handleRowAction}
/>

Dynamic Row Actions

<script lang="ts">
  function getRowActions(row) {
    if (row.status === 'active') {
      return [
        { label: 'Deactivate', value: 'deactivate' },
        { label: 'Edit', value: 'edit' }
      ]
    }
    return [
      { label: 'Activate', value: 'activate' }
    ]
  }
</script>

<DataTable 
  {data} 
  {columns}
  {getRowActions}
  onRowAction={handleRowAction}
/>

Column Management

Column Visibility

<DataTable 
  {data} 
  {columns}
  initialColumnVisibility={{
    email: false // Hide email column initially
  }}
  onColumnVisibilityChange={(visibility) => {
    console.log('Visibility changed:', visibility)
  }}
/>

Column Resizing

<script lang="ts">
  const columns = [
    {
      id: 'name',
      accessorKey: 'name',
      header: 'Name',
      cellType: 'text',
      size: 200,
      minSize: 100,
      maxSize: 400,
      enableResizing: true
    }
  ]
</script>

<DataTable 
  {data} 
  {columns}
  onColumnResize={(sizes) => {
    console.log('Column sizes:', sizes)
  }}
/>

Frozen Columns

<DataTable 
  {data} 
  {columns}
  initialFrozenColumns={['name', 'email']}
  onFreezeChange={(columnId) => {
    console.log('Freeze toggled for:', columnId)
  }}
/>

Column Ordering

<DataTable 
  {data} 
  {columns}
  initialColumnOrder={['status', 'name', 'email']}
  onColumnOrderChange={(order) => {
    console.log('New column order:', order)
  }}
/>

Loading State

Display skeleton placeholders while loading:
<script lang="ts">
  import { User } from '@invopop/ui-icons'
  
  const columns = [
    {
      id: 'name',
      accessorKey: 'name',
      header: 'Name',
      cellType: 'text',
      loadingConfig: {
        lines: 1,
        showAvatar: true,
        avatarSize: 32
      }
    },
    {
      id: 'email',
      accessorKey: 'email',
      header: 'Email',
      cellType: 'text',
      loadingConfig: {
        lines: 2
      }
    }
  ]
</script>

<DataTable {data} {columns} loading={true} />

Row Styling

<script lang="ts">
  function getRowClassName(row) {
    return row.status === 'critical' ? 'bg-red-50' : ''
  }
  
  function getRowState(row) {
    return {
      isError: row.status === 'error',
      isWarning: row.status === 'warning',
      isSuccess: row.status === 'success'
    }
  }
</script>

<DataTable 
  {data} 
  {columns}
  {getRowClassName}
  {getRowState}
/>

Row Click Handler

<script lang="ts">
  function handleRowClick(row) {
    console.log('Row clicked:', row)
  }
</script>

<DataTable {data} {columns} onRowClick={handleRowClick} />

Empty State

<script lang="ts">
  import { Search } from '@invopop/ui-icons'
</script>

<DataTable 
  {data} 
  {columns}
  emptyState={{
    iconSource: Search,
    title: 'No users found',
    description: 'Try adjusting your search criteria'
  }}
/>

Keyboard Navigation

By default, DataTable supports keyboard navigation:
  • Arrow Up/Down: Navigate rows
  • Shift + Arrow Up/Down: Select multiple rows
  • Space/Enter: Select current row
  • Escape: Clear focus
To disable keyboard navigation:
<DataTable {data} {columns} disableKeyboardNavigation={true} />

Props

data
TData[]
required
Array of data objects to display in the table.
columns
DataTableColumn<TData>[]
required
Column definitions for the table.
loading
boolean
default:"false"
Show skeleton placeholders instead of data.
disableSelection
boolean
default:"false"
Disable row selection functionality.
disablePagination
boolean
default:"false"
Disable pagination controls.
disableKeyboardNavigation
boolean
default:"false"
Disable keyboard navigation.
disableControls
boolean
default:"false"
Disable all controls (filters, pagination, view options).
disableJumpToPage
boolean
default:"false"
Disable first/last page buttons and make page input read-only.
rowActions
TableAction[]
Static row actions available for all rows.
getRowActions
(row: TData) => TableAction[]
Function to dynamically generate row actions based on row data.
onRowAction
(action: any, row: TData) => void
Callback when a row action is clicked.
initialPageSize
number
default:"10"
Initial number of rows per page.
initialPage
number
default:"0"
Initial page index (0-based).
initialSortColumn
string
Initial column ID to sort by.
initialSortDirection
'asc' | 'desc' | ''
Initial sort direction.
initialFrozenColumns
string[]
Array of column IDs to freeze initially.
initialColumnOrder
string[]
Initial column order (array of column IDs).
initialColumnVisibility
Record<string, boolean>
Initial column visibility state.
initialColumnSizing
Record<string, number>
Initial column sizes (column ID to width in pixels).
emptyState
EmptyStateProps
Configuration for empty state display.
onRowClick
(row: TData) => void
Callback when a row is clicked.
onSelectionChange
(selectedRows: TData[]) => void
Callback when row selection changes.
manualPagination
boolean
default:"false"
Enable server-side pagination mode.
manualSorting
boolean
default:"false"
Enable server-side sorting mode.
pageCount
number
Total number of pages (for manual pagination).
rowCount
number
Total number of rows (for manual pagination).
onPageChange
(pageIndex: number) => void
Callback when page changes.
onPageSizeChange
(pageSize: number) => void
Callback when page size changes.
onSortingChange
(columnId: string, direction: TableSortBy) => void
Callback when sorting changes.
onFilterChange
(columnId: string) => void
Callback when filter changes.
onFreezeChange
(columnId: string) => void
Callback when column freeze state changes.
onColumnResize
(columnSizes: Record<string, number>) => void
Callback when columns are resized.
onColumnOrderChange
(columnOrder: string[]) => void
Callback when column order changes.
onColumnVisibilityChange
(visibility: Record<string, boolean>) => void
Callback when column visibility changes.
getRowClassName
(row: TData) => string
Function to generate custom CSS class for rows.
getRowState
(row: TData) => { isSuccess?: boolean; isError?: boolean; isWarning?: boolean }
Function to set row state (affects row styling).

Column Definition

id
string
required
Unique identifier for the column.
accessorKey
keyof TData
Key to access data in the row object.
header
string
Header text to display. Use empty string or omit for no header.
cellType
'text' | 'boolean' | 'tag' | 'date' | 'currency' | 'uuid' | 'custom'
Built-in cell renderer type.
cellConfig
CellConfig
Configuration object for the cell renderer (varies by cellType).
cell
Snippet | Function
Custom cell renderer function or Snippet.
enableSorting
boolean
default:"true"
Enable sorting for this column.
enableHiding
boolean
default:"true"
Allow this column to be hidden.
enableResizing
boolean
default:"true"
Allow this column to be resized.
disableColumnFilter
boolean
default:"false"
Disable column filter option.
size
number
Default column width in pixels.
minSize
number
Minimum column width in pixels.
maxSize
number
Maximum column width in pixels.
loadingConfig
LoadingConfig
Configuration for loading skeleton placeholders.

Cell Config Types

TextCellConfig

className
string
Custom CSS class for the cell.

BooleanCellConfig

icon
IconSource
Icon to display.
iconClass
string
CSS class for the icon.
showWhenTrue
boolean
default:"true"
Show icon when value is true.
showWhenFalse
boolean
default:"false"
Show icon when value is false.
hintWhenTrue
string
Tooltip text when value is true.
hintWhenFalse
string
Tooltip text when value is false.

TagCellConfig

options
Array<{ value: string; label: string; color: StatusType }>
required
Tag options mapping values to labels and colors.
showDot
boolean
default:"false"
Show dot indicator before tag label.

DateCellConfig

className
string
Custom CSS class for the cell.

CurrencyCellConfig

className
string
Custom CSS class for the cell.

UuidCellConfig

prefixLength
number
default:"4"
Number of characters from start to show.
suffixLength
number
default:"4"
Number of characters from end to show.
full
boolean
default:"false"
Show full UUID.
disabled
boolean
default:"false"
Disable copy functionality.
onCopy
(value: string) => void
Callback when UUID is copied.

Loading Config

lines
number
default:"1"
Number of skeleton lines to show.
showAvatar
boolean
default:"false"
Show skeleton avatar before lines.
avatarSize
number
default:"32"
Size of skeleton avatar in pixels.

Build docs developers (and LLMs) love