DataTable

An advanced React table that supports filtering, sorting, and pagination out of the box.

<DataTableDemo />

Usage

import {
  DataTable,
  DataTableColumnDef,
  DataTableQuery,
  DataTableSort,
  useDataTable,
  EmptyFilterValue
} from "@raystack/apsara";

DataTable Props

PropTypeDefault
columns
any[]
-
data
T[]
-
mode
"client" | "server"
"client"
isLoading
boolean
false
defaultSort
any
-
query
DataTableQuery
-
onTableQueryChange
((query: DataTableQuery) => void)
-
onLoadMore
(() => Promise<void>)
-

DataTableQuery Interface

PropTypeDefault
filters
{ name: string; operator: FilterOperatorTypes; value: any; }[]
-
sort
{ key: string; order: "asc" | "desc"; }[]
-
group_by
string[]
-
search
string
-

DataTableColumn Interface

PropTypeDefault
accessorKey
string
-
header
string
-
columnType
"number" | "text" | "date" | "select"
-
enableSorting
boolean
-
enableColumnFilter
boolean
-
enableHiding
boolean
-
enableGrouping
boolean
-
filterOptions
FilterSelectOption[]
-
defaultHidden
boolean
-

DataTable.Content Props

PropTypeDefault
emptyState
ReactNode
-
zeroState
ReactNode
-
classNames
{ root?: string | undefined; table?: string | undefined; header?: string | undefined; body?: string | undefined; row?: string | undefined; }
-

Examples

Basic Usage

import { DataTable } from "@raystack/apsara";
 
const columns = [
  {
    accessorKey: "name",
    header: "Name",
    columnType: "text",
    enableSorting: true,
  },
  {
    accessorKey: "age",
    header: "Age",
    columnType: "number",
    enableSorting: true,
  },
];
const data = [
  { name: "John Doe", age: 30 },
  { name: "Jane Smith", age: 25 },
];
function MyTable() {
  return (
    <DataTable
      columns={columns}
      data={data}
      defaultSort={{ key: "name", order: "asc" }}>
      <DataTable.Toolbar />
      <DataTable.Content />
    </DataTable>
  );
}

Column Configuration

Columns can be configured with various options:

interface DataTableColumnDef<TData, TValue> {
  accessorKey: string; // Key to access data
  header: string; // Column header text
  columnType: "text" | "number" | "date" | "select"; // Data type
  enableSorting?: boolean; // Enable sorting
  enableColumnFilter?: boolean; // Enable filtering
  enableHiding?: boolean; // Enable column visibility toggle
  enableGrouping?: boolean; // Enable grouping
  filterOptions?: FilterSelectOption[]; // Options for select filter
  defaultHidden?: boolean; // Hide column by default
}

Filtering

The DataTable supports multiple filter types:

Filter types:

  • Text: equals, not equals, contains,
  • Number: equals, not equals, less than, less than or equal, greater than, greater than or equal
  • Date: equals, not equals, before, on or before, after, on or after
  • Select: equals, not equals

Sorting

Enable column sorting:

const columns = [
  {
    accessorKey: "name",
    header: "Name",
    enableSorting: true,
  },
];

Grouping

Group rows by same column data:

const columns = [
  {
    accessorKey: "category",
    header: "Category",
    enableGrouping: true,
    showGroupCount: true,
  },
];

Server-side Integration

function ServerTable() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState();
  const [isLoading, setIsLoading] = useState(false);
  const handleQueryChange = async (query: DataTableQuery) => {
    setIsLoading(true);
    setQuery(query);
    const response = await fetchData(query);
    setData(response.data);
    setIsLoading(false);
  };
  return (
    <DataTable
      data={data}
      query={query}
      columns={columns}
      isLoading={isLoading}
      mode="server"
      onTableQueryChange={handleQueryChange}>
      <DataTable.Toolbar />
      <DataTable.Content />
    </DataTable>
  );
}

Custom Styling

const columns = [
  {
    accessorKey: "name",
    header: "Name",
    classNames: {
      cell: "custom-cell",
      header: "custom-header",
    },
    styles: {
      cell: { fontWeight: "bold" },
      header: { backgroundColor: "#f5f5f5" },
    },
  },
];

Custom Cell Rendering

const columns = [
  {
    accessorKey: "status",
    header: "Status",
    cell: ({ row }) => (
      <Badge status={row.original.status}>{row.original.status}</Badge>
    ),
  },
];

Using DataTable Filter

The DataTable.Filters component can be used separately to filter data for custom views.

<DataTable
data={data}
query={query}
columns={columns}
mode="server"
onTableQueryChange={handleQueryChange}>
    <DataTable.Filters />
</DataTable>

The DataTable.Search component provides search functionality that automatically integrates with the table query. By default, it is disabled in zero state (when no data and no filters/search applied).

<DataTable
  data={data}
  columns={columns}
  defaultSort={{ name: "name", order: "asc" }}>
  <DataTable.Search />
  <DataTable.Content />
</DataTable>

Search Auto-Disable Behavior

By default, DataTable.Search is automatically disabled in zero state to provide a better user experience. You can override this behavior:

// Default: disabled in zero state
<DataTable.Search />
 
// Override: always enabled
<DataTable.Search autoDisableInZeroState={false} />
 
// Manual control: explicitly disable
<DataTable.Search disabled={true} />

The search will be automatically enabled when:

  • Data exists in the table
  • Filters are applied
  • A search query is already present

Empty States

The DataTable supports two types of empty states to provide better user experience:

Zero State

Zero state is shown when no data has been fetched initially (no filters or search applied). In this state, the filter bar is automatically hidden.

import { DataTable, EmptyState } from "@raystack/apsara";
import { OrganizationIcon } from "@raystack/apsara/icons";
 
<DataTable
  data={[]}
  columns={columns}
  defaultSort={{ name: "name", order: "asc" }}>
  <DataTable.Toolbar />
  <DataTable.Content
    zeroState={
      <EmptyState
        icon={<OrganizationIcon />}
        heading="No users yet"
        subHeading="Get started by creating your first user."
      />
    }
  />
</DataTable>

Empty State

Empty state is shown when initial data exists but no results match after applying filters or search. In this state, the filter bar remains visible so users can adjust their filters.

import { DataTable, EmptyState } from "@raystack/apsara";
import { OrganizationIcon, FilterIcon } from "@raystack/apsara/icons";
 
<DataTable
  data={initialData}
  columns={columns}
  defaultSort={{ name: "name", order: "asc" }}>
  <DataTable.Toolbar />
  <DataTable.Search />
  <DataTable.Content
    zeroState={
      <EmptyState
        icon={<OrganizationIcon />}
        heading="No users yet"
        subHeading="Get started by creating your first user."
      />
    }
    emptyState={
      <EmptyState
        icon={<FilterIcon />}
        heading="No users found"
        subHeading="We couldn't find any matches for that keyword or filter. Try alternative terms or check for typos."
      />
    }
  />
</DataTable>

Fallback Behavior

  • If zeroState is not provided, it falls back to emptyState
  • If neither zeroState nor emptyState is provided, a default empty state is shown
  • The filter bar visibility is automatically controlled based on the state

Custom Empty State Content

You can provide custom React components for both states:

import { DataTable, EmptyState, Flex, Text, Button } from "@raystack/apsara";
import { OrganizationIcon, FilterIcon } from "@raystack/apsara/icons";
 
<DataTable.Content
  zeroState={
    <Flex direction="column" gap={4} align="center" style={{ padding: "40px" }}>
      <OrganizationIcon width={48} height={48} style={{ opacity: 0.5 }} />
      <Flex direction="column" gap={2} align="center">
        <Text size={4} weight="medium">
          No data available
        </Text>
        <Text size={2} style={{ color: "var(--rs-color-text-subtle)" }}>
          There are no users in the system. Create your first user to get started.
        </Text>
      </Flex>
      <Button size="small">Create User</Button>
    </Flex>
  }
  emptyState={
    <EmptyState
      icon={<FilterIcon />}
      heading="No matches found"
      subHeading="Try adjusting your filters or search query."
    />
  }
/>