npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gulibs/react-vtable

v0.0.19

Published

![NPM version](https://img.shields.io/npm/v/@gulibs/react-vtable.svg?style=flat-square)

Readme

@gulibs/react-vtable

NPM version

A powerful and flexible React table component library built with shadcn/ui and TanStack Table.

English | 中文


English

Table of Contents

Features

  • Powerful Table: Built on TanStack Table v8 for maximum flexibility and performance
  • 🎨 shadcn/ui Components: Beautiful, accessible, and customizable UI components
  • 🔍 Search & Filter: Advanced search with auto-filtering for local/remote data
  • 📄 Pagination: Client-side and server-side pagination support
  • Row Selection: Single/multiple selection with cross-page selection support
  • 🎯 Batch Actions: Flexible batch operations with smart button grouping
  • ✏️ Inline Editing: Edit rows inline with validation support
  • 📌 Fixed Columns: Pin columns to left/right with automatic shadow effects
  • 🌳 Tree Table: Hierarchical data with expand/collapse support
  • 🔄 Drag & Drop: Row and column reordering with @dnd-kit
  • 📱 Responsive: Mobile-friendly design
  • 🌐 i18n: Built-in English and Chinese locales
  • 🎭 Rich Value Types: 20+ built-in value types (date, money, status, etc.)
  • 📋 Copy to Clipboard: One-click copy for cells
  • 💬 Tooltip: Customizable tooltips for cells and headers
  • 📏 Text Ellipsis: Single-line and multi-line text truncation
  • 🔧 TypeScript: Full TypeScript support with comprehensive types

Prerequisites

This library requires Tailwind CSS to be installed and configured in your project.

Installation

# Using pnpm (recommended)
pnpm add @gulibs/react-vtable

# Using npm
npm install @gulibs/react-vtable

# Using yarn
yarn add @gulibs/react-vtable

# Peer dependencies (if not already installed)
pnpm add react react-dom @tanstack/react-table tailwindcss

Configuration

Tailwind CSS Setup

Add the library path to your tailwind.config.js:

// tailwind.config.js
export default {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './node_modules/@gulibs/react-vtable/dist/**/*.js'
  ],
  // No additional configuration needed - styles are automatically injected
}

💡 Note: The library automatically injects its CSS styles. No manual CSS imports are required.

Quick Start

import { ProTable } from '@gulibs/react-vtable'

function App() {
  const columns = [
    { title: 'Name', dataIndex: 'name', key: 'name' },
    { title: 'Age', dataIndex: 'age', key: 'age' },
    { title: 'Email', dataIndex: 'email', key: 'email' },
  ]

  const dataSource = [
    { id: 1, name: 'John Doe', age: 30, email: '[email protected]' },
    { id: 2, name: 'Jane Smith', age: 25, email: '[email protected]' },
  ]

  return (
    <ProTable
      columns={columns}
      dataSource={dataSource}
      rowKey="id"
    />
  )
}

Core Concepts

1. Data Modes

Local Data Mode (using dataSource):

<ProTable
  dataSource={localData}
  columns={columns}
  rowKey="id"
/>

Remote Data Mode (using request):

<ProTable
  request={async (params) => {
    // params includes: current, pageSize, filters, sorter
    const res = await fetchData(params)
    return {
      data: res.items,
      total: res.total,
      success: true
    }
  }}
  columns={columns}
  rowKey="id"
/>

2. Search Behavior

The table provides automatic search behavior:

  • Local Mode: Converts search values to TanStack Table's columnFilters and filters in real-time
  • Remote Mode: Triggers data fetch with search parameters sent to backend

Default filter supports:

  • Case-insensitive substring matching for text fields
  • Exact match for enum/select fields (valueEnum, filters, valueType: 'select')
  • Array-based multi-select filtering
  • Automatic empty/null value handling

3. Pagination Modes

Client-side Pagination (default with dataSource):

<ProTable
  dataSource={data}
  pagination={{ pageSize: 10 }}
/>

Server-side Pagination (with request):

<ProTable
  request={fetchData}
  pagination={{ pageSize: 10 }}
/>

Manual Server Pagination (without request):

<ProTable
  dataSource={currentPageData}
  pagination={{
    mode: 'server',  // Important!
    current: page,
    pageSize: pageSize,
    total: total,
    onChange: (page, pageSize) => fetchPage(page, pageSize)
  }}
/>

Complete API Reference

ProTable Props

Core Props

| Property | Type | Default | Description | |----------|------|---------|-------------| | columns | ProColumn<T>[] | [] | Column definitions | | dataSource | T[] | [] | Local data array | | request | (params) => Promise<Response> | - | Remote data fetcher | | rowKey | string \| (record) => string | 'id' | Unique row identifier | | loading | boolean | false | Loading state | | defaultData | T[] | [] | Default data for initial render | | postData | (data: T[]) => T[] | - | Transform data after fetch | | params | Record<string, any> | - | Extra params for request |

Layout Props

| Property | Type | Default | Description | |----------|------|---------|-------------| | size | 'small' \| 'middle' \| 'large' | 'middle' | Table size | | bordered | boolean | false | Show table borders | | tableLayout | 'auto' \| 'fixed' | 'auto' | Table layout algorithm | | scroll | { x?: number \| string \| true; y?: number \| string } | - | Scrollable area config | | sticky | boolean \| { offsetHeader?: number } | false | Sticky header config | | showHeader | boolean | true | Show table header | | className | string | - | Custom class name | | style | CSSProperties | - | Custom styles | | tableStyle | CSSProperties | - | Table element styles |

Feature Props

| Property | Type | Default | Description | |----------|------|---------|-------------| | search | ProTableSearch \| false | false | Search form config | | toolbar | ProTableToolBar \| false | false | Toolbar config | | pagination | ProTablePagination \| false | false | Pagination config | | rowSelection | ProTableRowSelection<T> | - | Row selection config | | batchActions | ProTableBatchActions<T> | - | Batch actions config | | editable | ProTableEditable<T> | - | Inline editing config | | expandable | ProTableExpandable<T> \| false | - | Tree table config | | draggable | ProTableDraggable \| false | - | Drag & drop config | | columnsState | ProColumnState | - | Column state management |

Callback Props

| Property | Type | Description | |----------|------|-------------| | onChange | (pagination, filters, sorter, extra) => void | Table change callback | | onLoad | (dataSource: T[]) => void | Called after data loaded | | onLoadingChange | (loading: boolean) => void | Loading state change | | onRequestError | (error: Error) => void | Request error callback | | onSubmit | (params: any) => void | Search submit callback | | onReset | () => void | Search reset callback | | onRow | (record, index) => HTMLAttributes | Row props callback |

Advanced Props

| Property | Type | Description | |----------|------|-------------| | headerTitle | ReactNode | Table header title | | headerSubTitle | ReactNode | Table header subtitle | | footer | (data) => ReactNode | Table footer render | | emptyRender | ReactNode | Empty state render | | tableRender | (props, dom, domList) => ReactNode | Custom table render | | tableExtraRender | (props, data) => ReactNode | Extra content after table | | paginationRender | (opts) => ReactNode | Custom pagination render | | searchFormRender | (props, dom) => ReactNode | Custom search form render | | actionRef | Ref<ProTableAction<T>> | Table action reference | | formRef | Ref<FormInstance> | Search form reference | | locale | ProTableLocale | i18n locale config |


ProColumn Configuration

Basic Props

| Property | Type | Description | |----------|------|-------------| | title | string | Column header text | | dataIndex | keyof T \| string \| string[] | Data field path (supports nested: 'user.name' or ['user', 'name']) | | dataPath | string | Alternative to dataIndex (string path only: 'user.name') | | key | string | Unique column key | | width | number \| string | Column width (required for fixed columns) | | align | 'left' \| 'center' \| 'right' | Text alignment | | fixed | 'left' \| 'right' | Pin column to left/right |

Display Props

| Property | Type | Description | |----------|------|-------------| | valueType | ProFieldValueType | Value display type (20+ types) | | valueEnum | Record<string, { text: string; status?: string; color?: string }> | Enum value mapping | | render | (value, record, index) => ReactNode | Custom cell render (takes priority over valueType) | | ellipsis | boolean \| ProColumnEllipsis | Text ellipsis config | | tooltip | boolean \| ProColumnTooltip | Tooltip config | | headerEllipsis | boolean \| ProColumnEllipsis | Header ellipsis config | | headerTooltip | boolean \| ProColumnTooltip | Header tooltip config | | copyable | boolean \| ProColumnCopyable | Enable copy to clipboard |

Search Props

| Property | Type | Description | |----------|------|-------------| | search | boolean \| ProColumnSearch | Include in search form | | hideInSearch | boolean | Hide in search form | | searchFormItemProps | FormItemProps | Search form item props | | renderFormItem | (value, onChange, field, column) => ReactNode | Custom search input render |

Table Display Props

| Property | Type | Description | |----------|------|-------------| | hideInTable | boolean | Hide in table | | sorter | boolean \| ((a, b) => number) | Enable sorting | | filters | Array<{ text: string; value: any }> | Filter options | | onFilter | (value, record) => boolean | Filter function |

Edit Props

| Property | Type | Description | |----------|------|-------------| | editable | boolean \| ((record) => boolean) | Enable inline editing | | hideInForm | boolean | Hide in edit form | | formItemProps | FormItemProps | Form item props | | fieldProps | Record<string, any> | Field component props |


Value Types Reference

Supported valueType values:

| Type | Description | Example | |------|-------------|---------| | text | Plain text | "Hello World" | | textarea | Multi-line text | Long content | | number | Number | 123 | | money | Currency | $1,234.56 | | percent | Percentage | 75% | | date | Date | 2024-01-01 | | dateTime | Date with time | 2024-01-01 10:30:00 | | dateRange | Date range | 2024-01-01 ~ 2024-01-31 | | select | Select dropdown | Uses valueEnum | | status | Status badge | Uses valueEnum | | tags | Multiple tags | Array display | | switch | Boolean switch | true/false | | avatar | Avatar image | URL display | | image | Image | URL display | | progress | Progress bar | 0-100 | | code | Code block | Monospace text | | fromNow | Relative time | "2 hours ago" | | email | Email address | With mailto link | | phone | Phone number | Formatted | | url | URL link | Clickable link | | color | Color picker | Color swatch | | rate | Rating stars | 1-5 stars | | custom | Custom render | Use render function |


Search Configuration

interface ProTableSearch {
  filterType?: 'query' | 'light'  // Query: form above table, Light: inline filters
  searchText?: string              // Search button text
  resetText?: string               // Reset button text
  submitText?: string              // Submit button text
  labelWidth?: number | 'auto'     // Label width
  span?: number                    // Grid column span
  defaultCollapsed?: boolean       // Initially collapsed
  collapsed?: boolean              // Controlled collapse state
  onCollapse?: (collapsed: boolean) => void
  optionRender?: (config, props, dom) => ReactNode[]  // Custom action buttons
}

Example:

<ProTable
  search={{
    filterType: 'query',
    searchText: 'Search',
    resetText: 'Reset',
    labelWidth: 'auto',
    defaultCollapsed: false,
  }}
/>

Pagination Configuration

interface ProTablePagination {
  mode?: 'client' | 'server'  // Pagination mode
  pageSize?: number            // Items per page
  current?: number             // Current page (controlled)
  total?: number               // Total items
  showSizeChanger?: boolean    // Show page size selector
  showQuickJumper?: boolean    // Show quick jump input
  showTotal?: (total, range) => ReactNode
  pageSizeOptions?: string[]   // Page size options
  size?: 'default' | 'small'
  simple?: boolean             // Simple pagination
  disabled?: boolean
  onChange?: (page, pageSize) => void
  onShowSizeChange?: (current, size) => void
}

Row Selection Configuration

interface ProTableRowSelection<T> {
  type?: 'checkbox' | 'radio'  // Selection type
  selectedRowKeys?: React.Key[]  // Controlled selection
  onChange?: (keys, rows) => void
  preserveSelectedRowKeys?: boolean  // Keep selection across pages (default: true)
  getCheckboxProps?: (record) => any
  selections?: boolean | any[]
  hideSelectAll?: boolean
  fixed?: boolean              // Fix selection column
  columnWidth?: number | string
  renderCell?: (checked, record, index, originNode) => ReactNode
  alwaysShowAlert?: boolean    // Always show batch actions bar
  batchActions?: ProTableBatchActions<T>  // Batch operations config
}

Cross-Page Selection

When using request or pagination.onChange, selection automatically persists across pages:

<ProTable
  request={fetchData}
  rowSelection={{
    type: 'checkbox',
    preserveSelectedRowKeys: true,  // Default: true
    onChange: (selectedRowKeys, selectedRows) => {
      // selectedRowKeys: all selected keys across all pages
      // selectedRows: rows from current page only
      console.log('Total selected:', selectedRowKeys.length)
    }
  }}
/>

Batch Actions Configuration (Updated API)

interface ProTableBatchActions<T> {
  actions?: Array<ProTableBatchAction<T>>  // Batch action list
  maxVisibleActions?: number  // Max buttons to show directly (default: 3)
  hideClearButton?: boolean   // Hide clear selection button
}

interface ProTableBatchAction<T> {
  key: string                  // Unique action key
  label: string                // Button text
  icon?: ReactNode             // Button icon
  onClick: (rows: T[]) => void // Click handler
  variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
  clearSelection?: boolean     // Clear selection after action (default: false)
  showInMore?: boolean         // Show in "More" menu (default: false)
  isExportFormat?: boolean     // Mark as export format (auto-groups multiple export formats)
}

Example:

import { Trash2, Download, FileText, Mail } from 'lucide-react'

<ProTable
  rowSelection={{
    type: 'checkbox',
    batchActions: {
      maxVisibleActions: 3,  // Show max 3 buttons directly
      actions: [
        {
          key: 'delete',
          label: 'Delete',
          icon: <Trash2 className="h-3 w-3 mr-1" />,
          onClick: (rows) => {
            console.log('Delete:', rows)
          },
          variant: 'destructive',
          clearSelection: true,  // Clear after delete
        },
        {
          key: 'export',
          label: 'Export',
          icon: <Download className="h-3 w-3 mr-1" />,
          onClick: (rows) => {
            console.log('Export:', rows)
          },
          variant: 'outline',
        },
        {
          key: 'exportExcel',
          label: 'Export Excel',
          icon: <FileText className="h-3 w-3 mr-1" />,
          onClick: (rows) => {
            console.log('Export Excel:', rows)
          },
          variant: 'outline',
          isExportFormat: true,  // Groups with other export formats
          showInMore: true,
        },
        {
          key: 'exportCSV',
          label: 'Export CSV',
          icon: <FileText className="h-3 w-3 mr-1" />,
          onClick: (rows) => {
            console.log('Export CSV:', rows)
          },
          variant: 'outline',
          isExportFormat: true,
          showInMore: true,
        },
        {
          key: 'email',
          label: 'Send Email',
          icon: <Mail className="h-3 w-3 mr-1" />,
          onClick: (rows) => {
            console.log('Email:', rows)
          },
          variant: 'outline',
          showInMore: true,  // Move to "More" menu
        },
      ],
    },
  }}
/>

Features:

  • Smart button grouping: buttons exceeding maxVisibleActions auto-move to "More" (⋯) menu
  • Export format grouping: multiple isExportFormat: true actions auto-merge into dropdown
  • Flexible action control: use clearSelection to auto-clear selection after certain actions

Editable Configuration

interface ProTableEditable<T> {
  type?: 'single' | 'multiple'  // Edit mode
  form?: any                     // React Hook Form instance
  formProps?: Record<string, any>
  onSave?: (key, record, originRow) => Promise<void>
  onDelete?: (key, record) => Promise<void>
  onCancel?: (key, record, originRow) => void
  actionRender?: (record, config) => ReactNode[]  // Custom action buttons
  deletePopconfirmMessage?: ReactNode
  onlyOneLineEditorAlertMessage?: ReactNode
  onlyAddOneLineAlertMessage?: ReactNode
}

Expandable Configuration (Tree Table)

interface ProTableExpandable<T> {
  childrenColumnName?: string    // Children field name (default: 'children')
  defaultExpandedRowKeys?: React.Key[]  // Default expanded rows
  expandedRowKeys?: React.Key[]  // Controlled expanded rows
  onExpand?: (expanded, record) => void
  onExpandedRowsChange?: (keys) => void
  expandIcon?: (props: {
    expanded: boolean
    onExpand: () => void
    record: T
  }) => ReactNode
  showExpandColumn?: boolean     // Show expand column (default: true)
  expandColumnWidth?: number     // Expand column width (default: 50)
  defaultExpandAllRows?: boolean // Expand all by default
  accordion?: boolean            // Accordion mode (only one expanded)
}

Ellipsis Configuration

interface ProColumnEllipsis {
  multiline?: boolean  // Multi-line ellipsis
  rows?: number        // Number of rows (1-5, default: 2)
}

Examples:

// Single-line ellipsis
ellipsis: true

// Multi-line ellipsis (2 lines)
ellipsis: {
  multiline: true,
  rows: 2
}

Tooltip Configuration

interface ProColumnTooltip<T> {
  content?: ReactNode | ((value, record, index) => ReactNode)
  side?: 'top' | 'right' | 'bottom' | 'left'
  align?: 'start' | 'center' | 'end'
  delayDuration?: number
  disabled?: boolean
}

Examples:

// Simple tooltip (shows cell value)
tooltip: true

// Custom tooltip
tooltip: {
  content: 'Custom tooltip text',
  side: 'top',
  delayDuration: 300
}

// Function-based tooltip
tooltip: {
  content: (value, record) => `Full: ${value} (ID: ${record.id})`,
  side: 'top'
}

Action Reference

Access table methods via actionRef:

interface ProTableAction<T> {
  reload: (resetPageIndex?: boolean) => Promise<void>  // Reload data
  reloadAndRest: () => Promise<void>  // Reload and reset to page 1
  reset: () => void                    // Reset filters and search
  clearSelected: () => void            // Clear row selection
  startEditable: (rowKey) => boolean   // Start editing row
  cancelEditable: (rowKey) => boolean  // Cancel editing row
  saveEditable: (rowKey, record) => boolean  // Save edited row
  getRowData: (rowKey) => T | undefined     // Get row data
  getTableData: () => T[]              // Get all table data
  setTableData: (data: T[]) => void    // Set table data
}

Example:

const actionRef = useRef<ProTableAction<DataType>>(null)

// Reload data
actionRef.current?.reload()

// Reset filters
actionRef.current?.reset()

// Clear selection
actionRef.current?.clearSelected()

Advanced Examples

1. Remote Data with Search and Pagination

<ProTable
  request={async (params) => {
    // params includes: current, pageSize, keyword, ...filters, ...sorter
    const response = await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify(params)
    })
    const data = await response.json()
    return {
      data: data.items,
      total: data.total,
      success: true
    }
  }}
  columns={[
    { title: 'Name', dataIndex: 'name', search: true },
    { title: 'Email', dataIndex: 'email', search: true },
    {
      title: 'Status',
      dataIndex: 'status',
      valueType: 'select',
      search: true,
      valueEnum: {
        active: { text: 'Active', status: 'success' },
        inactive: { text: 'Inactive', status: 'default' },
      },
    },
  ]}
  search={{ filterType: 'query' }}
  pagination={{
    pageSize: 10,
    showSizeChanger: true,
    showQuickJumper: true,
  }}
  rowKey="id"
/>

2. Fixed Columns with Horizontal Scroll

<ProTable
  columns={[
    {
      title: 'ID',
      dataIndex: 'id',
      width: 80,
      fixed: 'left',  // Pin to left
    },
    {
      title: 'Name',
      dataIndex: 'name',
      width: 150,
      fixed: 'left',
    },
    { title: 'Email', dataIndex: 'email', width: 200 },
    { title: 'Phone', dataIndex: 'phone', width: 150 },
    { title: 'Address', dataIndex: 'address', width: 300 },
    {
      title: 'Actions',
      key: 'actions',
      width: 100,
      fixed: 'right',  // Pin to right
      render: (_, record) => (
        <Button size="sm">Edit</Button>
      ),
    },
  ]}
  dataSource={data}
  scroll={{ x: 1200 }}  // Enable horizontal scroll
  tableLayout="fixed"    // Required for fixed columns
  rowKey="id"
/>

3. Tree Table with Expandable Rows

<ProTable
  columns={[
    { title: 'Name', dataIndex: 'name', width: 200 },
    { title: 'Size', dataIndex: 'size' },
    { title: 'Type', dataIndex: 'type' },
  ]}
  dataSource={[
    {
      id: 1,
      name: 'Folder 1',
      type: 'folder',
      children: [
        { id: 11, name: 'File 1.1', type: 'file', size: '1.2 MB' },
        { id: 12, name: 'File 1.2', type: 'file', size: '2.5 MB' },
      ],
    },
    {
      id: 2,
      name: 'Folder 2',
      type: 'folder',
      children: [
        { id: 21, name: 'File 2.1', type: 'file', size: '3.1 MB' },
      ],
    },
  ]}
  expandable={{
    defaultExpandAllRows: false,
    accordion: false,  // Allow multiple rows expanded
  }}
  rowKey="id"
/>

4. Inline Editing

const [editableKeys, setEditableKeys] = useState<React.Key[]>([])

<ProTable
  columns={[
    {
      title: 'Name',
      dataIndex: 'name',
      editable: true,
    },
    {
      title: 'Age',
      dataIndex: 'age',
      valueType: 'number',
      editable: true,
    },
    {
      title: 'Email',
      dataIndex: 'email',
      editable: true,
    },
  ]}
  dataSource={data}
  editable={{
    type: 'multiple',
    editableKeys,
    onChange: setEditableKeys,
    onSave: async (key, record, originRow) => {
      // Save to backend
      await updateUser(key, record)
      message.success('Saved successfully')
    },
  }}
  rowKey="id"
/>

5. Drag & Drop Reordering

<ProTable
  columns={columns}
  dataSource={data}
  draggable={{
    enabled: true,
    handle: true,  // Show drag handle
    onDragEnd: (result) => {
      const { items, oldIndex, newIndex } = result
      console.log('New order:', items)
      // Update backend order
      updateOrder(items.map(item => item.id))
    },
  }}
  rowKey="id"
/>

6. Custom Cell Rendering

<ProTable
  columns={[
    {
      title: 'Avatar',
      dataIndex: 'avatar',
      valueType: 'avatar',
      render: (_, record) => (
        <Avatar>
          <AvatarImage src={record.avatar} />
          <AvatarFallback>{record.name[0]}</AvatarFallback>
        </Avatar>
      ),
    },
    {
      title: 'Status',
      dataIndex: 'status',
      valueType: 'status',
      valueEnum: {
        active: { text: 'Active', status: 'success' },
        inactive: { text: 'Inactive', status: 'default' },
        pending: { text: 'Pending', status: 'warning' },
      },
      render: (_, record) => (
        <Badge variant={record.status === 'active' ? 'success' : 'secondary'}>
          {record.status}
        </Badge>
      ),
    },
    {
      title: 'Progress',
      dataIndex: 'progress',
      valueType: 'progress',
      render: (_, record) => (
        <div className="flex items-center gap-2">
          <Progress value={record.progress} className="w-20" />
          <span className="text-xs">{record.progress}%</span>
        </div>
      ),
    },
  ]}
  dataSource={data}
  rowKey="id"
/>

7. Multi-line Ellipsis with Tooltip

<ProTable
  columns={[
    {
      title: 'Description',
      dataIndex: 'description',
      width: 300,
      ellipsis: {
        multiline: true,
        rows: 3,  // Show 3 lines max
      },
      tooltip: {
        content: (value) => value,
        side: 'top',
      },
    },
    {
      title: 'Long Title Column with Header Tooltip',
      dataIndex: 'data',
      width: 150,
      headerEllipsis: true,  // Header ellipsis
      headerTooltip: {
        content: 'This is a very long header title that needs tooltip',
        side: 'bottom',
      },
      ellipsis: true,  // Cell ellipsis
      tooltip: true,   // Cell tooltip
    },
  ]}
  dataSource={data}
  rowKey="id"
/>

8. Custom Search Form

<ProTable
  columns={[
    {
      title: 'Name',
      dataIndex: 'name',
      search: true,
    },
    {
      title: 'Date Range',
      dataIndex: 'dateRange',
      valueType: 'dateRange',
      search: true,
      renderFormItem: (value, onChange) => (
        <DateRangePicker
          value={value}
          onChange={onChange}
        />
      ),
    },
    {
      title: 'Custom Filter',
      dataIndex: 'customField',
      search: {
        transform: (value) => {
          // Transform before sending to backend
          return { customFieldQuery: value.toUpperCase() }
        },
      },
      renderFormItem: (value, onChange) => (
        <CustomFilterComponent
          value={value}
          onChange={onChange}
        />
      ),
    },
  ]}
  request={fetchData}
  search={{ filterType: 'query' }}
  rowKey="id"
/>

Best Practices

1. Performance Optimization

Use rowKey Correctly

Always provide a stable, unique rowKey:

// Good: Use unique ID
<ProTable rowKey="id" />

// Good: Use function for complex keys
<ProTable rowKey={(record) => `${record.type}-${record.id}`} />

// Bad: Use index (causes re-render issues)
<ProTable rowKey={(record, index) => index} />

Memoize Large Data

For large datasets, memoize your data:

const data = useMemo(() => generateLargeData(), [])

<ProTable dataSource={data} />

Optimize Column Rendering

Use useMemo for column definitions with complex render functions:

const columns = useMemo(() => [
  {
    title: 'Name',
    render: (_, record) => <ComplexComponent record={record} />,
  },
], [dependencies])

2. Request Best Practices

Handle Errors Gracefully

<ProTable
  request={async (params) => {
    try {
      const res = await fetchData(params)
      return { data: res.items, total: res.total, success: true }
    } catch (error) {
      message.error('Failed to load data')
      return { data: [], total: 0, success: false }
    }
  }}
  onRequestError={(error) => {
    console.error('Request error:', error)
    // Report to error tracking service
  }}
/>

Debounce Search Requests

Use params prop to trigger re-fetch:

const [searchParams, setSearchParams] = useState({})
const debouncedSearch = useMemo(
  () => debounce((value) => setSearchParams({ keyword: value }), 500),
  []
)

<ProTable
  request={fetchData}
  params={searchParams}
  toolbar={{
    search: {
      onSearch: debouncedSearch,
    },
  }}
/>

3. State Management

Controlled Components

For complex state management, use controlled mode:

const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
const [pagination, setPagination] = useState({ current: 1, pageSize: 10 })

<ProTable
  rowSelection={{
    selectedRowKeys,
    onChange: setSelectedRowKeys,
  }}
  pagination={{
    ...pagination,
    onChange: (current, pageSize) => {
      setPagination({ current, pageSize })
    },
  }}
/>

Use Action Ref

Access table methods via actionRef:

const actionRef = useRef<ProTableAction<DataType>>(null)

const handleRefresh = () => {
  actionRef.current?.reload()
}

const handleReset = () => {
  actionRef.current?.reset()
  actionRef.current?.clearSelected()
}

<ProTable actionRef={actionRef} />

4. Accessibility

  • Always provide meaningful title for columns
  • Use aria-label for action buttons
  • Ensure color contrast for status badges
  • Test with keyboard navigation

5. Internationalization

import { ProTable, zh_CN, en_US } from '@gulibs/react-vtable'
import { useState } from 'react'

function App() {
  const [locale, setLocale] = useState(en_US)

  return (
    <>
      <Button onClick={() => setLocale(zh_CN)}>中文</Button>
      <Button onClick={() => setLocale(en_US)}>English</Button>
      
      <ProTable
        locale={locale}
        columns={columns}
        dataSource={data}
      />
    </>
  )
}

6. Type Safety

Always provide TypeScript types for your data:

interface User {
  id: string
  name: string
  email: string
  age: number
  status: 'active' | 'inactive'
}

const columns: ProColumn<User>[] = [
  {
    title: 'Name',
    dataIndex: 'name',
    // TypeScript will validate dataIndex, valueType, render function params, etc.
  },
]

<ProTable<User>
  columns={columns}
  dataSource={users}
  rowKey="id"
/>

Development

# Install dependencies
pnpm install

# Start development server
pnpm run dev

# Build library
pnpm run build

# Lint code
pnpm run lint

License

MIT © @gulibs


Chinese

中文文档

查看完整中文文档 ↗

快速开始

import { ProTable, zh_CN } from '@gulibs/react-vtable'

function App() {
  const columns = [
    { title: '姓名', dataIndex: 'name', key: 'name' },
    { title: '年龄', dataIndex: 'age', key: 'age' },
  ]

  const dataSource = [
    { id: 1, name: '张三', age: 30 },
    { id: 2, name: '李四', age: 25 },
  ]

  return (
    <ProTable
      columns={columns}
      dataSource={dataSource}
      rowKey="id"
      locale={zh_CN}
    />
  )
}

主要特性

  • ✨ 强大的表格:基于 TanStack Table v8
  • 🎨 精美组件:shadcn/ui 设计
  • 🔍 搜索筛选:自动处理本地/远程数据
  • 📄 分页支持:客户端和服务端分页
  • ✅ 行选择:支持跨页选中
  • 🎯 批量操作:灵活的批量操作配置
  • ✏️ 内联编辑:支持验证
  • 📌 固定列:左右固定,自动阴影
  • 🌳 树形表格:层次数据展示
  • 🔄 拖拽排序:行列拖拽重排
  • 📱 响应式设计:移动端友好
  • 🌐 国际化:内置中英文
  • 🎭 丰富类型:20+ 值类型
  • 💬 提示框:单元格和表头提示
  • 📏 文本省略:单行和多行截断
  • 🔧 TypeScript:完整类型支持

批量操作示例

import { Trash2, Download, FileText } from 'lucide-react'

<ProTable
  rowSelection={{
    type: 'checkbox',
    batchActions: {
      maxVisibleActions: 3,
      actions: [
        {
          key: 'delete',
          label: '删除',
          icon: <Trash2 className="h-3 w-3 mr-1" />,
          onClick: (rows) => console.log('删除:', rows),
          variant: 'destructive',
          clearSelection: true,
        },
        {
          key: 'export',
          label: '导出',
          icon: <Download className="h-3 w-3 mr-1" />,
          onClick: (rows) => console.log('导出:', rows),
          variant: 'outline',
        },
        {
          key: 'exportExcel',
          label: '导出 Excel',
          icon: <FileText className="h-3 w-3 mr-1" />,
          onClick: (rows) => console.log('导出 Excel:', rows),
          variant: 'outline',
          isExportFormat: true,
          showInMore: true,
        },
      ],
    },
  }}
  dataSource={data}
  rowKey="id"
  locale={zh_CN}
/>

更多文档

完整的中文文档和 API 参考,请查看英文部分。所有功能和 API 在中英文版本中都是一致的。


Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

If you have any questions or need help, please open an issue on GitHub.