Sortable, Editable, Filterable Data Grid Web Component – data-grid.js

Category: Javascript , Table | September 2, 2026
Authorlekoala
Last UpdateSeptember 2, 2026
LicenseMIT
Views0 views
Sortable, Editable, Filterable Data Grid Web Component – data-grid.js

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.

  • action runs the row’s default action.
  • select toggles row selection.
  • none disables 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. Accepts eager or lazy.
  • columns (Column[], default []): Column definitions.
  • rowKey (String | Function, default "id"): Stable row identifier.
  • rowLabel (String | Function): Accessible row label.
  • sortable (Boolean, default false): Enables column sorting.
  • filterable (Boolean, default false): Displays column filters.
  • selectable (Boolean, default false): Enables checkbox row selection.
  • singleSelect (Boolean, default false): Enables radio-button selection.
  • selectVisibleOnly (Boolean, default true): Limits select-all to visible rows.
  • actions (Action[], default []): Client-defined row actions.
  • rowActions (Boolean, default false): Displays an actions column even when static actions are absent.
  • actionRenderer (Function): Global action content renderer.
  • rowClick (String, default "action"): Controls data-row clicks with action, select, or none.
  • collapseActions (Boolean, default false): Groups actions in a native popover where supported.
  • bulkActions (BulkAction[], default []): Actions that operate on current selection.
  • resizable (Boolean, default false): Enables column resizing.
  • reorder (Boolean, default false): Enables drag-based column reordering.
  • menu (Boolean, default false): Enables the column menu.
  • responsive (Boolean, default false): Enables responsive column behavior.
  • responsiveToggle (Boolean, default true): Displays the responsive disclosure control.
  • responsiveStartOpen (Boolean, default false): Starts responsive detail rows expanded.
  • rowDetails (Function): Renders application content for expanded rows.
  • rowDetailsStartOpen (Boolean, default false): Starts application row details expanded.
  • autosize (Boolean, default false): Measures widthless columns and assigns calculated widths.
  • autoheight (Boolean, default true): Fills the grid height on the final page.
  • autohidePager (Boolean, default false): Hides pagination when every row fits.
  • wrap (Boolean, default false): Permits multi-line data cells.
  • snapColumns (Boolean, default false): Enables proximity-based horizontal column snapping.
  • pageSizes (Number[], default [10,25,50,100,250]): Available page-size choices.
  • showPageSize (Boolean, default true): Displays the page-size selector.
  • filterDelay (Number, default 300): Text-filter debounce delay in milliseconds.
  • searchable (Boolean, default false): Displays the global search field.
  • searchPlaceholder (String, default ""): Global search placeholder.
  • searchDelay (Number, default 300): Global search debounce delay in milliseconds.
  • minSearchLength (Number, default 0): Minimum search length before applying a query.
  • density (String, default "default"): Row spacing preset.
  • saveState (Boolean, default false): 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, default false): 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 to field.
  • id (String): Stable column identifier. Defaults to field.
  • 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): Uses start, center, or end alignment.
  • format (String): Uses boolean, date, datetime, or number.
  • formatOptions (Object): Options passed to the corresponding Intl formatter.
  • editable (Boolean): Enables inline editing.
  • editableType (String): Input type used during editing.
  • validate (Function): Validates an edited value.
  • responsive (Number): Responsive priority. 0 keeps the column visible.
  • filterType (String): Uses text, select, boolean, number, or date.
  • 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

You Might Be Interested In:


Leave a Reply