Advanced Sorting
Multi-column sorting with custom sort functions and indicators
A comprehensive data table component built on TanStack Table with enterprise-level features for managing complex data sets efficiently.
TanStack Table TypeScript Export ReadyAdvanced 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:
import React from 'react'import { HiTable } from '@/components/HiTable'import type { ColumnDef } from '@tanstack/react-table'
interface User {id: stringname: stringemail: stringrole: stringstatus: '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 } }} />)}
interface HiTableConfig {// Pagination settingspagination?: { enabled: boolean pageSize?: number showSizeSelector?: boolean sizeOptions?: number[] showPageInfo?: boolean}
// Sorting configurationsorting?: { enabled: boolean multiSort?: boolean defaultSort?: SortingState}
// Filtering optionsfiltering?: { enabled: boolean searchable?: boolean globalFilterFn?: FilterFn<any> debounceMs?: number}
// Row selectionrowSelection?: { enabled: boolean mode?: 'single' | 'multiple' preserveSelection?: boolean}
// Column managementcolumnVisibility?: { enabled: boolean hiddenColumns?: string[]}
// Loading and empty statesloading?: booleanloadingComponent?: React.ComponentTypeemptyComponent?: React.ComponentType
// StylingclassName?: stringsize?: 'sm' | 'md' | 'lg'striped?: boolean}
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> )} />)}
import type { ColumnDef } from '@tanstack/react-table'
// Basic data column{accessorKey: 'name',header: 'Name',size: 200, // Column widthenableSorting: 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)}}
import React from 'react'import { Input } from '@/components/ui/input'import { Search } from 'lucide-react'
interface GlobalFilterProps {value: stringonChange: (value: string) => voidplaceholder?: 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<HiTabledata={data}columns={columns}config={{ filtering: { enabled: true, searchable: true, globalFilterComponent: GlobalFilter }}}/>
// 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>)}
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> ) }} />)}
import React, { useMemo } from 'react'import { HiTable } from '@/components/HiTable'
export default function LargeDataTable() {// Memoize columns to prevent re-rendersconst columns = useMemo(() => [ { accessorKey: 'id', header: 'ID', }, // ... other columns], [])
// Memoize data processingconst 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 managementfunction 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) }}
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> </>)}
Prop | Type | Description |
---|---|---|
data | T[] | Array of data objects to display |
columns | ColumnDef<T>[] | Column definitions (TanStack Table format) |
config | HiTableConfig | Table configuration options |
toolbarActions | (table) => ReactNode | Custom toolbar actions renderer |
className | string | Additional CSS classes |
loading | boolean | Loading state |
onRowClick | (row) => void | Row click handler |
onSelectionChange | (rows) => void | Selection change handler |
// Access table instance through toolbarActionsconst toolbarActions = (table) => {// Get selected rowsconst selectedRows = table.getSelectedRowModel().rows
// Get filtered dataconst filteredData = table.getFilteredRowModel().rows
// Reset filtersconst resetFilters = () => table.resetGlobalFilter()
// Reset sortingconst resetSorting = () => table.resetSorting()
// Get current page infoconst 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! 📊