Skip to main content

Overview

Solarecliente’s table components provide powerful data display capabilities with built-in sorting, filtering, pagination, and row actions. All tables are responsive and accessible.

DataTable Component

The primary component for displaying tabular data with full feature support.

Basic Usage

import { DataTable } from '@/components/tables';
import { ColumnDef } from '@/types/table';

const columns: ColumnDef[] = [
  {
    key: 'name',
    header: 'Client Name',
    sortable: true,
  },
  {
    key: 'email',
    header: 'Email',
    sortable: true,
  },
  {
    key: 'status',
    header: 'Status',
    sortable: true,
  },
];

function ClientList() {
  const { data, loading } = useClients();

  return (
    <DataTable
      columns={columns}
      data={data}
      loading={loading}
    />
  );
}

DataTable Props

columns
ColumnDef[]
required
Array of column definitions specifying how data should be displayed
data
any[]
required
Array of data objects to display in the table
loading
boolean
default:false
Show loading skeleton while data is being fetched
sortable
boolean
default:true
Enable column sorting functionality
filterable
boolean
default:true
Enable column filtering functionality
pagination
boolean | PaginationConfig
default:true
Enable pagination or provide custom pagination configuration
selectable
boolean
default:false
Enable row selection with checkboxes
onRowClick
function
Callback function when a row is clicked
rowActions
RowAction[]
Array of action buttons to display for each row

Column Definitions

Define how each column should render and behave.
{
  key: 'name',
  header: 'Client Name',
  sortable: true,
  width: '200px',
}

Column Configuration

key
string
required
Unique identifier for the column
header
string | ReactNode
required
Column header text or component
sortable
boolean
default:false
Enable sorting for this column
filterable
boolean
default:false
Enable filtering for this column
width
string
Column width (e.g., ‘200px’, ‘20%’, ‘auto’)
render
function
Custom render function: (value, row, index) => ReactNode
accessor
function
Custom accessor function to extract data: (row) => any

Sorting

Enable single or multi-column sorting.
<DataTable
  columns={columns}
  data={data}
  sortable
  defaultSort={{ key: 'name', direction: 'asc' }}
/>

Filtering

Provide flexible filtering options for users.
const columns = [
  {
    key: 'status',
    header: 'Status',
    filterable: true,
    filterType: 'select',
    filterOptions: [
      { value: 'active', label: 'Active' },
      { value: 'inactive', label: 'Inactive' },
      { value: 'pending', label: 'Pending' },
    ],
  },
  {
    key: 'name',
    header: 'Client Name',
    filterable: true,
    filterType: 'text',
    filterPlaceholder: 'Search clients...',
  },
  {
    key: 'installationDate',
    header: 'Installation Date',
    filterable: true,
    filterType: 'date',
  },
];

<DataTable
  columns={columns}
  data={data}
  filterable
  showFilterBar
/>

Filter Types

Text Filter

Free-form text search with debouncing

Select Filter

Dropdown selection from predefined options

Date Filter

Date range picker for temporal filtering

Number Filter

Numeric range with min/max inputs

Pagination

Customize pagination behavior and appearance.
<DataTable
  columns={columns}
  data={data}
  pagination
/>

Row Actions

Add action buttons for each row.
const rowActions = [
  {
    label: 'View',
    icon: 'eye',
    onClick: (row) => navigate(`/clients/${row.id}`),
  },
  {
    label: 'Edit',
    icon: 'pencil',
    onClick: (row) => openEditModal(row),
  },
  {
    label: 'Delete',
    icon: 'trash',
    variant: 'danger',
    onClick: (row) => handleDelete(row.id),
    confirm: {
      title: 'Delete Client',
      message: 'Are you sure you want to delete this client?',
    },
  },
];

<DataTable
  columns={columns}
  data={data}
  rowActions={rowActions}
/>

Row Selection

Enable multi-row selection for batch operations.
function ClientTable() {
  const [selectedRows, setSelectedRows] = useState([]);

  const handleBulkDelete = () => {
    const ids = selectedRows.map(row => row.id);
    deleteClients(ids);
  };

  return (
    <>
      {selectedRows.length > 0 && (
        <div className="bulk-actions">
          <span>{selectedRows.length} selected</span>
          <Button onClick={handleBulkDelete} variant="danger">
            Delete Selected
          </Button>
        </div>
      )}
      <DataTable
        columns={columns}
        data={data}
        selectable
        selectedRows={selectedRows}
        onSelectionChange={setSelectedRows}
      />
    </>
  );
}

Expandable Rows

Show additional details in expandable row sections.
const columns = [
  // ... regular columns
];

const expandedRowRender = (row) => (
  <div className="p-4 bg-gray-50">
    <h4 className="font-semibold mb-2">Additional Details</h4>
    <div className="grid grid-cols-2 gap-4">
      <div>
        <span className="text-gray-600">Address:</span>
        <p>{row.address}</p>
      </div>
      <div>
        <span className="text-gray-600">Phone:</span>
        <p>{row.phone}</p>
      </div>
      <div>
        <span className="text-gray-600">Installation Notes:</span>
        <p>{row.notes}</p>
      </div>
    </div>
  </div>
);

<DataTable
  columns={columns}
  data={data}
  expandable
  expandedRowRender={expandedRowRender}
/>

Complete Example

import { DataTable, ColumnDef } from '@/components/tables';
import { Badge, Button } from '@/components/ui';
import { useClients } from '@/hooks/useClients';

function ClientManagement() {
  const { data, loading, refetch } = useClients();
  const navigate = useNavigate();

  const columns: ColumnDef[] = [
    {
      key: 'name',
      header: 'Client Name',
      sortable: true,
      filterable: true,
      filterType: 'text',
      width: '200px',
    },
    {
      key: 'email',
      header: 'Email',
      sortable: true,
      filterable: true,
      filterType: 'text',
    },
    {
      key: 'status',
      header: 'Status',
      sortable: true,
      filterable: true,
      filterType: 'select',
      filterOptions: [
        { value: 'active', label: 'Active' },
        { value: 'inactive', label: 'Inactive' },
        { value: 'pending', label: 'Pending' },
      ],
      render: (value) => (
        <Badge
          variant={
            value === 'active' ? 'success' :
            value === 'pending' ? 'warning' : 'secondary'
          }
        >
          {value}
        </Badge>
      ),
    },
    {
      key: 'installationDate',
      header: 'Installation Date',
      sortable: true,
      filterable: true,
      filterType: 'date',
      render: (value) => new Date(value).toLocaleDateString(),
    },
    {
      key: 'panelCount',
      header: 'Panels',
      sortable: true,
      width: '100px',
    },
    {
      key: 'systemSize',
      header: 'System Size (kW)',
      sortable: true,
      render: (value) => `${value.toFixed(2)} kW`,
    },
  ];

  const rowActions = [
    {
      label: 'View',
      icon: 'eye',
      onClick: (row) => navigate(`/clients/${row.id}`),
    },
    {
      label: 'Edit',
      icon: 'pencil',
      onClick: (row) => navigate(`/clients/${row.id}/edit`),
    },
    {
      label: 'Delete',
      icon: 'trash',
      variant: 'danger',
      onClick: async (row) => {
        await deleteClient(row.id);
        refetch();
      },
      confirm: {
        title: 'Delete Client',
        message: `Are you sure you want to delete ${row.name}?`,
      },
    },
  ];

  return (
    <div className="space-y-4">
      <div className="flex justify-between items-center">
        <h1 className="text-2xl font-bold">Client Management</h1>
        <Button onClick={() => navigate('/clients/new')}>
          Add New Client
        </Button>
      </div>

      <DataTable
        columns={columns}
        data={data}
        loading={loading}
        sortable
        filterable
        pagination={{
          pageSize: 25,
          pageSizeOptions: [10, 25, 50, 100],
          showTotal: true,
        }}
        selectable
        rowActions={rowActions}
        onRowClick={(row) => navigate(`/clients/${row.id}`)}
        defaultSort={{ key: 'name', direction: 'asc' }}
      />
    </div>
  );
}

Best Practices

Virtual Scrolling

Use virtual scrolling for tables with 1000+ rows to maintain performance

Debounce Filters

Debounce text filter inputs to reduce unnecessary filtering operations

Server-Side Pagination

Use server-side pagination for large datasets to reduce initial load time

Loading States

Show skeleton loaders while data is being fetched for better UX

Build docs developers (and LLMs) love