Data Table

Data table components are the organizers of information. They transform raw data into beautiful, sortable, and interactive tables with professional styling and seamless user experience.

Preview
Code
Name
Email
Role
Status
John Doejohn@example.comAdminActive
Jane Smithjane@example.comUserActive
Bob Johnsonbob@example.comUserInactive

Installation

First, make sure you have the required dependencies:

npx @rajdevxd/aura-ui add data-table
yarn @rajdevxd/aura-ui add data-table
pnpm dlx @rajdevxd/aura-ui add data-table
bunx --bun @rajdevxd/aura-ui add data-table

Usage

Data table components provide sortable, interactive data display:

import { DataTable } from "@/components/ui/data-table"

interface Product {
  id: number
  name: string
  price: number
  category: string
  inStock: boolean
}

const products: Product[] = [
  { id: 1, name: 'Laptop', price: 999, category: 'Electronics', inStock: true },
  { id: 2, name: 'Book', price: 29, category: 'Education', inStock: false },
  // ... more products
]

const columns = [
  { key: 'name' as keyof Product, header: 'Product Name', sortable: true },
  { key: 'price' as keyof Product, header: 'Price', sortable: true },
  {
    key: 'category' as keyof Product,
    header: 'Category',
    render: (value: string) => <span className="capitalize">{value}</span>
  },
  {
    key: 'inStock' as keyof Product,
    header: 'Status',
    render: (value: boolean) => (
      <span className={`px-2 py-1 rounded-full text-xs ${
        value ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
      }`}>
        {value ? 'In Stock' : 'Out of Stock'}
      </span>
    )
  }
]

const ProductTable = () => {
  return (
    <DataTable
      data={products}
      columns={columns}
      onSort={(key, direction) => {
        // Handle sorting logic
        console.log(`Sorting by ${key} ${direction}`)
      }}
    />
  )
}

Props

PropTypeRequiredDescription
dataT[]YesArray of data objects
columnsColumn<T>[]YesColumn configuration array
onSort(key: keyof T, direction: 'asc' | 'desc') => voidNoSort callback function
classNamestringNoAdditional CSS classes
emptyMessagestringNoMessage when no data

Column Configuration

interface Column<T> {
  key: keyof T                    // Data key to display
  header: string                 // Column header text
  sortable?: boolean            // Enable sorting for this column
  render?: (value: any, item: T) => React.ReactNode // Custom render function
  className?: string            // Additional column classes
}

Features

  • Column Sorting: Click headers to sort data
  • Custom Rendering: Flexible cell content with render functions
  • Responsive Design: Adapts to different screen sizes
  • Empty States: Customizable no-data message
  • TypeScript Support: Full type safety
  • Accessible: Keyboard navigation and screen reader support
  • Customizable: Extensive styling options

Advanced Usage

With Pagination

const PaginatedTable = () => {
  const [currentPage, setCurrentPage] = useState(1)
  const [sortKey, setSortKey] = useState<keyof Product | null>(null)
  const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc')

  const itemsPerPage = 10
  const startIndex = (currentPage - 1) * itemsPerPage
  const endIndex = startIndex + itemsPerPage

  const handleSort = (key: keyof Product, direction: 'asc' | 'desc') => {
    setSortKey(key)
    setSortDirection(direction)
    // Sort your data here
  }

  const paginatedData = sortedData.slice(startIndex, endIndex)

  return (
    <div className="space-y-4">
      <DataTable
        data={paginatedData}
        columns={columns}
        onSort={handleSort}
      />

      <div className="flex justify-between items-center">
        <span className="text-sm text-muted-foreground">
          Showing {startIndex + 1} to {Math.min(endIndex, sortedData.length)} of {sortedData.length} entries
        </span>

        <div className="flex space-x-2">
          <button
            onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
            disabled={currentPage === 1}
            className="px-3 py-1 border rounded disabled:opacity-50"
          >
            Previous
          </button>
          <span className="px-3 py-1">Page {currentPage}</span>
          <button
            onClick={() => setCurrentPage(prev => prev + 1)}
            disabled={endIndex >= sortedData.length}
            className="px-3 py-1 border rounded disabled:opacity-50"
          >
            Next
          </button>
        </div>
      </div>
    </div>
  )
}

With Actions

const TableWithActions = () => {
  const handleEdit = (item: Product) => {
    // Edit logic
  }

  const handleDelete = (item: Product) => {
    // Delete logic
  }

  const actionColumns = [
    ...columns,
    {
      key: 'actions' as keyof Product,
      header: 'Actions',
      render: (_: any, item: Product) => (
        <div className="flex space-x-2">
          <button
            onClick={() => handleEdit(item)}
            className="px-2 py-1 text-blue-600 hover:bg-blue-50 rounded"
          >
            Edit
          </button>
          <button
            onClick={() => handleDelete(item)}
            className="px-2 py-1 text-red-600 hover:bg-red-50 rounded"
          >
            Delete
          </button>
        </div>
      )
    }
  ]

  return (
    <DataTable
      data={products}
      columns={actionColumns}
    />
  )
}

With Filtering

const FilterableTable = () => {
  const [filter, setFilter] = useState('')
  const [categoryFilter, setCategoryFilter] = useState('')

  const filteredData = products.filter(product => {
    const matchesSearch = product.name.toLowerCase().includes(filter.toLowerCase())
    const matchesCategory = !categoryFilter || product.category === categoryFilter
    return matchesSearch && matchesCategory
  })

  return (
    <div className="space-y-4">
      <div className="flex space-x-4">
        <input
          type="text"
          placeholder="Search products..."
          value={filter}
          onChange={(e) => setFilter(e.target.value)}
          className="px-3 py-2 border rounded"
        />

        <select
          value={categoryFilter}
          onChange={(e) => setCategoryFilter(e.target.value)}
          className="px-3 py-2 border rounded"
        >
          <option value="">All Categories</option>
          <option value="Electronics">Electronics</option>
          <option value="Books">Books</option>
          <option value="Clothing">Clothing</option>
        </select>
      </div>

      <DataTable
        data={filteredData}
        columns={columns}
        emptyMessage="No products match your filters"
      />
    </div>
  )
}

Common Patterns

User Management Table

const userColumns = [
  { key: 'name', header: 'Name', sortable: true },
  { key: 'email', header: 'Email', sortable: true },
  {
    key: 'role',
    header: 'Role',
    render: (value: string) => (
      <span className={`px-2 py-1 rounded text-xs ${
        value === 'Admin' ? 'bg-purple-100 text-purple-800' :
        value === 'Editor' ? 'bg-blue-100 text-blue-800' :
        'bg-gray-100 text-gray-800'
      }`}>
        {value}
      </span>
    )
  },
  {
    key: 'status',
    header: 'Status',
    render: (value: string) => (
      <span className={`px-2 py-1 rounded-full text-xs ${
        value === 'Active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
      }`}>
        {value}
      </span>
    )
  }
]

Financial Data Table

const financialColumns = [
  { key: 'date', header: 'Date', sortable: true },
  { key: 'description', header: 'Description' },
  {
    key: 'amount',
    header: 'Amount',
    sortable: true,
    render: (value: number) => (
      <span className={value >= 0 ? 'text-green-600' : 'text-red-600'}>
        ${Math.abs(value).toFixed(2)}
      </span>
    )
  },
  { key: 'category', header: 'Category' }
]

Product Catalog

const productColumns = [
  {
    key: 'image',
    header: 'Image',
    render: (value: string) => (
      <img src={value} alt="Product" className="w-12 h-12 object-cover rounded" />
    )
  },
  { key: 'name', header: 'Product Name', sortable: true },
  {
    key: 'price',
    header: 'Price',
    sortable: true,
    render: (value: number) => `$${value.toFixed(2)}`
  },
  {
    key: 'rating',
    header: 'Rating',
    render: (value: number) => '⭐'.repeat(Math.floor(value))
  }
]

Performance Considerations

Large Datasets

const OptimizedTable = () => {
  // Use React.memo for expensive render functions
  const memoizedColumns = useMemo(() => columns, [])

  // Implement virtual scrolling for very large datasets
  // Consider pagination for better performance

  return (
    <DataTable
      data={largeDataset}
      columns={memoizedColumns}
    />
  )
}

Sorting Optimization

const useSortedData = (data: T[], sortKey: keyof T | null, direction: 'asc' | 'desc') => {
  return useMemo(() => {
    if (!sortKey) return data

    return [...data].sort((a, b) => {
      const aValue = a[sortKey]
      const bValue = b[sortKey]

      if (aValue < bValue) return direction === 'asc' ? -1 : 1
      if (aValue > bValue) return direction === 'asc' ? 1 : -1
      return 0
    })
  }, [data, sortKey, direction])
}

Contributing

Data table components organize the digital chaos! Help us improve their sorting algorithms, add more customization options, and enhance their performance with large datasets.

Data Table - because data deserves beautiful presentation! 📊✨