Skip to main content

Vanilla JS

You don't need React, Angular, or Vue to use OGrid. The @alaarab/ogrid-js package gives you a sortable, filterable, editable data grid with the shared OGrid core through a simple class-based API — just a container element and an options object.

It's a good fit if you're working on an internal tool, a dashboard that can't take on a framework, or you're embedding a grid inside an existing non-framework app.

Install

npm install @alaarab/ogrid-js

Or drop it into an HTML file directly using a CDN (no build tools needed):

<script type="module">
import { OGrid } from 'https://cdn.jsdelivr.net/npm/@alaarab/ogrid-js/dist/esm/index.js';
</script>

Build a real example

Let's make an order management dashboard. It tracks orders by status, amount, and customer — sortable, filterable, and editable inline.

<div id="orders-grid" style="height: 500px;"></div>

<script type="module">
import { OGrid } from '@alaarab/ogrid-js';
import '@alaarab/ogrid-js/styles';

const grid = new OGrid(document.getElementById('orders-grid'), {
columns: [
{ columnId: 'orderId', name: 'Order #', sortable: true },
{ columnId: 'customer', name: 'Customer', sortable: true },
{
columnId: 'status',
name: 'Status',
sortable: true,
filterable: { type: 'multiSelect', options: ['Pending', 'Shipped', 'Delivered', 'Cancelled'] },
},
{
columnId: 'amount',
name: 'Amount',
type: 'numeric',
sortable: true,
editable: true,
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
{
columnId: 'date',
name: 'Order Date',
type: 'date',
sortable: true,
},
],
data: [
{ orderId: 'ORD-1001', customer: 'Acme Corp', status: 'Shipped', amount: 4200, date: '2024-03-01' },
{ orderId: 'ORD-1002', customer: 'Globex Inc', status: 'Pending', amount: 1850, date: '2024-03-03' },
{ orderId: 'ORD-1003', customer: 'Initech', status: 'Delivered', amount: 9300, date: '2024-02-28' },
{ orderId: 'ORD-1004', customer: 'Umbrella LLC', status: 'Cancelled', amount: 620, date: '2024-03-05' },
{ orderId: 'ORD-1005', customer: 'Acme Corp', status: 'Delivered', amount: 7100, date: '2024-02-20' },
],
getRowId: (row) => row.orderId,
pageSize: 20,
cellSelection: true,
statusBar: true,
});

// React to edits in real time
grid.on('cellValueChanged', ({ item, columnId, newValue }) => {
console.log(`Order ${item.orderId}: ${columnId} updated to ${newValue}`);
// sync to your backend here
});
</script>

Live demo

Vanilla JS - zero dependencies
Live

What you get out of the box

The JS package shares the same data model and most feature concepts as the framework packages, but browser-level interaction parity is still tracked package by package.

  • Sorting — click column headers
  • Filtering — multi-select dropdown on the Status column
  • Inline editing — double-click any Amount cell to edit
  • Cell selection — click and drag to select ranges
  • Keyboard navigation — arrow keys, Tab, Home/End, Ctrl+Arrow
  • Clipboard — Ctrl+C/V/X works like a spreadsheet
  • Undo/redo — Ctrl+Z/Y
  • Status bar — row count and selection aggregations
  • Pagination — page controls at the bottom

Constructor

const grid = new OGrid(containerElement, options);

The container element defines where the grid renders. OGrid fills it completely — give it an explicit height (CSS or inline).

Key options

OptionTypeDefaultDescription
columnsIColumnDef<T>[]requiredColumn definitions
dataT[]Client-side row data
dataSourceIDataSource<T>Server-side data source (use instead of data)
getRowId(item: T) => RowIdrequiredReturns a unique identifier per row
pageSizenumber20Rows per page
sort{ field, direction }Initial sort state
filtersIFiltersInitial filters
editablebooleanfalseEnable cell editing
cellSelectionbooleanfalseSpreadsheet-style range selection
rowSelection'single' | 'multiple'Row selection mode
sideBarboolean | ISideBarDefColumn visibility + filter panel
layoutMode'fill' | 'content''fill''fill' fills the container; 'content' sizes to data
pinnedColumnsRecord<string, 'left' | 'right'>Pin columns to left or right edge

Listening to events

Use .on() to subscribe to grid events:

grid.on('cellValueChanged', ({ item, columnId, oldValue, newValue }) => {
console.log(`${columnId} changed: ${oldValue}${newValue}`);
// save to backend
});

grid.on('selectionChange', ({ selectedItems, selectedRowIds }) => {
console.log(`${selectedItems.length} rows selected`);
updateActionBar(selectedItems, selectedRowIds);
});

grid.on('sortChange', ({ sort }) => {
if (!sort) return;
console.log(`Sorted by ${sort.field} ${sort.direction}`);
});
EventPayloadWhen it fires
cellValueChanged{ item, columnId, oldValue, newValue }A cell edit is committed
sortChange{ sort }User changes sort
filterChange{ filters }User changes filters
pageChange{ page }User navigates pages
selectionChange{ selectedItems, selectedRowIds }Row selection changes

Programmatic control

The grid API lives on grid.api:

// Swap in new data (re-renders efficiently)
grid.api.setRowData(freshData);

// Export what's currently displayed (respects active filters/sort)
grid.api.exportToCsv('orders.csv');

// Set sort programmatically
grid.api.setSort('amount', 'desc');

// Get current column state (widths, visibility, order)
const state = grid.api.getColumnState();

See the full JS API Reference for all available methods.

Theming

The default theme uses CSS custom properties. Override any of them to match your design system:

:root {
--ogrid-primary: #0066cc; /* buttons, active page */
--ogrid-selection: #0078d4; /* active cell outline */
--ogrid-bg: #ffffff; /* background */
--ogrid-fg: #242424; /* text color */
--ogrid-border: #e0e0e0; /* borders */
--ogrid-bg-subtle: #f3f2f1; /* header background */
--ogrid-bg-hover: #f5f5f5; /* row hover */
}

Dark mode works automatically if you rely on prefers-color-scheme, or you can opt into it explicitly:

[data-theme='dark'] {
--ogrid-bg: #1a1a24;
--ogrid-fg: #e0e0e0;
--ogrid-border: #333340;
}

Or skip the built-in theme entirely and write your own CSS targeting ogrid-* class names.

Cleanup

When your grid is no longer needed (navigating away, SPA route change, etc.), call destroy() to remove all DOM elements and event listeners:

grid.destroy();

What's next?

  • JS API Reference — full method and options reference
  • Features — feature guides, with JS notes where behavior differs from framework packages
  • Theming Guide — deep dive into CSS customization