
data-grid-component is a Web Component that turns local or server-backed tabular data into an interactive data grid.
It supports pagination, sorting, filtering, global search, inline editing, row selection, actions, responsive columns, and configurable cell formatting.
The component works through the native <data-grid> custom element and ES modules.
Static HTML tables can provide the initial rows and column definitions, while remote datasets can come from an HTTP endpoint or a custom JavaScript data source.
Features
- Local HTML tables and server-backed datasets.
- Server-side pagination, sorting, filtering, and search.
- Inline cell editing with validation.
- Checkbox and radio row selection.
- Row actions and bulk actions.
- Responsive columns and expandable row details.
- Resizable, reorderable, autosized, and frozen columns.
- Date, datetime, number, and boolean formatting.
- CSS custom properties and density presets.
- RTL layouts and configurable UI labels.
How To Use It
Installation
Load the core stylesheet and ES module build from a CDN.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/data-grid-component@3/dist/data-grid.min.css" > <script type="module" src="https://cdn.jsdelivr.net/npm/data-grid-component@3/dist/data-grid.min.js"> </script>
All import the package via NPM and import it into your project:
npm install data-grid-component
import { DataGrid } from "data-grid-component";
import "data-grid-component/styles";
Basic Usage
A regular HTML table can provide the initial columns and rows. The <data-grid> element takes control of later rendering after the component loads.
When neither src nor dataSource is configured, rows in <tbody> become the local dataset. Each tr[data-row-key] supplies the row identifier.
A td[data-value] stores the machine-readable value. Its displayed HTML can contain formatted text, badges, <time> elements, or other presentation markup. If data-value is absent, the component reads the cell text as a string.
The <th data-field> element defines a column. Supported column attributes include data-sortable, data-filterable, data-filter, data-responsive, data-hidden, data-editable, data-editable-type, data-transform, data-format, data-align, data-width, and data-min-width.
<data-grid
sortable
filterable
searchable
page-size="10"
>
<table>
<thead>
<tr>
<th data-field="customer" data-sort="asc">Customer</th>
<th data-field="email">Email</th>
<th data-field="plan" data-filter="select">Plan</th>
</tr>
</thead>
<tbody>
<tr data-row-key="101">
<td>Mira Chen</td>
<td>[email protected]</td>
<td data-value="pro">Pro</td>
</tr>
<tr data-row-key="102">
<td>Daniel Brooks</td>
<td>[email protected]</td>
<td data-value="starter">Starter</td>
</tr>
</tbody>
</table>
</data-grid>
Load Data From a Server
Set src when the grid should request rows from an HTTP endpoint.
<data-grid src="/api/customers" sortable filterable searchable selectable page-sizes="10,25,50" row-key="id" > </data-grid>
Server responses use a rows array and a total record count.
{
"rows": [
{
"id": 301,
"name": "Avery Johnson",
"status": "active"
}
],
"total": 142
}
Optional metadata can supply filter values and other server-driven data.
{
"rows": [],
"total": 142,
"meta": {
"filters": {
"status": [
{
"value": "active",
"text": "Active"
}
]
}
}
}
Pagination, sorting, filtering, and search state form the current query. Remote data sources receive that state when the grid loads a new result.
Constant request parameters can be supplied through params when the constructor API is used.
import { DataGrid } from "data-grid-component";
const customerGrid = new DataGrid({
src: "/api/customers",
params: {
region: "west"
},
sortable: true,
filterable: true,
searchable: true,
pageSizes: [20, 50, 100]
});
document.querySelector("#customer-grid").appendChild(customerGrid);
Lazy-Load Remote Data
The default loading mode is eager. Set loading="lazy" when the initial request should wait until the grid approaches the viewport.
<data-grid src="/api/archive" loading="lazy" sortable searchable > </data-grid>
Create Columns in JavaScript
The built-in formatters support boolean, date, datetime, and number.
date represents a calendar date. datetime represents an instant with time information. Number formatting uses Intl.NumberFormat, while date values use Intl.DateTimeFormat.
import { DataGrid } from "data-grid-component";
const grid = new DataGrid({
src: "/api/orders",
sortable: true,
filterable: true,
columns: [
{
field: "orderId",
title: "Order",
width: 120
},
{
field: "created",
title: "Created",
format: "date"
},
{
field: "total",
title: "Total",
format: "number",
formatOptions: {
style: "currency",
currency: "USD"
},
align: "end"
},
{
field: "paid",
title: "Paid",
format: "boolean"
}
]
});
document.body.appendChild(grid);
Inline Editing
Set editable on a column when its cells should enter edit mode.
import { DataGrid } from "data-grid-component";
const grid = new DataGrid({
columns: [
{
field: "product",
title: "Product"
},
{
field: "stock",
title: "Stock",
editable: true,
editableType: "number",
validate(value) {
return Number(value) >= 0 || "Stock cannot be negative";
}
}
]
});
Listen for the edit event when an edited value is committed.
grid.addEventListener("edit", function (event) {
const { data, value, field, column } = event.detail;
console.log(data, value, field, column);
});
Row Selection
Enable checkbox selection with the selectable attribute.
<data-grid src="/api/invoices" selectable select-visible-only="false" > </data-grid>
single-select switches the selection control to radio buttons.
<data-grid src="/api/accounts" single-select > </data-grid>
selectVisibleOnly defaults to true. The select-all control therefore selects rows visible in the current result by default.
The selection API can inspect or modify selection state.
const state = grid.getSelectionState(); grid.selectRow(row); grid.deselectRow(row); grid.toggleRow(row); grid.selectAll(); grid.clearSelection();
getSelectionState() returns an object with mode, ids, and except. This representation also works with server-backed datasets where the complete dataset does not exist in the browser.
Row Actions
Row actions can represent view, edit, approve, delete, or other record-specific operations.
import { DataGrid } from "data-grid-component";
const grid = new DataGrid({
src: "/api/members",
rowClick: "action",
actions: [
{
name: "view",
label: "View",
intent: "primary",
href: row => `/members/${row.id}`,
default: true
},
{
name: "delete",
label: "Delete",
intent: "danger",
confirm: row => `Delete ${row.name}?`
}
]
});
The action event reports the resolved action and row.
grid.addEventListener("action", function (event) {
const {
action,
name,
row,
rowKey,
rowIndex,
trigger
} = event.detail;
console.log(name, rowKey);
});
Actions can also come from declarative table markup.
<data-grid row-actions>
<table>
<thead>
<tr>
<th data-field="customer">Customer</th>
<th data-actions>Actions</th>
</tr>
</thead>
<tbody>
<tr data-row-key="501">
<td>Jordan Lee</td>
<td data-actions>
<a data-action="view" href="/customers/501">
View
</a>
<button
data-action="delete"
data-confirm="Delete this customer?"
>
Delete
</button>
</td>
</tr>
</tbody>
</table>
</data-grid>
Row Click Behavior
The rowClick option accepts three modes.
actionruns the row’s default action.selecttoggles row selection.nonedisables automatic row-click behavior.
A cancelable rowClick event fires before the configured behavior.
grid.addEventListener("rowClick", function (event) {
if (event.detail.row.locked) {
event.preventDefault();
}
});
Custom content can opt out through data-row-click-ignore.
<span data-row-click-ignore> Custom interactive content </span>
Responsive Columns
Enable responsive column handling with the responsive option.
<data-grid responsive responsive-start-open > </data-grid>
Set a column’s responsive value to control its priority.
columns: [
{
field: "customer",
title: "Customer",
responsive: 0
},
{
field: "phone",
title: "Phone",
responsive: 2
},
{
field: "notes",
title: "Notes",
responsive: 5
}
]
responsiveStartOpen displays hidden column values immediately inside the responsive detail area.
<data-grid responsive responsive-start-open responsive-toggle="false" > </data-grid>
Row Details
rowDetails renders application content beneath a row. Responsive hidden-column content and application row details can share the disclosure control. Set responsiveToggle: false when responsive values should stay open while application details use their own disclosure.
const grid = new DataGrid({
rowDetails: ({ row }) => {
const panel = document.createElement("div");
panel.textContent = `Account owner: ${row.owner}`;
return panel;
}
});
Scrollable Grid
A constrained grid height creates an internal scroll viewport.
.customer-results {
max-height: 70vh;
}
<data-grid class="customer-results" src="/api/customers" > </data-grid>
Column Formatting
Four built-in format values handle common data types.
const columns = [
{
field: "enabled",
format: "boolean"
},
{
field: "birthday",
format: "date"
},
{
field: "lastLogin",
format: "datetime"
},
{
field: "revenue",
format: "number",
formatOptions: {
style: "currency",
currency: "USD"
}
}
];
A column can also specify alignment and sizing independently.
{
field: "revenue",
format: "number",
align: "end",
minWidth: 100,
width: 140
}
Column Widths and Wrapping
minWidth acts as the minimum width. width specifies the preferred width. A column with no preferred width shares the available space.
Enable automatic measurement for widthless columns with autosize.
<data-grid autosize></data-grid>
Long text stays on one line by default. Set wrap for the entire grid or for individual columns.
columns: [
{
field: "title"
},
{
field: "description",
wrap: true
}
]
A frozen column can stay pinned to the logical start edge.
columns: [
{
field: "customer",
width: 220,
frozen: "start"
},
{
field: "email",
width: 280
}
]
Horizontal column snapping is available through snapColumns.
<data-grid snap-columns></data-grid>
Styling and Customization
The core stylesheet uses --dg-* custom properties for colors, controls, spacing, borders, selection states, and sizing.
data-grid {
--dg-bg: #ffffff;
--dg-color: #202124;
--dg-accent: #6750a4;
--dg-header-bg: #f7f5fa;
--dg-border-color: #ddd8e4;
--dg-row-hover-bg: #f9f7fc;
--dg-radius: 6px;
}
Available theme variables:
--dg-bg: Table and menu surfaces.--dg-color: Primary text.--dg-muted-color: Secondary text and placeholders.--dg-border-color: Main borders and separators.--dg-accent: Interactive accent.--dg-accent-soft: Soft accent background.--dg-focus-ring: Focus ring.--dg-header-bg: Header and footer background.--dg-header-color: Header text.--dg-filter-bg: Filter controls background.--dg-row-stripe-bg: Striped row background.--dg-row-hover-bg: Row hover background.--dg-row-selected-bg: Selected row background.--dg-row-selected-hover-bg: Selected row hover background.--dg-row-border-color: Row separators.--dg-control-bg: Buttons, inputs, and select backgrounds.--dg-control-color: Control text.--dg-control-border-color: Control borders.--dg-danger-bg: Error and danger background.--dg-danger-color: Error and danger text.--dg-danger-border-color: Error and danger borders.--dg-cell-padding-inline: Horizontal cell padding.--dg-cell-padding-block: Vertical cell padding.--dg-header-padding-y: Header vertical padding.--dg-control-height: Filter and footer control height.--dg-selection-column-width: Selection column width.--dg-actions-column-width: Collapsed actions column width.--dg-radius: Grid, control, and menu corner radius.
The density option changes the component spacing presets.
Supported values: compact, default, and comfortable.
<data-grid density="compact"></data-grid>
State selectors:
data-grid[data-loading] {}
data-grid[data-error] {}
data-grid[data-empty] {}
data-grid tr[data-selected] {}
data-grid td[data-editing] {}
data-grid td[data-invalid] {}
Configuration Options
src(String, default""): URL for a remote data endpoint.params(Object, default{}): Constant HTTP parameters sent with requests.dataSource(DataSource): Custom data source instance.loading(String, default"eager"): Initial loading mode. Acceptseagerorlazy.columns(Column[], default[]): Column definitions.rowKey(String | Function, default"id"): Stable row identifier.rowLabel(String | Function): Accessible row label.sortable(Boolean, defaultfalse): Enables column sorting.filterable(Boolean, defaultfalse): Displays column filters.selectable(Boolean, defaultfalse): Enables checkbox row selection.singleSelect(Boolean, defaultfalse): Enables radio-button selection.selectVisibleOnly(Boolean, defaulttrue): Limits select-all to visible rows.actions(Action[], default[]): Client-defined row actions.rowActions(Boolean, defaultfalse): Displays an actions column even when static actions are absent.actionRenderer(Function): Global action content renderer.rowClick(String, default"action"): Controls data-row clicks withaction,select, ornone.collapseActions(Boolean, defaultfalse): Groups actions in a native popover where supported.bulkActions(BulkAction[], default[]): Actions that operate on current selection.resizable(Boolean, defaultfalse): Enables column resizing.reorder(Boolean, defaultfalse): Enables drag-based column reordering.menu(Boolean, defaultfalse): Enables the column menu.responsive(Boolean, defaultfalse): Enables responsive column behavior.responsiveToggle(Boolean, defaulttrue): Displays the responsive disclosure control.responsiveStartOpen(Boolean, defaultfalse): Starts responsive detail rows expanded.rowDetails(Function): Renders application content for expanded rows.rowDetailsStartOpen(Boolean, defaultfalse): Starts application row details expanded.autosize(Boolean, defaultfalse): Measures widthless columns and assigns calculated widths.autoheight(Boolean, defaulttrue): Fills the grid height on the final page.autohidePager(Boolean, defaultfalse): Hides pagination when every row fits.wrap(Boolean, defaultfalse): Permits multi-line data cells.snapColumns(Boolean, defaultfalse): Enables proximity-based horizontal column snapping.pageSizes(Number[], default[10,25,50,100,250]): Available page-size choices.showPageSize(Boolean, defaulttrue): Displays the page-size selector.filterDelay(Number, default300): Text-filter debounce delay in milliseconds.searchable(Boolean, defaultfalse): Displays the global search field.searchPlaceholder(String, default""): Global search placeholder.searchDelay(Number, default300): Global search debounce delay in milliseconds.minSearchLength(Number, default0): Minimum search length before applying a query.density(String, default"default"): Row spacing preset.saveState(Boolean, defaultfalse): Persists query and column visibility state.errorMessage(String, default""): Message displayed after a load failure.noData(String, default""): Message displayed for an empty result.caption(String, default""): Table caption and accessible dataset name.initialQuery(QueryState): Initial query object.initialResult(PageResult): Initial result rendered before an asynchronous load.validate(Function): Grid-level editor validator.debug(Boolean, defaultfalse): Logs grid actions in DevTools.dir(String, default"ltr"): Text direction.id(String): Custom grid identifier.
HTML Attributes
src loading page page-size sortable filterable searchable search-placeholder search-delay min-search-length filter-delay responsive responsive-toggle responsive-start-open row-details-start-open selectable single-select select-visible-only row-click row-key row-label collapse-actions save-state no-data error-message page-sizes row-actions reorder menu wrap snap-columns autosize resizable autoheight autohide-pager show-page-size debug dir density
A bare Boolean attribute represents true.
<data-grid src="/api/customers" sortable filterable searchable selectable row-click="select" row-key="customerId" min-search-length="2" page-sizes="10,25,50" no-data="No matching customers" > </data-grid>
Column Options
field(String): Data property used by the column.title(String): Column heading. Defaults tofield.id(String): Stable column identifier. Defaults tofield.width(Number): Preferred column width.class(String): Class applied to header and body cells.attr(String): Writes the field value to a row attribute.hidden(Boolean): Hides the column.sortable(Boolean): Controls sorting for the column.filterable(Boolean): Controls filtering for the column.transform(String | Function): Transforms values before presentation.minWidth(Number): Minimum column width.align(String): Usesstart,center, orendalignment.format(String): Usesboolean,date,datetime, ornumber.formatOptions(Object): Options passed to the correspondingIntlformatter.editable(Boolean): Enables inline editing.editableType(String): Input type used during editing.validate(Function): Validates an edited value.responsive(Number): Responsive priority.0keeps the column visible.filterType(String): Usestext,select,boolean,number, ordate.filterList(FilterOption[]): Values used by a select filter.firstFilterOption(FilterOption): First empty select-filter item.filterMultiple(Boolean): Uses a multi-value select filter where browser support permits it.renderHeaderCell(Function): Custom header-cell renderer.renderFilterCell(Function): Custom filter-cell renderer.renderCell(Function): Custom body-cell renderer.cellClass(String | Function): Class applied to body cells for each row.wrap(Boolean): Overrides the grid-wide cell wrapping preference.frozen(String): Set to"start"to pin a column to the logical start edge.
Action Options
name(String): Action identifier.label(String): Button text and accessible label.intent("default" | "primary" | "danger"): Visual action intent.href(String | Function): Renders the action as a link.class(String): Custom action class.visible(Function): Controls visibility for each row.disabled(Boolean | Function): Blocks an action.render(Function): Custom action content renderer.confirm(Boolean | String | Function): Requests confirmation before dispatch.default(Boolean): Marks the primary row action.
API Methods
// Read a snapshot of the current query state.
grid.query;
// Read the current page.
grid.page;
// Read rows, total result count, and metadata.
grid.rows;
grid.total;
grid.meta;
// Read loading state and the last error.
grid.loading;
grid.error;
// Merge query values and reload.
grid.setQuery({
page: 2,
pageSize: 50
});
// Restore the initial query and reload.
grid.resetQuery();
// Reload the current query.
grid.refresh();
grid.load();
// Read normalized columns for the current render cycle.
grid.getColumns();
// Change column visibility.
grid.showColumn("email");
grid.hideColumn("email");
// Read available values for a select filter.
grid.getFilterOptions(column);
// Read server-aware selection state.
grid.getSelectionState();
// Test one row's selection state.
grid.isRowSelected(row);
// Change one row's selection state.
grid.selectRow(row);
grid.deselectRow(row);
grid.toggleRow(row);
// Select or clear rows.
grid.selectAll();
grid.clearSelection();
// Read selected rows available on the current page.
grid.getSelection();
// Update the global search value.
grid.setSearch("active");
grid.clearSearch();
// Update a row in the current result.
grid.updateRow(42, {
status: "approved"
});
// Remove a row from a mutable local dataset.
grid.removeRow(42);
// Navigate through pages.
grid.getFirst();
grid.getPrev();
grid.getNext();
grid.getLast();
// Clear active column filters.
grid.clearFilters();
// Change sorting.
grid.sortAsc("name");
grid.sortDesc("created");
grid.sortNone("created");
// Register plugin constructors.
DataGrid.registerPlugins({
CustomPlugin
});
// Read or replace global UI labels.
DataGrid.getLabels();
DataGrid.setLabels({
noData: "Nothing found"
});
// Fetch labels from a JSON file.
DataGrid.loadLabels("/locales/fr.json");
// Refresh labels on one connected grid.
grid.updateLabels();
Events
// Fires when the custom element connects.
grid.addEventListener("connected", function (event) {
console.log(event);
});
// Fires when the custom element disconnects.
grid.addEventListener("disconnected", function (event) {
console.log(event);
});
// Fires after a data request fails.
grid.addEventListener("loadError", function (event) {
console.log(event.detail);
});
// Fires when row selection changes.
grid.addEventListener("selectionChange", function (event) {
console.log(event.detail.selectionState);
});
// Fires when a column becomes visible or hidden.
grid.addEventListener("columnVisibility", function (event) {
console.log(event.detail.col, event.detail.visibility);
});
// Fires after column resizing.
grid.addEventListener("columnResized", function (event) {
console.log(event.detail.col, event.detail.width);
});
// Fires after column reordering.
grid.addEventListener("columnReordered", function (event) {
console.log(event.detail.col, event.detail.from, event.detail.to);
});
// Fires after the header renders.
grid.addEventListener("headerRendered", function (event) {
console.log(event);
});
// Fires after the body renders.
grid.addEventListener("bodyRendered", function (event) {
console.log(event);
});
// Fires for each rendered row.
grid.addEventListener("rowRendered", function (event) {
console.log(event.detail.rowData, event.detail.tr);
});
// Fires before automatic row-click behavior.
// Calling preventDefault() cancels that behavior.
grid.addEventListener("rowClick", function (event) {
console.log(event.detail.row, event.detail.rowKey);
});
// Fires after a row action.
grid.addEventListener("action", function (event) {
console.log(
event.detail.action,
event.detail.name,
event.detail.row,
event.detail.rowKey,
event.detail.rowIndex,
event.detail.trigger
);
});
// Fires after a bulk action.
grid.addEventListener("bulkAction", function (event) {
console.log(
event.detail.action,
event.detail.name,
event.detail.selection,
event.detail.query,
event.detail.trigger
);
});
// Fires when an edit is committed.
// The event can be canceled.
grid.addEventListener("edit", function (event) {
console.log(
event.detail.data,
event.detail.value,
event.detail.field,
event.detail.column
);
});
// Fires when row details open or close.
grid.addEventListener("rowDetailsToggle", function (event) {
console.log(
event.detail.row,
event.detail.rowKey,
event.detail.expanded
);
});
Translations
UI labels can be replaced globally.
DataGrid.setLabels({
noData: "No records",
booleanTrue: "Yes",
booleanFalse: "No"
});
A JSON translation file can also be loaded at runtime.
await DataGrid.loadLabels("/locales/es.json");
Call updateLabels() when one connected grid needs to refresh its rendered labels.
grid.updateLabels();
Alternatives
- Advanced Data Grid/Table Library in Vanilla JavaScript – VanillaGrid
- Interactive And Customizable Data Table/Grid Web Component – Active Table
- Add Sortable and Filterable Functionalities to HTML Tables with the Action Table Web Component
- Editable Spreadsheet Style Data Grid Library – simple-data-table







