Ten million rows.
Thirty milliseconds.
The data grid is the most demanding component you ship. Elyra DataGrid is the only one that is server-driven, scales to millions of rows, works across Livewire, Vue and Svelte, and follows your Tailwind theme — all at once. One protocol, three clients, one PHP server.
Not a screenshot.
the real engine.
This grid runs the exact client-core engine that powers the Livewire, Vue and Svelte clients — here against 2,400 rows in your browser. Sort a column, search, filter by region, page through. In production the same engine talks to the PHP server and only ever queries a viewport, so ten million rows feel identical.
Sample data, generated in-browser. The production grid is server-driven over the DataGrid protocol.
This is a deliberately minimal setup — just sorting, search, a filter and paging. The same grid configures a lot more, each an opt-in flag on the definition:
See the grid definition guide for the full feature surface.
Grids force a compromise.
This one doesn't.
Client-bound grids fall over past a few thousand rows. Ecosystem-locked grids marry you to one framework. Headless cores leave the server, filtering UI, editing and export as homework. Elyra DataGrid makes four decisions instead.
One protocol, many clients
A small JSON contract — GridRequest / GridResponse — generates both the TypeScript types and the PHP DTOs from one source. The Livewire, Vue and Svelte clients are genuinely interchangeable and always in sync. You are never locked into one frontend.
Server-driven by design
Sorting, filtering, grouping, aggregation, search and paging run in the database, never in JavaScript. The client renders only a viewport — so the grid behaves the same over 100 rows and 10,000,000. The work is O(viewport), not O(data).
Native to ElyraSQL, portable everywhere
The server turns database features straight into grid features: FACET value lists with counts, per-level subtotals, percentile/median footers, HYBRID semantic + full-text search, and streamed cursor exports of millions of rows at constant memory. Dialect drivers mean it also runs on MySQL, PostgreSQL, ClickHouse, and SQLite / Elyra SQL Anywhere.
Follows your app
A Tailwind v4-native theme that inherits your palette, accent and dark mode automatically — it looks like part of your app on day one, not a bolted-on widget.
One grid, your stack
Pick the client. Keep the grid.
The same features, keyboard spec and server — three first-class clients that share a headless TypeScript core and one PHP package.
Grid state lives in the component; the protocol request is built and answered during render. Drop it into a Blade view — no build step, no API endpoint to wire. It did not compile under Livewire 4 at all in 0.4.0 or 0.5.0: Livewire 4 injects a PHP block into the opening tag of a looped element carrying wire:key, and Blade's component-tag compiler cannot parse an opening tag containing PHP. Nothing caught it because no test had ever rendered the component — orchestra/testbench and phpunit were dev dependencies and entirely unused. There is a Testbench harness now, and it runs in CI.
A headless engine holds state and POSTs the protocol request to a Laravel endpoint, with SSR-seeded first paint so the first screen is instant.
The same headless core with Svelte 5 runes — virtual rows and columns, the full keyboard spec, and your Tailwind theme, on the Inertia stack.
Ask the Grid.
in plain language.
Let anyone query millions of rows without knowing your columns or SQL. They type a question — “revenue over 50k in region Nord last quarter” — and the grid turns it into a real filter.
- Server-side, constrained. An LLM whose output can only ever use this grid's declared columns and operators — nothing else.
- Re-validated, then compiled. The suggestion is checked against the allow-list and run through the ordinary FilterCompiler — columns allow-listed, values bound.
- Never unsafe SQL. An off-target guess degrades to dropping that one condition — it can never produce unsafe SQL.
- All three clients. Livewire, Vue and Svelte — add an ask box and go.
Fast at any size —
because size stops mattering.
Benchmarked against a 10,000,000-row table on ElyraSQL 1.4.7. Interactive operations — paging, sorting, filtering — stay flat because the grid only ever queries a viewport.
See the full benchmarkEverything a grid needs. Nothing left as homework.
Multi-column sort and per-column filters, plus an advanced AND/OR filter builder — all resolved in the database.
Filter-by-checkbox value lists with live counts (FACET), computed lazily — only when a filter panel opens.
Users type a question — “revenue over 50k in region Nord” — and a server-side LLM, constrained to the grid’s own columns and operators and re-validated against the allow-list, compiles it exactly like a hand-built filter. Never unsafe SQL; works across all three clients. The safety of the compiled filter was never the gap — the spending was. Until 0.5.0 the config flag meant to switch the assistant off was never read by anything, so ask() was reachable on any grid page with the interface merely hidden, with no length bound and no rate limit on a billed model call. It is a real server-side kill switch now, questions cap at 500 characters, and calls are metered per caller and grid with a limiter that fails closed.
Group by any column with subtotals, non-additive aggregates like median/percentile, and expandable detail rows. This page said WITH ROLLUP until 0.6.0 and that was never how it worked: the rollup flag was documented as the way to get subtotals, plumbed from all three clients through the protocol into the driver contract, and never read by the engine — groupBy was always called with false. Nothing was lost, because the subtotals were never produced by ROLLUP anyway. The engine runs one GROUP BY per level, so each level aggregates over its own rows, which is what makes a median subtotal correct rather than a mean of the cells above it. The dead surface is removed in 0.6.0. Pivot mode arrived in the same release, in all three clients, and rests on exactly that: every subtotal and margin is its own aggregate query rather than a sum of the visible cells, which is the difference between a correct median subtotal and a plausible one. It costs one query per grouping level, issued in parallel — four for a 1×1 pivot — and grouping queries finally have a limit at all, since the number of group rows scales with a column's distinct values rather than the page size. A truncated grouping says so in the response instead of being presented as complete. On PostgreSQL it did not work at all before 0.5.0 — the engine quoted every identifier through the driver except one hardcoded backtick, so a headline feature raised a syntax error on a database this page calls supported. The live PostgreSQL suite never loaded the engine, which is why it survived; there is an engine-level suite now that drives the real thing against a real server.
Classic paging or smooth virtual scrolling — rows and columns virtualized — across all three clients.
Inline, in-cell, modal or slideover editing, with batch edits — validated on the server. Two things 0.5.0 closed that a page selling editing has to say. With any row pinned, paste and copy addressed the wrong record: each client sorted its own copy for display while the core resolved the active cell against the unsorted array, so it was a silent wrong-row UPDATE rather than a wrong paste. And create and update returned the row with SELECT *, handing the client every column in the table — password hashes, tokens, internal flags — where it now returns only the columns the grid declares.
Row and range selection with a complete keyboard spec and clipboard support — spreadsheet-grade navigation.
Expandable master-detail rows and full tree-grid hierarchies, lazily loaded from the server.
Freeze columns left and right, auto-fit widths, reorder and resize — with column virtualization for very wide grids.
Streamed CSV and rich XLSX of the full result set — millions of rows — from a server cursor at constant memory. That was not true of a grid with no key until 0.5.0: such a grid cannot be paginated, so the export buffered the whole result set, contradicting its own docblock while max_export_rows defaulted to null. It refuses without an explicit cap now rather than risking an out-of-memory. The other limit is the connection rather than the memory: a streamed export holds an HTTP response open for its whole duration, and the 1.67M-row benchmark takes about 52 seconds against a 60-second ALB or nginx idle timeout, occupying a PHP-FPM worker throughout. 0.6.0 adds a queued export — progress in the toolbar, then a signed download link — with four gates on that link, because a signed URL is a bearer capability and Laravel's signed URLs do not authenticate: finished, unexpired, owned by the caller, and still permitted by the grid's export policy re-evaluated at download time. The ElyraSQL key-window strategy also walked the key's value range, so Snowflake or ULID-style ids needed around a trillion queries — bounded now, with sparse keys falling back to keyset paging: two queries where there were 1.2e12.
A search box that understands meaning, not just substrings — semantic + full-text (HYBRID) via ElyraSQL.
Tailwind v4-native theming that inherits your palette, accent and dark mode with no configuration.
Localizable UI and persistent grid state (columns, sort, filters) across sessions.
A commercial license,
for a grid you won't outgrow.
Not open source — every plan ships the full source of all packages (protocol, PHP server, headless core, all three clients and the theme), updates and support. Free to evaluate on local and development domains; a license activates your production domains. One purchase covers Livewire, Vue and Svelte.
All annual plans renew yearly and include updates + support for the term. The Lifetime plan is a one-time purchase with perpetual updates — a limited launch offer. See the licensing docs for details.