DataTable-like functionality for interactive data grids with sorting, filtering, pagination, and more.
Domma.tables
Transform a table element into an interactive data grid.
// Create a table instance
const table = Domma.tables.create('#my-table', {
data: [
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'Admin' },
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'User' },
// ...more rows
],
columns: [
{ key: 'id', title: 'ID', sortable: true },
{ key: 'name', title: 'Name', sortable: true },
{ key: 'email', title: 'Email', sortable: true, editable: true },
{ key: 'role', title: 'Role', filterable: true },
{
key: 'actions',
title: '',
render: (val, row) => ``
}
],
pagination: true,
pageSize: 10,
selectable: true
});
// HTML
| Option | Type | Description |
|---|---|---|
key |
string | Property name in data object |
title |
string | Column header text |
sortable |
boolean | Enable sorting on this column |
filterable |
boolean | Enable filtering on this column |
editable |
boolean | Enable inline editing |
render |
function | Custom cell renderer (val, row) => html |
Use the render function to transform cell values — perfect for date formatting,
badges, or custom HTML.
// Format dates using Domma's date module
{
key: 'joined',
title: 'Joined',
sortable: true,
render: (val) => D(val).format('DD/MM/YYYY') // 15/01/2024
}
// Status badge with conditional styling
{
key: 'status',
title: 'Status',
render: (val) => `${val}`
}
// Action buttons with row data
{
key: 'actions',
title: '',
render: (val, row) => ``
}
Tip: Use width: '100px' or width:
'20%' to set column widths.
| Option | Type | Default | Description |
|---|---|---|---|
data |
array | [] | Array of row objects |
columns |
array | [] | Column configuration |
pagination |
boolean | false | Enable pagination |
pageSize |
number | 10 | Rows per page |
selectable |
boolean | false | Enable row selection |
selectionMode |
string | 'multiple' | 'single' or 'multiple' |
columnToggle |
boolean | false | Show column visibility dropdown |
This demo includes the Columns dropdown (to show/hide columns)
and the Export Panel (Copy/Download toggle). Note the Joined column
uses a custom render function to format dates as DD/MM/YYYY.
Sort data by one or multiple columns.
// Single column sort
table.sort('name', 'asc');
table.sort('age', 'desc');
// Multi-column sort
table.sortMultiple([
{ key: 'department', direction: 'asc' },
{ key: 'name', direction: 'asc' }
]);
// Clear sorting
table.clearSort();
// Get current sort state
const sortState = table.getSortState();
// { column: 'name', direction: 'asc' }
// Listen for sort events
table.on('sort', (column, direction) => {
console.log(`Sorted by ${column} ${direction}`);
});
Click column headers in the demo table above to sort. Click again to reverse order.
Filter data with text search or column-specific filters.
// Global text search
table.search('alice');
// Column filter
table.filter('role', 'Admin');
table.filter('status', 'active', 'equals');
// Filter operators
table.filter('age', 25, 'gt'); // Greater than
table.filter('age', 30, 'lt'); // Less than
table.filter('age', 25, 'gte'); // Greater or equal
table.filter('name', 'al', 'contains');
// Custom filter function
table.filterBy((row) => {
return row.age >= 25 && row.status === 'active';
});
// Clear filters
table.clearFilters();
// Listen for filter events
table.on('filter', (filters) => {
console.log('Active filters:', filters);
});
Navigate through large datasets with pagination controls.
// Create table with pagination
const table = Domma.tables.create('#table', {
data: largeDataset,
pagination: true,
pageSize: 25
});
// Navigate pages
table.page(1); // Go to page 1 (0-indexed)
table.nextPage();
table.prevPage();
table.firstPage();
table.lastPage();
// Change page size
table.pageSize(50);
// Get pagination info
const info = table.pageInfo();
// {
// currentPage: 0,
// pageSize: 25,
// totalPages: 10,
// totalRows: 250,
// startRow: 1,
// endRow: 25
// }
// Listen for page events
table.on('page', (page, pageSize) => {
console.log(`Page ${page + 1}`);
});
Select single or multiple rows for bulk operations.
// Enable selection
const table = Domma.tables.create('#table', {
data: data,
selectable: true,
selectionMode: 'multiple' // or 'single'
});
// Select rows
table.select(0); // Select first row
table.select([0, 1, 2]); // Select multiple rows
table.selectAll();
// Deselect
table.deselect(0);
table.deselectAll();
// Toggle selection
table.toggleSelect(0);
// Get selected rows
const selected = table.getSelected();
// Returns array of selected row data
const selectedIndices = table.getSelectedIndices();
// Returns array of selected row indices
// Listen for selection events
table.on('select', (selectedRows, selectedIndices) => {
console.log(`${selectedRows.length} rows selected`);
});
Use checkboxes in the demo table to select rows. Selected rows are highlighted.
Add, update, and remove data dynamically.
// Set all data
table.setData(newDataArray);
// Get all data (filtered/sorted state)
const currentData = table.getData();
// Get original data
const originalData = table.getData(true);
// Add rows
table.addRow({ id: 100, name: 'New User', ... });
table.addRows([{ ... }, { ... }]);
// Update row
table.updateRow(0, { name: 'Updated Name' });
table.updateRow(0, newRowData, true); // Replace entire row
// Remove rows
table.removeRow(0);
table.removeRows([0, 1, 2]);
// Clear all data
table.clear();
// Refresh table (re-render)
table.refresh();
Enable a built-in export toolbar with Copy/Download toggle and format buttons.
// Enable export panel
const table = Domma.tables.create('#my-table', {
data: myData,
columns: [...],
exportPanel: true, // Enable the export toolbar
exportOptions: ['copy', 'csv', 'excel', 'json'], // Choose which buttons to show
onExport: (info) => {
console.log(`Exported ${info.rowCount} rows as ${info.format}`);
}
});
// The panel shows:
// [ Copy (•)──Download ] | 📋 Text 📄 CSV 📊 Excel { } JSON
//
// Toggle LEFT = Copy mode (copies to clipboard)
// Toggle RIGHT = Download mode (downloads file)
| Option | Type | Default | Description |
|---|---|---|---|
exportPanel |
boolean | false | Show the export toolbar |
exportOptions |
array | ['copy', 'csv', 'excel', 'json'] | Which export buttons to display |
onExport |
function | null | Callback when data is exported |
Enable a dropdown to show/hide columns dynamically. Users can toggle column visibility without reloading the table.
// Enable column toggle dropdown
const table = Domma.tables.create('#my-table', {
data: myData,
columns: [
{ key: 'id', title: 'ID' },
{ key: 'name', title: 'Name' },
{ key: 'email', title: 'Email', visible: false }, // Hidden by default
{ key: 'role', title: 'Role' }
],
columnToggle: true // Enable the Columns dropdown
});
// Programmatic control
table.showColumn('email'); // Show a hidden column
table.hideColumn('role'); // Hide a visible column
table.toggleColumn('name'); // Toggle visibility
// Get column info
table.getVisibleColumns(); // Array of visible column configs
table.getHiddenColumns(); // Array of hidden column configs
table.getColumns(); // All columns
| Option | Type | Default | Description |
|---|---|---|---|
columnToggle |
boolean | false | Show the Columns dropdown in the toolbar |
regexSearch |
boolean | false | Show the regex toggle button next to search input |
visible (column) |
boolean | true | Initial visibility of a column |
Try the Columns dropdown in the Interactive Demo above to show/hide columns.
Customise row stripe and hover colours using named variants or custom hex/RGB values.
// Named variants (uses CSS variables)
const table1 = Domma.tables.create('#table', {
data: data,
striped: true,
evenRowColor: 'lighter', // var(--dm-gray-100)
oddRowColor: 'light', // var(--dm-gray-200)
hoverColor: 'medium' // var(--dm-gray-300)
});
// Custom hex colors
const table2 = Domma.tables.create('#table', {
data: data,
striped: true,
evenRowColor: '#ffffff',
oddRowColor: '#f9f9f9',
hoverColor: '#e6f2ff'
});
// Semantic tints
const table3 = Domma.tables.create('#table', {
data: data,
striped: true,
evenRowColor: 'none',
oddRowColor: 'primary-tint',
hoverColor: 'light'
});
// Named variants: 'none', 'lighter', 'light', 'medium', 'dark',
// 'primary-tint', 'success-tint', 'warning-tint',
// 'danger-tint', 'info-tint'
| Option | Type | Default | Description |
|---|---|---|---|
evenRowColor |
string | null (transparent) |
Colour for even rows (0, 2, 4...). Named variant or hex/rgb value. |
oddRowColor |
string | null ('#f9f9f9') |
Colour for odd rows (1, 3, 5...). Named variant or hex/rgb value. |
hoverColor |
string | null ('#f0f0f0') |
Colour for row hover state. Named variant or hex/rgb value. |
Interactive demo with different stripe configurations:
Programmatically export data in various formats.
// Copy to clipboard
table.copyToClipboard(); // Tab-separated (paste into Excel)
table.copyToClipboard('csv'); // CSV format
table.copyToClipboard('json'); // JSON format
// Get data as string
const csv = table.toCSV(); // CSV string
const json = table.toJSON(); // JSON string
const excel = table.toExcel(); // HTML table (Excel-compatible)
// Download files
table.download('csv', 'users.csv');
table.download('excel', 'users.xls');
table.download('json', 'users.json');
// Export callback
table.on('export', (info) => {
console.log(`Exported ${info.rowCount} rows as ${info.format}`);
});
Try the export buttons in the Interactive Demo above, or use these:
Listen to table events for custom behavior.
// Available events
table.on('sort', (column, direction) => { });
table.on('filter', (activeFilters) => { });
table.on('search', (query) => { });
table.on('page', (page, pageSize) => { });
table.on('select', (rows, indices) => { });
table.on('edit', (row, column, oldValue, newValue) => { });
table.on('render', () => { });
table.on('dataChange', (data) => { });
// Remove event listener
table.off('sort', handler);
// One-time listener
table.once('render', () => {
console.log('Table rendered for the first time');
});
While tables use Domma.tables.create(), you can still leverage Domma's config engine to
declaratively manage table setups, event handlers, and interactions.
// Define all table configurations in one place
const tableConfigs = {
users: {
selector: '#users-table',
options: {
data: userData,
columns: [
{ key: 'id', title: 'ID', sortable: true },
{ key: 'name', title: 'Name', sortable: true },
{ key: 'email', title: 'Email', sortable: true },
{ key: 'role', title: 'Role', filterable: true }
],
pagination: true,
pageSize: 20,
selectable: true,
exportPanel: true
},
events: {
select: (rows) => {
console.log(`${rows.length} users selected`);
},
edit: (row, column, oldVal, newVal) => {
// Save to server
api.updateUser(row.id, { [column]: newVal });
}
}
},
products: {
selector: '#products-table',
options: {
data: productData,
columns: [
{ key: 'sku', title: 'SKU', sortable: true },
{ key: 'name', title: 'Product', sortable: true },
{ key: 'price', title: 'Price', sortable: true, render: (val) => `$${val}` },
{ key: 'stock', title: 'Stock', sortable: true }
],
pagination: true,
pageSize: 50
}
}
};
// Initialize all tables
Object.values(tableConfigs).forEach(config => {
const table = Domma.tables.create(config.selector, config.options);
// Attach event handlers
if (config.events) {
Object.entries(config.events).forEach(([event, handler]) => {
table.on(event, handler);
});
}
});
// Enhance table containers with config engine
$.setup({
'.table-container': {
initial: {
css: {
background: 'var(--dm-gray-50)',
padding: 'var(--dm-space-4)',
borderRadius: 'var(--dm-radius-lg)'
}
},
events: {
ready: (e, $el) => {
// Create table when container is ready
const tableId = $el.find('table').attr('id');
const config = tableConfigs[tableId];
if (config) {
const table = Domma.tables.create(`#${tableId}`, config.options);
$el.data('tableInstance', table);
}
}
}
},
'#export-controls': {
events: {
'click .export-btn': (e, $el) => {
const format = $(e.target).data('format');
const table = $('.table-container').data('tableInstance');
if (table) {
table.download(format);
Domma.elements.toast.success(`Exported as ${format.toUpperCase()}`);
}
}
}
}
});
// Create a reusable table factory with config
class TableFactory {
static presets = {
users: {
columns: [
{ key: 'id', title: 'ID', sortable: true },
{ key: 'name', title: 'Name', sortable: true },
{ key: 'email', title: 'Email', sortable: true, editable: true },
{ key: 'role', title: 'Role', filterable: true }
],
pagination: true,
pageSize: 20,
exportPanel: true
},
products: {
columns: [
{ key: 'sku', title: 'SKU', sortable: true },
{ key: 'name', title: 'Product', sortable: true },
{ key: 'price', title: 'Price', sortable: true, render: (val) => `$${val}` },
{ key: 'stock', title: 'Stock', sortable: true }
],
pagination: true,
pageSize: 50
}
};
static create(selector, preset, data, customOptions = {}) {
const presetConfig = this.presets[preset];
if (!presetConfig) {
throw new Error(`Unknown preset: ${preset}`);
}
return Domma.tables.create(selector, {
...presetConfig,
...customOptions,
data
});
}
}
// Usage - clean and declarative
const usersTable = TableFactory.create('#users', 'users', userData);
const productsTable = TableFactory.create('#products', 'products', productData, {
pageSize: 100 // Override preset
});
// Use Domma models for reactive table state
const tableState = M.create({
data: { type: 'array', default: [] },
sortColumn: { type: 'string', default: '' },
sortDirection: { type: 'string', default: 'asc' },
currentPage: { type: 'number', default: 1 },
pageSize: { type: 'number', default: 20 },
selectedRows: { type: 'array', default: [] }
});
// Create table
const table = Domma.tables.create('#state-table', {
data: tableState.get('data'),
pageSize: tableState.get('pageSize'),
pagination: true
});
// Sync table events with state
table.on('sort', (column, direction) => {
tableState.set({ sortColumn: column, sortDirection: direction });
});
table.on('page', (page) => {
tableState.set({ currentPage: page });
});
table.on('select', (rows) => {
tableState.set({ selectedRows: rows });
});
// React to state changes
tableState.onChange('data', (newData) => {
table.setData(newData);
});
tableState.onChange('selectedRows', (rows) => {
$('#selection-count').text(`${rows.length} selected`);
});
Imperative (Scattered)
// Table 1
const table1 = Domma.tables.create('#t1', {
data: data1,
columns: [/*...*/],
pagination: true
});
table1.on('select', (rows) => {
console.log(rows);
});
// Table 2 - elsewhere in code
const table2 = Domma.tables.create('#t2', {
data: data2,
columns: [/*...*/],
pageSize: 50
});
// Configurations spread out
Configuration-Driven (Centralized)
// One config object
const config = {
table1: {
selector: '#t1',
options: {
data: data1,
columns: [/*...*/],
pagination: true
},
events: {
select: (rows) => console.log(rows)
}
},
table2: {
selector: '#t2',
options: { data: data2, pageSize: 50 }
}
};
// Single init
initTables(config);