
npm install @gulibs/react-vtableA powerful and flexible React table component library built with shadcn/ui and TanStack Table.
---
- Features
- Prerequisites
- Installation
- Quick Start
- Core Concepts
- Complete API Reference
- Advanced Examples
- Best Practices
- Development
- License
- ✨ 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
This library requires Tailwind CSS to be installed and configured in your project.
``bashUsing pnpm (recommended)
pnpm add @gulibs/react-vtable
$3
#### Tailwind CSS Setup
Add the library path to your
tailwind.config.js:`javascript
// 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.
$3
`tsx
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: 'john@example.com' },
{ id: 2, name: 'Jane Smith', age: 25, email: 'jane@example.com' },
]
return (
columns={columns}
dataSource={dataSource}
rowKey="id"
/>
)
}
`$3
#### 1. Data Modes
Local Data Mode (using
dataSource):
`tsx
dataSource={localData}
columns={columns}
rowKey="id"
/>
`Remote Data Mode (using
request):
`tsx
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 backendDefault 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):
`tsx
dataSource={data}
pagination={{ pageSize: 10 }}
/>
`Server-side Pagination (with
request):
`tsx
request={fetchData}
pagination={{ pageSize: 10 }}
/>
`Manual Server Pagination (without
request):
`tsx
dataSource={currentPageData}
pagination={{
mode: 'server', // Important!
current: page,
pageSize: pageSize,
total: total,
onChange: (page, pageSize) => fetchPage(page, pageSize)
}}
/>
`---
Complete API Reference
$3
#### Core Props
| Property | Type | Default | Description |
|----------|------|---------|-------------|
|
columns | ProColumn | [] | Column definitions |
| dataSource | T[] | [] | Local data array |
| request | (params) => Promise | - | 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 | - | 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 | - | Row selection config |
| batchActions | ProTableBatchActions | - | Batch actions config |
| editable | ProTableEditable | - | Inline editing config |
| expandable | ProTableExpandable | - | 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 | Table action reference |
| formRef | Ref | Search form reference |
| locale | ProTableLocale | i18n locale config |---
$3
#### 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 | 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 | Field component props |---
$3
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 |---
$3
`typescript
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:
`tsx
search={{
filterType: 'query',
searchText: 'Search',
resetText: 'Reset',
labelWidth: 'auto',
defaultCollapsed: false,
}}
/>
`---
$3
`typescript
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
}
`---
$3
`typescript
interface ProTableRowSelection {
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 // Batch operations config
}
`#### Cross-Page Selection
When using
request or pagination.onChange, selection automatically persists across pages:`tsx
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)
}
}}
/>
`---
$3
`typescript
interface ProTableBatchActions {
actions?: Array> // Batch action list
maxVisibleActions?: number // Max buttons to show directly (default: 3)
hideClearButton?: boolean // Hide clear selection button
}interface ProTableBatchAction {
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:
`tsx
import { Trash2, Download, FileText, Mail } from 'lucide-react' rowSelection={{
type: 'checkbox',
batchActions: {
maxVisibleActions: 3, // Show max 3 buttons directly
actions: [
{
key: 'delete',
label: 'Delete',
icon: ,
onClick: (rows) => {
console.log('Delete:', rows)
},
variant: 'destructive',
clearSelection: true, // Clear after delete
},
{
key: 'export',
label: 'Export',
icon: ,
onClick: (rows) => {
console.log('Export:', rows)
},
variant: 'outline',
},
{
key: 'exportExcel',
label: 'Export Excel',
icon: ,
onClick: (rows) => {
console.log('Export Excel:', rows)
},
variant: 'outline',
isExportFormat: true, // Groups with other export formats
showInMore: true,
},
{
key: 'exportCSV',
label: 'Export CSV',
icon: ,
onClick: (rows) => {
console.log('Export CSV:', rows)
},
variant: 'outline',
isExportFormat: true,
showInMore: true,
},
{
key: 'email',
label: 'Send Email',
icon: ,
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---
$3
`typescript
interface ProTableEditable {
type?: 'single' | 'multiple' // Edit mode
form?: any // React Hook Form instance
formProps?: Record
onSave?: (key, record, originRow) => Promise
onDelete?: (key, record) => Promise
onCancel?: (key, record, originRow) => void
actionRender?: (record, config) => ReactNode[] // Custom action buttons
deletePopconfirmMessage?: ReactNode
onlyOneLineEditorAlertMessage?: ReactNode
onlyAddOneLineAlertMessage?: ReactNode
}
`---
$3
`typescript
interface ProTableExpandable {
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)
}
`---
$3
`typescript
interface ProColumnEllipsis {
multiline?: boolean // Multi-line ellipsis
rows?: number // Number of rows (1-5, default: 2)
}
`Examples:
`tsx
// Single-line ellipsis
ellipsis: true// Multi-line ellipsis (2 lines)
ellipsis: {
multiline: true,
rows: 2
}
`---
$3
`typescript
interface ProColumnTooltip {
content?: ReactNode | ((value, record, index) => ReactNode)
side?: 'top' | 'right' | 'bottom' | 'left'
align?: 'start' | 'center' | 'end'
delayDuration?: number
disabled?: boolean
}
`Examples:
`tsx
// 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'
}
`---
$3
Access table methods via
actionRef:`typescript
interface ProTableAction {
reload: (resetPageIndex?: boolean) => Promise // Reload data
reloadAndRest: () => Promise // 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:
`tsx
const actionRef = useRef>(null)// Reload data
actionRef.current?.reload()
// Reset filters
actionRef.current?.reset()
// Clear selection
actionRef.current?.clearSelected()
`---
Advanced Examples
$3
`tsx
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"
/>
`$3
`tsx
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) => (
),
},
]}
dataSource={data}
scroll={{ x: 1200 }} // Enable horizontal scroll
tableLayout="fixed" // Required for fixed columns
rowKey="id"
/>
`$3
`tsx
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"
/>
`$3
`tsx
const [editableKeys, setEditableKeys] = useState([]) 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"
/>
`$3
`tsx
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"
/>
`$3
`tsx
columns={[
{
title: 'Avatar',
dataIndex: 'avatar',
valueType: 'avatar',
render: (_, record) => (
{record.name[0]}
),
},
{
title: 'Status',
dataIndex: 'status',
valueType: 'status',
valueEnum: {
active: { text: 'Active', status: 'success' },
inactive: { text: 'Inactive', status: 'default' },
pending: { text: 'Pending', status: 'warning' },
},
render: (_, record) => (
{record.status}
),
},
{
title: 'Progress',
dataIndex: 'progress',
valueType: 'progress',
render: (_, record) => (
{record.progress}%
),
},
]}
dataSource={data}
rowKey="id"
/>
`$3
`tsx
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"
/>
`$3
`tsx
columns={[
{
title: 'Name',
dataIndex: 'name',
search: true,
},
{
title: 'Date Range',
dataIndex: 'dateRange',
valueType: 'dateRange',
search: true,
renderFormItem: (value, onChange) => (
value={value}
onChange={onChange}
/>
),
},
{
title: 'Custom Filter',
dataIndex: 'customField',
search: {
transform: (value) => {
// Transform before sending to backend
return { customFieldQuery: value.toUpperCase() }
},
},
renderFormItem: (value, onChange) => (
value={value}
onChange={onChange}
/>
),
},
]}
request={fetchData}
search={{ filterType: 'query' }}
rowKey="id"
/>
`---
Best Practices
$3
#### Use
rowKey Correctly
Always provide a stable, unique rowKey:`tsx
// Good: Use unique ID
// Good: Use function for complex keys
${record.type}-${record.id}} />// Bad: Use index (causes re-render issues)
index} />
`#### Memoize Large Data
For large datasets, memoize your data:
`tsx
const data = useMemo(() => generateLargeData(), [])
`#### Optimize Column Rendering
Use
useMemo for column definitions with complex render functions:`tsx
const columns = useMemo(() => [
{
title: 'Name',
render: (_, record) => ,
},
], [dependencies])
`$3
#### Handle Errors Gracefully
`tsx
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:`tsx
const [searchParams, setSearchParams] = useState({})
const debouncedSearch = useMemo(
() => debounce((value) => setSearchParams({ keyword: value }), 500),
[]
) request={fetchData}
params={searchParams}
toolbar={{
search: {
onSearch: debouncedSearch,
},
}}
/>
`$3
#### Controlled Components
For complex state management, use controlled mode:
`tsx
const [selectedRowKeys, setSelectedRowKeys] = useState([])
const [pagination, setPagination] = useState({ current: 1, pageSize: 10 }) rowSelection={{
selectedRowKeys,
onChange: setSelectedRowKeys,
}}
pagination={{
...pagination,
onChange: (current, pageSize) => {
setPagination({ current, pageSize })
},
}}
/>
`#### Use Action Ref
Access table methods via
actionRef:`tsx
const actionRef = useRef>(null)const handleRefresh = () => {
actionRef.current?.reload()
}
const handleReset = () => {
actionRef.current?.reset()
actionRef.current?.clearSelected()
}
`$3
- Always provide meaningful
title for columns
- Use aria-label for action buttons
- Ensure color contrast for status badges
- Test with keyboard navigation$3
`tsx
import { ProTable, zh_CN, en_US } from '@gulibs/react-vtable'
import { useState } from 'react'function App() {
const [locale, setLocale] = useState(en_US)
return (
<>
locale={locale}
columns={columns}
dataSource={data}
/>
>
)
}
`$3
Always provide TypeScript types for your data:
`tsx
interface User {
id: string
name: string
email: string
age: number
status: 'active' | 'inactive'
}const columns: ProColumn[] = [
{
title: 'Name',
dataIndex: 'name',
// TypeScript will validate dataIndex, valueType, render function params, etc.
},
]
columns={columns}
dataSource={users}
rowKey="id"
/>
`---
Development
`bash
Install dependencies
pnpm installStart development server
pnpm run devBuild library
pnpm run buildLint code
pnpm run lint
`---
License
MIT © @gulibs
---
Chinese
中文文档
$3
`tsx
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 (
columns={columns}
dataSource={dataSource}
rowKey="id"
locale={zh_CN}
/>
)
}
`$3
- ✨ 强大的表格:基于 TanStack Table v8
- 🎨 精美组件:shadcn/ui 设计
- 🔍 搜索筛选:自动处理本地/远程数据
- 📄 分页支持:客户端和服务端分页
- ✅ 行选择:支持跨页选中
- 🎯 批量操作:灵活的批量操作配置
- ✏️ 内联编辑:支持验证
- 📌 固定列:左右固定,自动阴影
- 🌳 树形表格:层次数据展示
- 🔄 拖拽排序:行列拖拽重排
- 📱 响应式设计:移动端友好
- 🌐 国际化:内置中英文
- 🎭 丰富类型:20+ 值类型
- 💬 提示框:单元格和表头提示
- 📏 文本省略:单行和多行截断
- 🔧 TypeScript:完整类型支持
$3
`tsx
import { Trash2, Download, FileText } from 'lucide-react' rowSelection={{
type: 'checkbox',
batchActions: {
maxVisibleActions: 3,
actions: [
{
key: 'delete',
label: '删除',
icon: ,
onClick: (rows) => console.log('删除:', rows),
variant: 'destructive',
clearSelection: true,
},
{
key: 'export',
label: '导出',
icon: ,
onClick: (rows) => console.log('导出:', rows),
variant: 'outline',
},
{
key: 'exportExcel',
label: '导出 Excel',
icon: ,
onClick: (rows) => console.log('导出 Excel:', rows),
variant: 'outline',
isExportFormat: true,
showInMore: true,
},
],
},
}}
dataSource={data}
rowKey="id"
locale={zh_CN}
/>
``完整的中文文档和 API 参考,请查看英文部分。所有功能和 API 在中英文版本中都是一致的。
---
Contributions are welcome! Please feel free to submit a Pull Request.
If you have any questions or need help, please open an issue on GitHub.