npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vue-data-grid/engine

v0.0.0

Published

Headless table core for Vue: column model, row and column windows, CSS-variable geometry, row pipeline.

Readme

@vue-data-grid/engine

Headless table core for Vue 3. It models columns (declaration, groups, order, widths, pinning, hiding, group collapse and sort state), keeps that state in a store of your choice, and turns it into geometry: flex, position: sticky and CSS custom properties. It also windows rows and columns, and ships optional composables for work on rows and cells: sorting, streaming updates, grouping with aggregates, trees, selection, cell focus, cell ranges, editing with undo, paste and fill, recent cell changes and CSV.

There is no markup here: no components, no templates, no h() calls, no render types. The engine gives you a TableScope, and your own markup renders the table from it. It is the low-level layer: @vue-data-grid/core, the main package, builds on it with the render fields of columns, keyboard navigation, autosize from content and more.

Install

pnpm add @vue-data-grid/engine vue

Vue 3.5 or later is a peer dependency. The package is ESM-only and free of side effects.

Quick start

import { defineComponent, h, shallowRef } from 'vue';
import {
	defineColumn,
	defineColumns,
	getCellText,
	createTableScopeContext,
	useTableEngine,
	useTableGeometry,
} from '@vue-data-grid/engine';

interface Instrument {
	id: string;
	symbol: string;
	price: number;
}

const column = defineColumn<Instrument>();

const columns = defineColumns({
	symbol: column(row => row.symbol, { label: 'Symbol', width: 140 }),
	price: column(row => row.price, { label: 'Price', align: 'right', format: value => value.toFixed(2) }),
});

export default defineComponent({
	props: { rows: { type: Array as () => Instrument[], required: true } },
	setup(props) {
		const root = shallowRef<HTMLElement | null>(null);

		const engine = useTableEngine({
			columns,
			rows: () => props.rows,
			root,
			rowKey: 'id',
			rowHeight: 36,
			virtual: true,
		});

		createTableScopeContext(engine.scope);
		useTableGeometry(root, engine.layers);

		return () => h('div', { ref: root, style: 'overflow: auto; height: 400px' }, [
			h('div', { style: { position: 'relative', height: `${engine.totalSize.value}px` } },
				engine.items.value.map((item) => {
					const row = engine.scope.rows.value[item.index];

					return h('div', {
						key: item.key,
						'data-dg-index': item.index,
						style: { display: 'flex', position: 'absolute', top: `${item.start}px`, height: `${item.size}px` },
					}, engine.scope.renderedColumns.value.map(rendered => h('div', rendered.cellProps,
						rendered.column ? getCellText(rendered.column, row) : undefined)));
				})),
		]);
	},
});

A real table also renders a header from headerProps, handles gestures and memoizes rows. The core styles only the geometry of a cell: its width, grow factor and pin. align becomes a data-dg-align attribute, and the layout of a cell, alignment included, comes from CSS: the structural styles of @vue-data-grid/core (@vue-data-grid/core/style.css), or rules of your own on [data-dg-column] and [data-dg-align]. Without them the price column above is not right-aligned. The documentation site in docs/ documents @vue-data-grid/core, which re-exports this core: its core page maps the exports, and a guide on your own markup renders a table from the engine. Run it with pnpm docs from the repository root. Every export carries JSDoc with the details.

Service columns and row headers

A column holds data of the row unless it says kind: 'service': a checkbox, a row number, actions. Service columns are real columns, pinned, resized and moved like any other, but toCsv, useCellRanges, useCellChanges and autosize in @vue-data-grid/core leave them out unless told otherwise (includeService, or naming the columns).

One column names each row for assistive technology: the first shown data column, or every column with rowHeader: true; rowHeader: false on the first data column leaves the table without one. RenderedColumn.rowHeader marks it, and the column window always renders it in its place, with spacers on both sides, so a row keeps its accessible name while scrolled sideways. Spacers are told apart by key (dg-spacer-start, dg-spacer-start-1, …); every rendered column and group cell has a key for v-for.

What the core does, and what is left to you

| Core | Your markup | | --- | --- | | column order, widths, pinning, hiding, sort state | header, body, footer, rows and cells | | keeping that state in localStorage, memory or your own store | a server-side store, if you want one | | column groups: header rows, cells, collapse, keep-together | how groups look: rows, one line, a collapse control | | row and column windows, scrolling to a row or a column | pinned rows | | service columns (kind: 'service') and row headers | what service cells show: checkboxes, numbers, actions | | geometry as CSS variables, the layer of pinned cells (--dg-pinned-z-index), widths in one write, fit to viewport | styling, ARIA roles and attributes: the core gives the numbers | | stable references for memoization; a row moved in the data, moveRow | dragging rows and columns, as in @vue-data-grid/core/drag-and-drop | | sorting, streaming, grouping, trees, selection as separate composables | filtering, loading data, selection gestures, checkboxes | | cell focus and ranges: addresses by row key ({ key, column }) and by row index ({ index, column }), moves, adding ranges and taking cells out, reactivity per row, range rectangles, clipboard text | key bindings, tabindex, element.focus(), drawing ranges, clipboard writes | | editing: rights, parsing, validation, immutable writes with a source, onBeforeCommit, undo and redo | editors, their keys, showing errors, saving rows | | recent changes of cell values and their direction | the flash or arrow that shows them | | cell text through format, CSV, reading pasted text as a spreadsheet does and laying it over a selection | files, Blob, downloads, the paste event |

The engine learns about the right-hand side through insets, rows, and what must stay rendered: keepRows, keepColumns or scope.keepRendered.

Entry points

  • @vue-data-grid/engine is the stable API. Everything in the guides is imported from here.
  • @vue-data-grid/engine/internals exposes the building blocks useTableEngine is made of, for those who assemble their own engine. It is not covered by semver: signatures there change together with the engine, including in minor releases. Nothing from the root entry is repeated there.

Source layout

src/
	persist/        stores and usePersistedState: keeping any state between visits
	columns/        column declaration, order, layout, header sort, column state
	column-groups/  column groups: declaration, header rows, collapse
	engine/         useTableEngine and TableScope; column reconciliation, fit, resize
	virtual/        row and column windows, scrolling, viewport
	render/         what markup uses: CSS variables, cell styles
	rows/           row pipeline: sorting, streaming, grouping with aggregates, trees, selection, row keys
	cells/          cell addresses, focus, ranges, editing, history, paste, fill, cell changes, CSV
	shared/         stableComputed, the model ref, the row token

A folder is a feature: it holds both the pure logic and the composable on top of it. Reactivity lives only in use-* files. The other files never call ref, computed or watch, so they can be tested with plain function calls. The one exception is shared/stable-computed.ts, which is itself a reactive primitive.

Each folder has an index.ts with everything it exports to its neighbours and to the entry points. The root index.ts and internals.ts export only from those barrels, and tests/entries.spec.ts checks that every folder export lands in exactly one entry point.

Folders depend on each other in one direction only, by layers: shared, persist ← columns ← render, rows ← column-groups, virtual ← engine ← cells. A folder imports only folders of lower layers, and tests/folders.spec.ts fails on anything else, so any folder can be moved into a package of its own.

Tests

Run pnpm test from the repository root. tests/ mirrors src/.