@gulibs/react-vtable
v0.0.19
Published

Readme
@gulibs/react-vtable
A powerful and flexible React table component library built with shadcn/ui and TanStack Table.
English
Table of Contents
- Features
- Prerequisites
- Installation
- Quick Start
- Core Concepts
- Complete API Reference
- Advanced Examples
- Best Practices
- Development
- License
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 tailwindcssConfiguration
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
columnFiltersand 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
maxVisibleActionsauto-move to "More" (⋯) menu - Export format grouping: multiple
isExportFormat: trueactions auto-merge into dropdown - Flexible action control: use
clearSelectionto 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
titlefor columns - Use
aria-labelfor 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 lintLicense
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.
