Skip to content

HiTable Component

A comprehensive data table component built on TanStack Table with enterprise-level features for managing complex data sets efficiently.

TanStack Table TypeScript Export Ready

Advanced Sorting

Multi-column sorting with custom sort functions and indicators

Flexible Filtering

Global search, column-specific filters, and custom filter components

Smart Pagination

Configurable page sizes, navigation controls, and row count display

Row Selection

Single and multi-row selection with bulk action support

Export Integration

Built-in CSV/Excel export with customizable data transformation

Column Management

Show/hide columns, resize, and reorder functionality

Import and use HiTable with minimal configuration:

Basic HiTable Usage
import React from 'react'
import { HiTable } from '@/components/HiTable'
import type { ColumnDef } from '@tanstack/react-table'
interface User {
id: string
name: string
email: string
role: string
status: 'active' | 'inactive'
}
const userColumns: ColumnDef<User>[] = [
{
accessorKey: 'id',
header: 'ID',
size: 80
},
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => (
<div className="font-medium">{row.getValue('name')}</div>
)
},
{
accessorKey: 'email',
header: 'Email'
},
{
accessorKey: 'role',
header: 'Role',
filterFn: 'includesString'
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const status = row.getValue('status') as string
return (
<Badge
variant={status === 'active' ? 'success' : 'secondary'}
>
{status}
</Badge>
)
}
}
]
const users: User[] = [
{
id: '1',
name: 'John Doe',
role: 'Admin',
status: 'active'
},
// ... more users
]
export default function UserTable() {
return (
<HiTable
data={users}
columns={userColumns}
config={{
pagination: { enabled: true, pageSize: 10 },
sorting: { enabled: true },
filtering: { enabled: true, searchable: true },
rowSelection: { enabled: true }
}}
/>
)
}
HiTable Configuration Interface
interface HiTableConfig {
// Pagination settings
pagination?: {
enabled: boolean
pageSize?: number
showSizeSelector?: boolean
sizeOptions?: number[]
showPageInfo?: boolean
}
// Sorting configuration
sorting?: {
enabled: boolean
multiSort?: boolean
defaultSort?: SortingState
}
// Filtering options
filtering?: {
enabled: boolean
searchable?: boolean
globalFilterFn?: FilterFn<any>
debounceMs?: number
}
// Row selection
rowSelection?: {
enabled: boolean
mode?: 'single' | 'multiple'
preserveSelection?: boolean
}
// Column management
columnVisibility?: {
enabled: boolean
hiddenColumns?: string[]
}
// Loading and empty states
loading?: boolean
loadingComponent?: React.ComponentType
emptyComponent?: React.ComponentType
// Styling
className?: string
size?: 'sm' | 'md' | 'lg'
striped?: boolean
}
Advanced HiTable with Actions
import React from 'react'
import { HiTable } from '@/components/HiTable'
import { HiExport } from '@/components/HiExport'
import { Button } from '@/components/ui/button'
import { Edit, Trash2, Plus } from 'lucide-react'
export default function ProductTable() {
const [products, setProducts] = React.useState<Product[]>([])
const [loading, setLoading] = React.useState(true)
const productColumns: ColumnDef<Product>[] = [
{
accessorKey: 'image',
header: 'Image',
cell: ({ row }) => (
<img
src={row.getValue('image')}
alt="Product"
className="h-10 w-10 rounded-md object-cover"
/>
),
enableSorting: false
},
{
accessorKey: 'name',
header: 'Product Name',
cell: ({ row }) => (
<div>
<div className="font-medium">{row.getValue('name')}</div>
<div className="text-sm text-muted-foreground">
SKU: {row.original.sku}
</div>
</div>
)
},
{
accessorKey: 'price',
header: 'Price',
cell: ({ row }) => {
const price = parseFloat(row.getValue('price'))
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(price)
}
},
{
accessorKey: 'stock',
header: 'Stock',
cell: ({ row }) => {
const stock = row.getValue('stock') as number
return (
<Badge variant={stock > 10 ? 'success' : 'warning'}>
{stock} units
</Badge>
)
}
},
{
accessorKey: 'category',
header: 'Category',
filterFn: 'includesString'
},
{
id: 'actions',
header: 'Actions',
cell: ({ row }) => (
<div className="flex space-x-2">
<Button
variant="ghost"
size="sm"
onClick={() => handleEdit(row.original)}
>
<Edit className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => handleDelete(row.original.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
),
enableSorting: false
}
]
const handleEdit = (product: Product) => {
// Edit functionality
}
const handleDelete = (productId: string) => {
// Delete functionality
}
return (
<HiTable
data={products}
columns={productColumns}
config={{
pagination: {
enabled: true,
pageSize: 15,
showSizeSelector: true,
sizeOptions: [10, 15, 25, 50]
},
sorting: {
enabled: true,
multiSort: true
},
filtering: {
enabled: true,
searchable: true,
debounceMs: 300
},
rowSelection: {
enabled: true,
mode: 'multiple'
},
columnVisibility: { enabled: true },
loading,
className: "border rounded-lg"
}}
toolbarActions={(table) => (
<div className="flex items-center space-x-2">
<Button onClick={() => {/* Add product */}}>
<Plus className="h-4 w-4 mr-2" />
Add Product
</Button>
<HiExport
table={table}
originalData={products}
filename="products-export"
buttonText="Export"
/>
</div>
)}
/>
)
}
Column Definition Examples
import type { ColumnDef } from '@tanstack/react-table'
// Basic data column
{
accessorKey: 'name',
header: 'Name',
size: 200, // Column width
enableSorting: true,
enableGlobalFilter: true
}
// Custom cell rendering
{
accessorKey: 'status',
header: 'Status',
cell: ({ row, getValue }) => {
const status = getValue() as string
return <StatusBadge status={status} />
}
}
// Accessor function for complex data
{
accessorFn: (row) => `${row.firstName} ${row.lastName}`,
id: 'fullName',
header: 'Full Name'
}
// Action column (no data)
{
id: 'actions',
header: 'Actions',
cell: ({ row }) => <ActionButtons row={row} />,
enableSorting: false,
enableGlobalFilter: false
}
// Custom filter function
{
accessorKey: 'createdAt',
header: 'Created Date',
filterFn: (row, id, value) => {
return dayjs(row.getValue(id)).isAfter(value)
}
}
Custom Global Filter
import React from 'react'
import { Input } from '@/components/ui/input'
import { Search } from 'lucide-react'
interface GlobalFilterProps {
value: string
onChange: (value: string) => void
placeholder?: string
}
export const GlobalFilter: React.FC<GlobalFilterProps> = ({
value,
onChange,
placeholder = "Search..."
}) => {
return (
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder={placeholder}
value={value}
onChange={(e) => onChange(e.target.value)}
className="pl-10 max-w-sm"
/>
</div>
)
}
// Usage in HiTable
<HiTable
data={data}
columns={columns}
config={{
filtering: {
enabled: true,
searchable: true,
globalFilterComponent: GlobalFilter
}
}}
/>
Column-Specific Filters
// Date range filter
{
accessorKey: 'createdAt',
header: 'Created Date',
filterFn: 'dateBetween',
Filter: ({ column }) => (
<DateRangePicker
onDateChange={(range) => column.setFilterValue(range)}
/>
)
}
// Select filter
{
accessorKey: 'category',
header: 'Category',
filterFn: 'includesString',
Filter: ({ column }) => (
<Select onValueChange={(value) => column.setFilterValue(value)}>
<SelectTrigger>
<SelectValue placeholder="All Categories" />
</SelectTrigger>
<SelectContent>
<SelectItem value="">All Categories</SelectItem>
<SelectItem value="electronics">Electronics</SelectItem>
<SelectItem value="clothing">Clothing</SelectItem>
</SelectContent>
</Select>
)
}
Bulk Actions Implementation
import React from 'react'
import { HiTable } from '@/components/HiTable'
import { Button } from '@/components/ui/button'
import { Trash2, Archive, Mail } from 'lucide-react'
export default function UserTableWithBulkActions() {
const handleBulkDelete = (selectedRows: User[]) => {
const ids = selectedRows.map(row => row.id)
// Perform bulk delete
console.log('Deleting users:', ids)
}
const handleBulkArchive = (selectedRows: User[]) => {
// Perform bulk archive
}
const handleBulkEmail = (selectedRows: User[]) => {
const emails = selectedRows.map(row => row.email)
// Open email composer with selected emails
}
return (
<HiTable
data={users}
columns={columns}
config={{
rowSelection: { enabled: true, mode: 'multiple' }
}}
toolbarActions={(table) => {
const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original)
const hasSelection = selectedRows.length > 0
return (
<div className="flex items-center space-x-2">
{hasSelection && (
<>
<span className="text-sm text-muted-foreground">
{selectedRows.length} selected
</span>
<Button
variant="outline"
size="sm"
onClick={() => handleBulkDelete(selectedRows)}
>
<Trash2 className="h-4 w-4 mr-2" />
Delete
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleBulkArchive(selectedRows)}
>
<Archive className="h-4 w-4 mr-2" />
Archive
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleBulkEmail(selectedRows)}
>
<Mail className="h-4 w-4 mr-2" />
Email
</Button>
</>
)}
</div>
)
}}
/>
)
}
Performance Optimization
import React, { useMemo } from 'react'
import { HiTable } from '@/components/HiTable'
export default function LargeDataTable() {
// Memoize columns to prevent re-renders
const columns = useMemo(() => [
{
accessorKey: 'id',
header: 'ID',
},
// ... other columns
], [])
// Memoize data processing
const processedData = useMemo(() => {
return rawData.map(item => ({
...item,
computedField: expensiveComputation(item)
}))
}, [rawData])
return (
<HiTable
data={processedData}
columns={columns}
config={{
pagination: {
enabled: true,
pageSize: 50 // Larger page size for better UX
},
// Enable virtualization for very large datasets
virtualization: {
enabled: true,
rowHeight: 48
}
}}
/>
)
}
// Custom hook for table data management
function useTableData(endpoint: string) {
const [data, setData] = React.useState([])
const [loading, setLoading] = React.useState(true)
React.useEffect(() => {
// Implement data fetching with pagination
fetchData(endpoint).then(result => {
setData(result)
setLoading(false)
})
}, [endpoint])
return { data, loading, refetch: () => fetchData(endpoint) }
}
Table with Form Modal Integration
import React, { useState } from 'react'
import { HiTable } from '@/components/HiTable'
import { HiForm } from '@/components/HiForm'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
export default function UserManagementTable() {
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
const [selectedUser, setSelectedUser] = useState<User | null>(null)
const handleEdit = (user: User) => {
setSelectedUser(user)
setIsEditModalOpen(true)
}
const handleSave = async (formData: any) => {
// Save user data
await updateUser(selectedUser!.id, formData)
setIsEditModalOpen(false)
// Refresh table data
}
const columns: ColumnDef<User>[] = [
// ... column definitions
{
id: 'actions',
header: 'Actions',
cell: ({ row }) => (
<Button
variant="ghost"
size="sm"
onClick={() => handleEdit(row.original)}
>
Edit
</Button>
)
}
]
return (
<>
<HiTable
data={users}
columns={columns}
config={{
pagination: { enabled: true },
sorting: { enabled: true }
}}
/>
<Dialog open={isEditModalOpen} onOpenChange={setIsEditModalOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit User</DialogTitle>
</DialogHeader>
{selectedUser && (
<HiForm
fields={userFormFields}
defaultValues={selectedUser}
onSubmit={handleSave}
/>
)}
</DialogContent>
</Dialog>
</>
)
}
PropTypeDescription
dataT[]Array of data objects to display
columnsColumnDef<T>[]Column definitions (TanStack Table format)
configHiTableConfigTable configuration options
toolbarActions(table) => ReactNodeCustom toolbar actions renderer
classNamestringAdditional CSS classes
loadingbooleanLoading state
onRowClick(row) => voidRow click handler
onSelectionChange(rows) => voidSelection change handler
Table Instance Methods
// Access table instance through toolbarActions
const toolbarActions = (table) => {
// Get selected rows
const selectedRows = table.getSelectedRowModel().rows
// Get filtered data
const filteredData = table.getFilteredRowModel().rows
// Reset filters
const resetFilters = () => table.resetGlobalFilter()
// Reset sorting
const resetSorting = () => table.resetSorting()
// Get current page info
const pageInfo = {
pageIndex: table.getState().pagination.pageIndex,
pageSize: table.getState().pagination.pageSize,
pageCount: table.getPageCount()
}
return (
<div>
{/* Use table methods */}
</div>
)
}

HiTable provides enterprise-grade data management capabilities with excellent developer experience and user interface! 📊