Skip to content
Reeflow
Start Building

Table Component

The <reeflow-table> component displays data in a tabular format with support for row clicks, extensible sorting, and client-side row limiting.

import { ReeflowProvider, ReeflowTable } from '@reeflow/react';
<ReeflowProvider authToken="your-token">
<ReeflowTable
title="Customer Orders"
query={{
from: 'orders',
dimensions: ['customer_name', 'order_date'],
measures: [
{ column: 'revenue', type: 'sum' },
{ column: 'quantity', type: 'sum' },
],
}}
/>
</ReeflowProvider>;

Display a title above the table:

<reeflow-table title="Sales Report">
<!-- query -->
</reeflow-table>

Maximum number of rows to display. Default: 20

This limits the number of rows rendered from the data result. For true pagination with navigation, you need to implement it using JSONQL limit and offset (see Advanced usage).

<reeflow-table page-size="50">
<!-- query -->
</reeflow-table>

Override the theme inherited from the Provider:

<reeflow-table theme="ocean">
<!-- query -->
</reeflow-table>

Override the theme mode (light/dark/auto):

<reeflow-table theme-mode="dark">
<!-- query -->
</reeflow-table>

The table component provides several properties and methods for programmatic control:

  • data - Set or get table data directly without API call
  • refresh() - Reload data from the API
  • reset(clearQuery?) - Reset table state

Example:

const table = document.querySelector('reeflow-table');
// Set data directly
table.data = {
columns: [{ name: 'Product', type: 'string' }],
rows: [['Widget A'], ['Widget B']],
};
// Get current data
console.log(table.data);
// Reload from API
await table.refresh();
// Reset state
table.reset();

For complete API documentation, see the SDK Reference.

The table component emits lifecycle and interaction events.

  • reeflow-state-change - Fired when component state changes (detail: { state: ReeflowComponentState, oldState: ReeflowComponentState })
  • reeflow-data-loaded - Data successfully loaded (detail: QueryResult)
  • reeflow-error - Error occurred during loading (detail: ApiError with code, message, and optional details)

In React, use event handler props with automatic detail extraction:

import { ReeflowTable } from '@reeflow/react';
import { useState } from 'react';
function OrdersTable() {
const [selectedRow, setSelectedRow] = useState(null);
return (
<div>
<ReeflowTable
query={query}
onTableRowClick={(detail) => {
console.log('Row clicked:', detail.row);
console.log('Row index:', detail.rowIndex);
setSelectedRow(detail.row);
}}
onTableSortChange={(detail) => {
console.log('Sort column:', detail.column);
console.log('Sort direction:', detail.direction);
}}
/>
{selectedRow && (
<div className="selection-panel">
<h3>Selected Row</h3>
<pre>{JSON.stringify(selectedRow, null, 2)}</pre>
</div>
)}
</div>
);
}

Available table event handlers:

  • onTableRowClick - Fired when a table row is clicked (receives { row, rowIndex })
  • onTableSortChange - Fired when a column header is clicked (receives { column, direction })

For complete event details and TypeScript types, see the SDK Reference.

The table exposes several CSS parts for custom styling:

/* Container */
reeflow-table::part(container) {
padding: 20px;
background: white;
border-radius: 8px;
}
/* Title */
reeflow-table::part(title) {
font-size: 24px;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 16px;
}
/* Table element */
reeflow-table::part(table) {
width: 100%;
border-collapse: collapse;
}
/* Header row */
reeflow-table::part(header) {
background: #f5f5f5;
}
reeflow-table::part(header-row) {
border-bottom: 2px solid #e0e0e0;
}
reeflow-table::part(header-cell) {
padding: 12px 16px;
text-align: left;
font-weight: 600;
cursor: pointer;
}
reeflow-table::part(header-cell):hover {
background: #ebebeb;
}
/* Body rows */
reeflow-table::part(body) {
background: white;
}
reeflow-table::part(row) {
border-bottom: 1px solid #e0e0e0;
transition: background 0.2s;
}
reeflow-table::part(row):hover {
background: #f9f9f9;
}
reeflow-table::part(cell) {
padding: 12px 16px;
}
/* Loading state */
reeflow-table::part(loading) {
display: flex;
align-items: center;
gap: 12px;
padding: 40px;
color: #666;
}
/* Error state */
reeflow-table::part(error) {
text-align: center;
padding: 40px;
color: #d32f2f;
}
/* Empty state */
reeflow-table::part(empty) {
text-align: center;
padding: 40px;
color: #999;
}

The table component provides events and extensibility points for implementing advanced features. Here are examples of common implementations.

The table does not sort data automatically. You can implement sorting by listening to the table-sort-change event and updating the query:

const table = document.querySelector('reeflow-table');
let currentSort = { column: null, direction: 'asc' };
table.addEventListener('reeflow-table-sort-change', (event) => {
const column = event.detail.column;
// Toggle direction if same column
if (currentSort.column === column) {
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
} else {
currentSort.column = column;
currentSort.direction = 'asc';
}
// Update query with order_by
table.query = {
...table.query,
order_by: [
{
column: column,
direction: currentSort.direction,
},
],
};
});

The page-size attribute only limits displayed rows. For full pagination with navigation, implement it using JSONQL limit and offset:

const table = document.querySelector('reeflow-table');
let currentPage = 0;
const pageSize = 20;
function goToPage(page) {
currentPage = page;
table.query = {
...table.query,
limit: pageSize,
offset: page * pageSize,
};
}
// Navigation buttons
document.getElementById('next').addEventListener('click', () => {
goToPage(currentPage + 1);
});
document.getElementById('prev').addEventListener('click', () => {
if (currentPage > 0) {
goToPage(currentPage - 1);
}
});

The table emits click events for rows but does not manage selection state. You can implement row selection tracking using the table-row-click event:

const table = document.querySelector('reeflow-table');
const selectedRows = new Set();
table.addEventListener('reeflow-table-row-click', (event) => {
const rowIndex = event.detail.rowIndex;
if (selectedRows.has(rowIndex)) {
selectedRows.delete(rowIndex);
} else {
selectedRows.add(rowIndex);
}
console.log('Selected rows:', Array.from(selectedRows));
});

The table component is fully typed when using TypeScript:

import type { ReeflowTable } from '@reeflow/core';
const table = document.querySelector('reeflow-table') as ReeflowTable;
// All methods and properties are typed
table.query = {
/* ... */
};
await table.refresh();

For complete type definitions, see the SDK Reference.