Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ephraimduncan/blocks/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Table blocks provide powerful components for displaying and managing tabular data. From simple data tables to complex interfaces with accordion rows, filters, and inline actions, these components help you build comprehensive data management interfaces.

Available Blocks

Table with Accordion

Expandable table rows revealing additional details

Table with Actions

Data table with row-level action menus

Table with Filters

Filterable table with search and column filters

Table with Grouped Rows

Table with grouped or categorized rows

Data Table

Comprehensive data table with sorting and pagination

Table Features

  • Sorting: Click column headers to sort data
  • Filtering: Search and filter table contents
  • Pagination: Navigate large datasets efficiently
  • Row Actions: Contextual actions for each row
  • Selection: Select single or multiple rows
  • Expandable Rows: Show/hide additional details
  • Responsive: Adapt to different screen sizes

Use Cases

Display user lists with actions like edit, delete, or change permissions. Use row selection for bulk operations.
Show orders with expandable rows revealing order items, status history, and customer details.
Present product listings with filters for category, price, availability, and other attributes.
Display audit logs or transaction histories with date filters, search, and detailed row expansion.
Create data-rich reports with grouping, sorting, and export capabilities.

Implementation Example

import { DataTable } from '@/components/blocks/table-05'
import { useState } from 'react'

interface User {
  id: string
  name: string
  email: string
  role: string
  status: 'active' | 'inactive'
  createdAt: Date
}

export default function UsersTable() {
  const [users, setUsers] = useState<User[]>([])
  const [sorting, setSorting] = useState({ column: 'name', direction: 'asc' })
  const [currentPage, setCurrentPage] = useState(1)
  const pageSize = 10
  
  const columns = [
    {
      key: 'name',
      label: 'Name',
      sortable: true
    },
    {
      key: 'email',
      label: 'Email',
      sortable: true
    },
    {
      key: 'role',
      label: 'Role',
      sortable: true
    },
    {
      key: 'status',
      label: 'Status',
      render: (value: string) => (
        <span className={`
          px-2 py-1 rounded-full text-xs
          ${value === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}
        `}>
          {value}
        </span>
      )
    },
    {
      key: 'actions',
      label: '',
      render: (_, row: User) => (
        <DropdownMenu>
          <DropdownMenuItem onClick={() => editUser(row.id)}>Edit</DropdownMenuItem>
          <DropdownMenuItem onClick={() => deleteUser(row.id)}>Delete</DropdownMenuItem>
        </DropdownMenu>
      )
    }
  ]
  
  return (
    <DataTable
      columns={columns}
      data={users}
      sorting={sorting}
      onSortChange={setSorting}
      currentPage={currentPage}
      pageSize={pageSize}
      totalItems={users.length}
      onPageChange={setCurrentPage}
    />
  )
}

Sorting Implementation

function sortData<T>(data: T[], column: keyof T, direction: 'asc' | 'desc'): T[] {
  return [...data].sort((a, b) => {
    const aValue = a[column]
    const bValue = b[column]
    
    if (aValue < bValue) return direction === 'asc' ? -1 : 1
    if (aValue > bValue) return direction === 'asc' ? 1 : -1
    return 0
  })
}

const handleSort = (column: string) => {
  setSorting(prev => ({
    column,
    direction: prev.column === column && prev.direction === 'asc' ? 'desc' : 'asc'
  }))
}
For large datasets (>1000 rows), implement server-side sorting, filtering, and pagination to maintain good performance. Client-side operations should only be used for smaller datasets.
Implement table filtering:
import { TableWithFilters } from '@/components/blocks/table-03'
import { useState } from 'react'

function FilterableTable() {
  const [searchQuery, setSearchQuery] = useState('')
  const [filters, setFilters] = useState({
    role: 'all',
    status: 'all'
  })
  
  const filteredData = data.filter(row => {
    // Text search
    const matchesSearch = 
      row.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
      row.email.toLowerCase().includes(searchQuery.toLowerCase())
    
    // Role filter
    const matchesRole = filters.role === 'all' || row.role === filters.role
    
    // Status filter
    const matchesStatus = filters.status === 'all' || row.status === filters.status
    
    return matchesSearch && matchesRole && matchesStatus
  })
  
  return (
    <div>
      <div className="mb-4 flex gap-4">
        <input
          type="search"
          placeholder="Search users..."
          value={searchQuery}
          onChange={(e) => setSearchQuery(e.target.value)}
          className="flex-1"
        />
        
        <select
          value={filters.role}
          onChange={(e) => setFilters({ ...filters, role: e.target.value })}
        >
          <option value="all">All Roles</option>
          <option value="admin">Admin</option>
          <option value="user">User</option>
        </select>
        
        <select
          value={filters.status}
          onChange={(e) => setFilters({ ...filters, status: e.target.value })}
        >
          <option value="all">All Status</option>
          <option value="active">Active</option>
          <option value="inactive">Inactive</option>
        </select>
      </div>
      
      <TableWithFilters data={filteredData} />
    </div>
  )
}

Best Practices

Table Design

  • Column Width: Set appropriate widths for different content types
  • Text Alignment: Left-align text, right-align numbers
  • Row Height: Ensure adequate padding for readability
  • Zebra Striping: Use alternating row colors for easier scanning
  • Sticky Headers: Keep headers visible when scrolling
  • Responsive: Consider card view for mobile devices

Data Handling

  • Loading States: Show skeleton rows while data loads
  • Empty States: Provide helpful messages when no data exists
  • Error Handling: Display errors gracefully
  • Optimistic Updates: Update UI immediately, sync in background
  • Debounce Search: Don’t filter on every keystroke

User Experience

  • Clear Actions: Make row actions discoverable but not cluttered
  • Bulk Operations: Allow multi-select for batch actions
  • Keyboard Navigation: Support arrow keys and shortcuts
  • Export Options: Provide CSV or Excel export when appropriate
  • Customize Columns: Let users show/hide columns

Pagination

Implement pagination:
function Pagination({ 
  currentPage, 
  totalPages, 
  onPageChange 
}: { 
  currentPage: number
  totalPages: number
  onPageChange: (page: number) => void 
}) {
  return (
    <div className="flex items-center justify-between mt-4">
      <div className="text-sm text-gray-600">
        Page {currentPage} of {totalPages}
      </div>
      
      <div className="flex gap-2">
        <button
          onClick={() => onPageChange(currentPage - 1)}
          disabled={currentPage === 1}
        >
          Previous
        </button>
        
        {Array.from({ length: totalPages }, (_, i) => i + 1).map(page => (
          <button
            key={page}
            onClick={() => onPageChange(page)}
            className={currentPage === page ? 'font-bold' : ''}
          >
            {page}
          </button>
        ))}
        
        <button
          onClick={() => onPageChange(currentPage + 1)}
          disabled={currentPage === totalPages}
        >
          Next
        </button>
      </div>
    </div>
  )
}

Expandable Rows

Implement accordion-style rows:
import { TableWithAccordion } from '@/components/blocks/table-01'
import { useState } from 'react'

function ExpandableTable() {
  const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
  
  const toggleRow = (id: string) => {
    setExpandedRows(prev => {
      const next = new Set(prev)
      if (next.has(id)) {
        next.delete(id)
      } else {
        next.add(id)
      }
      return next
    })
  }
  
  return (
    <table>
      <tbody>
        {data.map(row => (
          <React.Fragment key={row.id}>
            <tr onClick={() => toggleRow(row.id)}>
              <td>
                <ChevronIcon 
                  className={expandedRows.has(row.id) ? 'rotate-90' : ''}
                />
              </td>
              <td>{row.name}</td>
              <td>{row.email}</td>
            </tr>
            
            {expandedRows.has(row.id) && (
              <tr>
                <td colSpan={3}>
                  <div className="p-4 bg-gray-50">
                    {/* Expanded content */}
                    <h4>Additional Details</h4>
                    <p>Created: {row.createdAt}</p>
                    <p>Last login: {row.lastLogin}</p>
                  </div>
                </td>
              </tr>
            )}
          </React.Fragment>
        ))}
      </tbody>
    </table>
  )
}

Row Selection

Implement row selection:
function SelectableTable() {
  const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set())
  
  const toggleRow = (id: string) => {
    setSelectedRows(prev => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }
  
  const toggleAll = () => {
    if (selectedRows.size === data.length) {
      setSelectedRows(new Set())
    } else {
      setSelectedRows(new Set(data.map(row => row.id)))
    }
  }
  
  return (
    <>
      {selectedRows.size > 0 && (
        <div className="mb-4">
          {selectedRows.size} rows selected
          <button onClick={() => bulkDelete(Array.from(selectedRows))}>
            Delete Selected
          </button>
        </div>
      )}
      
      <table>
        <thead>
          <tr>
            <th>
              <input
                type="checkbox"
                checked={selectedRows.size === data.length}
                onChange={toggleAll}
              />
            </th>
            <th>Name</th>
            <th>Email</th>
          </tr>
        </thead>
        <tbody>
          {data.map(row => (
            <tr key={row.id}>
              <td>
                <input
                  type="checkbox"
                  checked={selectedRows.has(row.id)}
                  onChange={() => toggleRow(row.id)}
                />
              </td>
              <td>{row.name}</td>
              <td>{row.email}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </>
  )
}

Responsive Tables

Handle mobile responsiveness:
// Card view for mobile
function ResponsiveTable({ data }: { data: any[] }) {
  const isMobile = useMediaQuery('(max-width: 768px)')
  
  if (isMobile) {
    return (
      <div className="space-y-4">
        {data.map(row => (
          <div key={row.id} className="border rounded-lg p-4">
            <div className="font-semibold">{row.name}</div>
            <div className="text-sm text-gray-600">{row.email}</div>
            <div className="mt-2">
              <span className="text-xs bg-gray-100 px-2 py-1 rounded">
                {row.role}
              </span>
            </div>
          </div>
        ))}
      </div>
    )
  }
  
  return <Table data={data} /> // Desktop table
}

Accessibility

  • Use semantic <table> elements
  • Include <caption> for table description
  • Use <thead>, <tbody>, <tfoot> properly
  • Add scope attributes to headers
  • Ensure keyboard navigation works
  • Provide sort direction indicators
  • Make action buttons keyboard accessible
  • Announce dynamic updates to screen readers
  • Grid List - Alternative grid layout
  • Stats - Metric summaries above tables
  • Dialogs - Modal forms for row editing