@svgrid/mcp 3.0.21 → 3.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data.js +18 -9
- package/dist/preview.d.ts +1 -1
- package/dist/preview.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/data.js
CHANGED
|
@@ -4433,7 +4433,7 @@ export const docs = [
|
|
|
4433
4433
|
"path": "docs/enterprise/studio.md",
|
|
4434
4434
|
"title": "SvGrid Studio",
|
|
4435
4435
|
"section": "Studio",
|
|
4436
|
-
"markdown": "# SvGrid Studio\n\nStudio is the grid-centric data-app layer in `@svgrid/enterprise`: turn a\ndatabase or schema into a **production-ready CRUD screen** - grid, edit form,\nand SvelteKit API route - in seconds. One `EntitySchema` drives the grid\ncolumns, the edit form, validation, and the generated code.\n\n\n\nIt is part of the **Enterprise** license, and you can evaluate it\nwithout a key: loaded without one, everything runs and the watermark and the console\nmessage about the Enterprise package are displayed. See\n[licensing](./licensing.md#studio-data-app-generator).\n\n> **Just want to run it?** One command scaffolds a complete, working app - no\n> manual setup, no database required:\n>\n> ```bash\n> npm create @svgrid/studio@latest my-app\n> cd my-app && npm install && npm run dev\n> ```\n>\n> You'll be asked to pick a **theme** (one of `@svgrid/grid`'s 19 built-in\n> presets) and **light or dark** mode - or skip the prompts with\n> `-- --theme material --dark`. Open `http://localhost:5173` and you have a\n> real SvelteKit app - nav shell, **Customers** and **Orders** screens (linked\n> by a searchable lookup), grid + modal create/edit/delete, seeded data.\n> Nothing to configure. See\n> [Getting started](./studio/getting-started.md#fastest-path-a-downloadable-ready-to-run-example)\n> for what to do next.\n\n> **Want to build it yourself, step by step?** Follow the\n> **[Getting started](./studio/getting-started.md)** guide - from an empty app to\n> a working Customers screen, no prior SvGrid experience assumed.\n\n> **Prefer not to write code?** Build the same app **visually** - point, click,\n> preview, then press one button to generate it. Try it right now, no install,\n> at **[svgrid.com/studio](https://svgrid.com/studio)** - or see\n> **[Launch the designer](./studio/launch.md)** for the local CLI version\n> (auto-saves to disk, generates straight into a folder), or open a one-click\n> **[sample app](./studio/samples.md)**.\n\n> **Live demos:** [Live SQL](https://svgrid.com/demos/193-studio-live-sql/)\n> (the whole stack over real Postgres in the browser via PGlite) ·\n> [Supabase](https://svgrid.com/demos/194-studio-supabase/) (your own hosted\n> Postgres, straight from the browser) ·\n> [Chart](https://svgrid.com/demos/197-studio-chart/) (a live chart panel\n> beside the grid; click to filter).\n\n---\n\n## A CRUD app in under a minute\n\nThis assumes you already have a SvelteKit app and a `customers` table in your\ndatabase. Starting from nothing? Use **[Getting started](./studio/getting-started.md)**\ninstead - it builds the same screen from an empty folder, no database required.\n\n```bash\nnpm i @svgrid/grid @svgrid/enterprise pg # pg = driver for your database\nnpx @svgrid/studio add customers --db postgres --url \"$DATABASE_URL\"\nnpm run dev # open /customers\n```\n\n`add customers` introspects the `customers` table and writes three files - the\nentity schema, a SvelteKit `+server.ts` API route, and a `+page.svelte` screen -\ngiving you a working `/customers` page with **search, sort, pagination,\nmulti-select delete, and create/edit**. Re-running `add` is safe: only the\n`svgrid:managed` regions are regenerated.\n\n> The grid themes its own borders, backgrounds, and hover states out of the box. Font is\n> the one thing it inherits from your page rather than forcing - if your app doesn't set\n> one yet (a bare `npx sv create` scaffold doesn't), add a `body { font-family: ... }`\n> rule or a [`--sg-font`](./studio/theming.md) token, or the screen renders in the\n> browser's default serif font.\n\n---\n\n## How it fits together\n\nOne `EntitySchema` is the single source of truth. The grid columns, the edit\nform, the dashboard, the generated SvelteKit code, and the data layer all derive\nfrom it - so a change to the schema flows everywhere at once.\n\n\n\nNew here? [Concepts](./studio/concepts.md) walks this picture end to end and\ndefines every Studio term (screen, block, project model, managed region).\n\n---\n\n## Data binding options\n\nStudio binds a screen to data through one small contract - `ServerDataSource`\n(read + create + update + delete). Every option below implements it, so the\ngrid, form, sorting, filtering, and pagination work identically no matter where\nthe data lives. Pick your source:\n\n| Option | Use it for | Guide |\n| --- | --- | --- |\n| **Databases** | PostgreSQL, Supabase, MySQL / MariaDB, SQL Server, SQLite | [Databases](./studio/databases.md) |\n| **Drizzle schema** | scaffold from a Drizzle `schema.ts` file (no live DB needed) | [Drizzle schema](./studio/drizzle.md) |\n| **REST / custom API** | an existing HTTP API, or any backend you wire by hand | [REST & custom APIs](./studio/rest-api.md) |\n| **In-memory** | prototypes, tests, static data, seeding a schema | [In-memory](./studio/in-memory.md) |\n\nNew to how binding works? Start with **[Data binding](./studio/data-binding.md)** -\nthe `ServerDataSource` contract and how sort / filter / page / CRUD flow.\n\n---\n\n## Three ways to build\n\nAll three produce the same output, because they share one `scaffold()` core.\n\n**1. [CLI](./studio/cli.md)** - `npx @svgrid/studio add ...` (deterministic, no\nAI required).\n\n**2. [AI / MCP](./studio/ai-generation.md)** - with `@svgrid/mcp` configured, ask\nyour agent (Claude Code, Cursor, ...) to build a screen for a table; it calls\n`svgrid_scaffold`, and the output is compile-verified\nbefore it's written.\n\n**3. [Visual app designer](./studio/app-designer.md)** - run `npx @svgrid/studio\ndesigner` to open the full app builder (`SvStudioDesigner`): compose grids, charts,\ndashboards, boards, schedulers, and master-detail across **many entities and\nscreens**, bind each to real data, write code-behind, and click **Generate app**.\nThis is the rich, multi-screen builder you see when you launch Studio.\n\n\n\nWant to embed a schema editor in your **own** app instead? The\n[`<SvSchemaDesigner>`](./studio/designer.md) component is a smaller,\n**single-entity** widget - author one entity's fields + validation with a live grid\n+ form preview, and you own where the schema is stored.\n\nHere a ready-made **CRM sample** is open in the app designer - a forecast dashboard\nwith KPI tiles and deal-value charts, all built by pointing and clicking, no code:\n\n\n\n---\n\n## What you get in a generated screen\n\nA shippable data screen, not a stub:\n\n- **Grid** with server-side **sort**, **filter**, **global search**, and\n columns that fit the width.\n- **Native pagination footer** driven by the server row count.\n- **Create / edit** in a themed **modal or slide-over** panel with **built-in\n validation** (required, email/URL, min/max, length, regex, plus any Zod /\n Standard Schema validator you add).\n- **Multi-select delete** with **optimistic updates** (rows change instantly,\n roll back on error).\n- **Loading, error, and empty** states, light/dark theming via the grid tokens.\n\n\n\n---\n\n## The building blocks (use them directly)\n\n| Piece | What |\n| --- | --- |\n| `EntitySchema` | one model -> grid columns (`schemaToColumns`) + form fields (`schemaToFormFields`) + validation |\n| `SvGridEditPanel` | schema-driven create / edit form (drawer / modal / inline), with FK lookup fields |\n| `createRelationLookup` / `SvLookupInput` | searchable foreign-key picker over any `ServerDataSource` ([Relations](./studio/relations.md)) |\n| `SvGridMasterDetail` | expand a row into a nested grid of related records |\n| `createSqlDataSource` | a `ServerDataSource` over any SQL client (`execute(text, params)`) |\n| `createSupabaseDataSource` | a `ServerDataSource` over a `supabase-js` client (browser + RLS) |\n| `createSupabaseRealtime` | live grid: refetch / flash rows on Postgres change streams ([Real-time](./studio/realtime.md)) |\n| `SvAuthGate` / `createSupabaseAuth` | secure a screen behind Supabase Auth; RLS scopes data per user ([Auth](./studio/auth.md)) |\n| `SvSchemaChart` | a chart bound to the schema; aggregates client-side or via a source's `getAggregate` (`GROUP BY`) ([Dashboards](./studio/dashboards.md)) |\n| `scaffoldApp` | scaffold a whole multi-entity app - nav + home + every screen |\n| `createRestDataSource` | a `ServerDataSource` over an HTTP/JSON API (overridable request/response) |\n| `introspectSupabaseTable` | detect a Supabase table's columns -> `EntitySchema`, from the browser |\n| `createKitDataSource` / `createKitHandlers` | the SvelteKit client + server transport |\n| `createServerDataSource` | server-side sort / filter / page + optimistic CRUD |\n| `SvSchemaDesigner` | the visual schema designer + live preview + code generation |\n\nThe grid renders its **own** sort, filter, and pagination UI in server mode via\n`externalSort` / `externalFilter` / `externalPagination` - you pass `rowCount`\nand `pageIndex`, it emits change events, you fetch the page.\n\n---\n\n## Production & enterprise ready\n\n- **Real code, no lock-in** - Studio generates plain SvelteKit files you own and\n can edit. There is no runtime, no proprietary host.\n- **No telemetry, no network calls** - licensing is validated locally\n ([details](./licensing.md)); nothing phones home.\n- **Parameterized SQL** - every value is bound, and identifiers come from the\n schema-whitelisted plan, so there is no injection surface in the data layer.\n- **SvelteKit-native** - server `load` / actions, `$lib`, SSR-safe, idiomatic\n Svelte 5 runes.\n- **Bring your own key** - the AI generator uses your own model API key; your\n data and schema never touch our servers.\n- **One license** - Studio is part of the Enterprise license; no separate SKU.\n\n---\n\n## All Studio topics\n\nEvery Studio page, in reading order. The sidebar follows the same order.\n\n**Start here**\n\n- [Getting started](./studio/getting-started.md) - zero to a working CRUD screen, step by step\n- [Concepts](./studio/concepts.md) - the mental model: one `EntitySchema` drives screens, data, and code\n- [Sample apps](./studio/samples.md) - ready-made apps to open, explore, and rebind to your data\n- [Build a CRM](./studio/tutorial-crm.md) - companies, contacts, deals - a multi-entity app end to end\n\n**Three ways to build**\n\n- [The Studio CLI](./studio/cli.md) - the `add` command + all flags\n- [Launch the designer](./studio/launch.md) - `npx @svgrid/studio designer`, saved to disk as you edit\n- [Visual app designer](./studio/app-designer.md) - the full multi-screen builder (`SvStudioDesigner`)\n- [Schema designer](./studio/designer.md) - the embeddable single-entity widget (`SvSchemaDesigner`)\n- [AI generation](./studio/ai-generation.md) - the MCP server + agents\n\n**Connect to data**\n\n- [Data binding](./studio/data-binding.md) - the `ServerDataSource` contract; read this one first\n- [Databases](./studio/databases.md) - PostgreSQL, Supabase, MySQL / MariaDB, SQL Server, SQLite, Turso\n- [Local database](./studio/local-database.md) - Postgres in the browser (PGlite), no server needed\n- [Supabase](./studio/supabase.md) - hosted Postgres, from the browser or the server\n- [Drizzle schema](./studio/drizzle.md) - scaffold from a `schema.ts` file\n- [Prisma schema](./studio/prisma.md) - scaffold from a `schema.prisma` file\n- [REST & custom APIs](./studio/rest-api.md) - any HTTP / bespoke backend\n- [OData & GraphQL](./studio/odata-graphql.md) - map the grid request to either protocol\n- [In-memory](./studio/in-memory.md) - static / prototype data\n\n**One-page CRUD tutorials**\n\n- [Postgres CRUD grid](./studio/postgres-grid.md) - pick this if you have a Postgres connection string\n- [Supabase CRUD grid](./studio/supabase-grid.md) - pick this if your data lives in Supabase\n- [REST CRUD grid](./studio/rest-grid.md) - pick this if you already have an HTTP API\n- [Supabase Northwind sample](./studio/supabase-sample.md) - a relational practice dataset, ready to load\n\n**Screens & features**\n\n- [The EntitySchema](./studio/schema.md) - the model everything derives from\n- [Sorting, filtering & paging](./studio/server-grid.md) - the grid's server-mode UI\n- [Edit forms & validation](./studio/edit-forms.md) - `SvGridEditPanel`\n- [Relations](./studio/relations.md) - foreign keys, lookup pickers, related entities from the schema\n- [Master-detail](./studio/master-detail.md) - the `SvGridMasterDetail` component\n- [Dashboards & charts](./studio/dashboards.md) - KPI tiles + `SvSchemaChart` over your entities\n- [Scheduler](./studio/scheduler.md) - a calendar view of any entity with dates\n- [Dock layout](./studio/dock-layout.md) - dockable, floatable panes for workspace screens\n- [Navigation](./studio/navigation.md) - drill-through pages and row action buttons\n- [Real-time](./studio/realtime.md) - live grid over Supabase change streams\n\n**Logic & generated code**\n\n- [Business logic](./studio/business-logic.md) - computed fields, hooks, server-enforced rules\n- [Code-behind](./studio/code-behind.md) - typed event handlers that survive regeneration\n- [Code generation](./studio/code-generation.md) - what's emitted + safe regeneration\n\n**Auth & security**\n\n- [Auth & secured screens](./studio/auth.md) - Supabase Auth + RLS per user\n- [Access control](./studio/access-control.md) - roles gating screens and write actions\n- [Audit log](./studio/audit-log.md) - a server-side change trail with a viewer screen\n\n**Ship it**\n\n- [Themes & styling](./studio/theming.md) - `--sg-*` tokens, light / dark, your design system\n- [Internationalization](./studio/i18n.md) - message catalog + locale switcher\n- [Accessibility](./studio/accessibility.md) - what's handled for you and what you own\n- [Testing](./studio/testing.md) - data source, component, and end-to-end layers\n- [Deploying a Studio app](./studio/deployment.md) - production checklist\n\n**Reference & help**\n\n- [API index](./studio/api.md) - every Studio export, grouped by area\n- [Troubleshooting & FAQ](./studio/troubleshooting.md) - common issues + answers\n\n**Enterprise**\n\n- [Licensing](./licensing.md) · [MCP server](../help/mcp-server.md) · [Evaluation](./evaluation.md)\n"
|
|
4436
|
+
"markdown": "# SvGrid Studio\n\nStudio is the grid-centric data-app layer in `@svgrid/enterprise`: turn a\ndatabase or schema into a **production-ready CRUD screen** - grid, edit form,\nand SvelteKit API route - in seconds. One `EntitySchema` drives the grid\ncolumns, the edit form, validation, and the generated code.\n\n<!-- tutorial:mk-studio-tour -->\n<figure class=\"docs-tutorial docs-tutorial-player\" id=\"tutorial-mk-studio-tour\" data-docs-tutorial=\"mk-studio-tour\">\n<video class=\"docs-tutorial-video\" src=\"/tutorials/mk-studio-tour.mp4\" poster=\"/tutorials/mk-studio-tour.poster.webp\" width=\"960\" height=\"540\" controls playsinline preload=\"none\" aria-label=\"SvGrid Studio: from your database to a SvelteKit app, 190 second tutorial\"><track kind=\"captions\" srclang=\"en\" label=\"English\" src=\"/tutorials/mk-studio-tour.vtt\">Your browser does not play embedded video. <a href=\"/tutorials/mk-studio-tour.mp4\">Download the MP4</a>.</video>\n<figcaption><strong>SvGrid Studio: from your database to a SvelteKit app</strong> (3:10, with narration). <a href=\"https://www.youtube.com/watch?v=1ryvuMVcItU\" rel=\"noopener\">Watch on YouTube</a></figcaption>\n</figure>\n<!-- /tutorial:mk-studio-tour -->\n\n\n\nIt is part of the **Enterprise** license, and you can evaluate it\nwithout a key: loaded without one, everything runs and the watermark and the console\nmessage about the Enterprise package are displayed. See\n[licensing](./licensing.md#studio-data-app-generator).\n\n> **Just want to run it?** One command scaffolds a complete, working app - no\n> manual setup, no database required:\n>\n> ```bash\n> npm create @svgrid/studio@latest my-app\n> cd my-app && npm install && npm run dev\n> ```\n>\n> You'll be asked to pick a **theme** (one of `@svgrid/grid`'s 19 built-in\n> presets) and **light or dark** mode - or skip the prompts with\n> `-- --theme material --dark`. Open `http://localhost:5173` and you have a\n> real SvelteKit app - nav shell, **Customers** and **Orders** screens (linked\n> by a searchable lookup), grid + modal create/edit/delete, seeded data.\n> Nothing to configure. See\n> [Getting started](./studio/getting-started.md#fastest-path-a-downloadable-ready-to-run-example)\n> for what to do next.\n\n> **Want to build it yourself, step by step?** Follow the\n> **[Getting started](./studio/getting-started.md)** guide - from an empty app to\n> a working Customers screen, no prior SvGrid experience assumed.\n\n> **Prefer not to write code?** Build the same app **visually** - point, click,\n> preview, then press one button to generate it. Try it right now, no install,\n> at **[svgrid.com/studio](https://svgrid.com/studio)** - or see\n> **[Launch the designer](./studio/launch.md)** for the local CLI version\n> (auto-saves to disk, generates straight into a folder), or open a one-click\n> **[sample app](./studio/samples.md)**.\n\n> **Live demos:** [Live SQL](https://svgrid.com/demos/193-studio-live-sql/)\n> (the whole stack over real Postgres in the browser via PGlite) ·\n> [Supabase](https://svgrid.com/demos/194-studio-supabase/) (your own hosted\n> Postgres, straight from the browser) ·\n> [Chart](https://svgrid.com/demos/197-studio-chart/) (a live chart panel\n> beside the grid; click to filter).\n\n---\n\n## A CRUD app in under a minute\n\nThis assumes you already have a SvelteKit app and a `customers` table in your\ndatabase. Starting from nothing? Use **[Getting started](./studio/getting-started.md)**\ninstead - it builds the same screen from an empty folder, no database required.\n\n```bash\nnpm i @svgrid/grid @svgrid/enterprise pg # pg = driver for your database\nnpx @svgrid/studio add customers --db postgres --url \"$DATABASE_URL\"\nnpm run dev # open /customers\n```\n\n`add customers` introspects the `customers` table and writes three files - the\nentity schema, a SvelteKit `+server.ts` API route, and a `+page.svelte` screen -\ngiving you a working `/customers` page with **search, sort, pagination,\nmulti-select delete, and create/edit**. Re-running `add` is safe: only the\n`svgrid:managed` regions are regenerated.\n\n> The grid themes its own borders, backgrounds, and hover states out of the box. Font is\n> the one thing it inherits from your page rather than forcing - if your app doesn't set\n> one yet (a bare `npx sv create` scaffold doesn't), add a `body { font-family: ... }`\n> rule or a [`--sg-font`](./studio/theming.md) token, or the screen renders in the\n> browser's default serif font.\n\n---\n\n## How it fits together\n\nOne `EntitySchema` is the single source of truth. The grid columns, the edit\nform, the dashboard, the generated SvelteKit code, and the data layer all derive\nfrom it - so a change to the schema flows everywhere at once.\n\n\n\nNew here? [Concepts](./studio/concepts.md) walks this picture end to end and\ndefines every Studio term (screen, block, project model, managed region).\n\n---\n\n## Data binding options\n\nStudio binds a screen to data through one small contract - `ServerDataSource`\n(read + create + update + delete). Every option below implements it, so the\ngrid, form, sorting, filtering, and pagination work identically no matter where\nthe data lives. Pick your source:\n\n| Option | Use it for | Guide |\n| --- | --- | --- |\n| **Databases** | PostgreSQL, Supabase, MySQL / MariaDB, SQL Server, SQLite | [Databases](./studio/databases.md) |\n| **Drizzle schema** | scaffold from a Drizzle `schema.ts` file (no live DB needed) | [Drizzle schema](./studio/drizzle.md) |\n| **REST / custom API** | an existing HTTP API, or any backend you wire by hand | [REST & custom APIs](./studio/rest-api.md) |\n| **In-memory** | prototypes, tests, static data, seeding a schema | [In-memory](./studio/in-memory.md) |\n\nNew to how binding works? Start with **[Data binding](./studio/data-binding.md)** -\nthe `ServerDataSource` contract and how sort / filter / page / CRUD flow.\n\n---\n\n## Three ways to build\n\nAll three produce the same output, because they share one `scaffold()` core.\n\n**1. [CLI](./studio/cli.md)** - `npx @svgrid/studio add ...` (deterministic, no\nAI required).\n\n**2. [AI / MCP](./studio/ai-generation.md)** - with `@svgrid/mcp` configured, ask\nyour agent (Claude Code, Cursor, ...) to build a screen for a table; it calls\n`svgrid_scaffold`, and the output is compile-verified\nbefore it's written.\n\n**3. [Visual app designer](./studio/app-designer.md)** - run `npx @svgrid/studio\ndesigner` to open the full app builder (`SvStudioDesigner`): compose grids, charts,\ndashboards, boards, schedulers, and master-detail across **many entities and\nscreens**, bind each to real data, write code-behind, and click **Generate app**.\nThis is the rich, multi-screen builder you see when you launch Studio.\n\n\n\nWant to embed a schema editor in your **own** app instead? The\n[`<SvSchemaDesigner>`](./studio/designer.md) component is a smaller,\n**single-entity** widget - author one entity's fields + validation with a live grid\n+ form preview, and you own where the schema is stored.\n\nHere a ready-made **CRM sample** is open in the app designer - a forecast dashboard\nwith KPI tiles and deal-value charts, all built by pointing and clicking, no code:\n\n\n\n---\n\n## What you get in a generated screen\n\nA shippable data screen, not a stub:\n\n- **Grid** with server-side **sort**, **filter**, **global search**, and\n columns that fit the width.\n- **Native pagination footer** driven by the server row count.\n- **Create / edit** in a themed **modal or slide-over** panel with **built-in\n validation** (required, email/URL, min/max, length, regex, plus any Zod /\n Standard Schema validator you add).\n- **Multi-select delete** with **optimistic updates** (rows change instantly,\n roll back on error).\n- **Loading, error, and empty** states, light/dark theming via the grid tokens.\n\n\n\n---\n\n## The building blocks (use them directly)\n\n| Piece | What |\n| --- | --- |\n| `EntitySchema` | one model -> grid columns (`schemaToColumns`) + form fields (`schemaToFormFields`) + validation |\n| `SvGridEditPanel` | schema-driven create / edit form (drawer / modal / inline), with FK lookup fields |\n| `createRelationLookup` / `SvLookupInput` | searchable foreign-key picker over any `ServerDataSource` ([Relations](./studio/relations.md)) |\n| `SvGridMasterDetail` | expand a row into a nested grid of related records |\n| `createSqlDataSource` | a `ServerDataSource` over any SQL client (`execute(text, params)`) |\n| `createSupabaseDataSource` | a `ServerDataSource` over a `supabase-js` client (browser + RLS) |\n| `createSupabaseRealtime` | live grid: refetch / flash rows on Postgres change streams ([Real-time](./studio/realtime.md)) |\n| `SvAuthGate` / `createSupabaseAuth` | secure a screen behind Supabase Auth; RLS scopes data per user ([Auth](./studio/auth.md)) |\n| `SvSchemaChart` | a chart bound to the schema; aggregates client-side or via a source's `getAggregate` (`GROUP BY`) ([Dashboards](./studio/dashboards.md)) |\n| `scaffoldApp` | scaffold a whole multi-entity app - nav + home + every screen |\n| `createRestDataSource` | a `ServerDataSource` over an HTTP/JSON API (overridable request/response) |\n| `introspectSupabaseTable` | detect a Supabase table's columns -> `EntitySchema`, from the browser |\n| `createKitDataSource` / `createKitHandlers` | the SvelteKit client + server transport |\n| `createServerDataSource` | server-side sort / filter / page + optimistic CRUD |\n| `SvSchemaDesigner` | the visual schema designer + live preview + code generation |\n\nThe grid renders its **own** sort, filter, and pagination UI in server mode via\n`externalSort` / `externalFilter` / `externalPagination` - you pass `rowCount`\nand `pageIndex`, it emits change events, you fetch the page.\n\n---\n\n## Production & enterprise ready\n\n- **Real code, no lock-in** - Studio generates plain SvelteKit files you own and\n can edit. There is no runtime, no proprietary host.\n- **No telemetry, no network calls** - licensing is validated locally\n ([details](./licensing.md)); nothing phones home.\n- **Parameterized SQL** - every value is bound, and identifiers come from the\n schema-whitelisted plan, so there is no injection surface in the data layer.\n- **SvelteKit-native** - server `load` / actions, `$lib`, SSR-safe, idiomatic\n Svelte 5 runes.\n- **Bring your own key** - the AI generator uses your own model API key; your\n data and schema never touch our servers.\n- **One license** - Studio is part of the Enterprise license; no separate SKU.\n\n---\n\n## All Studio topics\n\nEvery Studio page, in reading order. The sidebar follows the same order.\n\n**Start here**\n\n- [Getting started](./studio/getting-started.md) - zero to a working CRUD screen, step by step\n- [Concepts](./studio/concepts.md) - the mental model: one `EntitySchema` drives screens, data, and code\n- [Sample apps](./studio/samples.md) - ready-made apps to open, explore, and rebind to your data\n- [Build a CRM](./studio/tutorial-crm.md) - companies, contacts, deals - a multi-entity app end to end\n\n**Three ways to build**\n\n- [The Studio CLI](./studio/cli.md) - the `add` command + all flags\n- [Launch the designer](./studio/launch.md) - `npx @svgrid/studio designer`, saved to disk as you edit\n- [Visual app designer](./studio/app-designer.md) - the full multi-screen builder (`SvStudioDesigner`)\n- [Schema designer](./studio/designer.md) - the embeddable single-entity widget (`SvSchemaDesigner`)\n- [AI generation](./studio/ai-generation.md) - the MCP server + agents\n\n**Connect to data**\n\n- [Data binding](./studio/data-binding.md) - the `ServerDataSource` contract; read this one first\n- [Databases](./studio/databases.md) - PostgreSQL, Supabase, MySQL / MariaDB, SQL Server, SQLite, Turso\n- [Local database](./studio/local-database.md) - Postgres in the browser (PGlite), no server needed\n- [Supabase](./studio/supabase.md) - hosted Postgres, from the browser or the server\n- [Drizzle schema](./studio/drizzle.md) - scaffold from a `schema.ts` file\n- [Prisma schema](./studio/prisma.md) - scaffold from a `schema.prisma` file\n- [REST & custom APIs](./studio/rest-api.md) - any HTTP / bespoke backend\n- [OData & GraphQL](./studio/odata-graphql.md) - map the grid request to either protocol\n- [In-memory](./studio/in-memory.md) - static / prototype data\n\n**One-page CRUD tutorials**\n\n- [Postgres CRUD grid](./studio/postgres-grid.md) - pick this if you have a Postgres connection string\n- [Supabase CRUD grid](./studio/supabase-grid.md) - pick this if your data lives in Supabase\n- [REST CRUD grid](./studio/rest-grid.md) - pick this if you already have an HTTP API\n- [Supabase Northwind sample](./studio/supabase-sample.md) - a relational practice dataset, ready to load\n\n**Screens & features**\n\n- [The EntitySchema](./studio/schema.md) - the model everything derives from\n- [Sorting, filtering & paging](./studio/server-grid.md) - the grid's server-mode UI\n- [Edit forms & validation](./studio/edit-forms.md) - `SvGridEditPanel`\n- [Relations](./studio/relations.md) - foreign keys, lookup pickers, related entities from the schema\n- [Master-detail](./studio/master-detail.md) - the `SvGridMasterDetail` component\n- [Dashboards & charts](./studio/dashboards.md) - KPI tiles + `SvSchemaChart` over your entities\n- [Scheduler](./studio/scheduler.md) - a calendar view of any entity with dates\n- [Dock layout](./studio/dock-layout.md) - dockable, floatable panes for workspace screens\n- [Navigation](./studio/navigation.md) - drill-through pages and row action buttons\n- [Real-time](./studio/realtime.md) - live grid over Supabase change streams\n\n**Logic & generated code**\n\n- [Business logic](./studio/business-logic.md) - computed fields, hooks, server-enforced rules\n- [Code-behind](./studio/code-behind.md) - typed event handlers that survive regeneration\n- [Code generation](./studio/code-generation.md) - what's emitted + safe regeneration\n\n**Auth & security**\n\n- [Auth & secured screens](./studio/auth.md) - Supabase Auth + RLS per user\n- [Access control](./studio/access-control.md) - roles gating screens and write actions\n- [Audit log](./studio/audit-log.md) - a server-side change trail with a viewer screen\n\n**Ship it**\n\n- [Themes & styling](./studio/theming.md) - `--sg-*` tokens, light / dark, your design system\n- [Internationalization](./studio/i18n.md) - message catalog + locale switcher\n- [Accessibility](./studio/accessibility.md) - what's handled for you and what you own\n- [Testing](./studio/testing.md) - data source, component, and end-to-end layers\n- [Deploying a Studio app](./studio/deployment.md) - production checklist\n\n**Reference & help**\n\n- [API index](./studio/api.md) - every Studio export, grouped by area\n- [Troubleshooting & FAQ](./studio/troubleshooting.md) - common issues + answers\n\n**Enterprise**\n\n- [Licensing](./licensing.md) · [MCP server](../help/mcp-server.md) · [Evaluation](./evaluation.md)\n"
|
|
4437
4437
|
},
|
|
4438
4438
|
{
|
|
4439
4439
|
"slug": "enterprise/studio/access-control",
|
|
@@ -5343,7 +5343,7 @@ export const docs = [
|
|
|
5343
5343
|
"path": "docs/help/comparison.md",
|
|
5344
5344
|
"title": "Comparison: SvGrid vs AG Grid vs TanStack Table",
|
|
5345
5345
|
"section": "Help",
|
|
5346
|
-
"markdown": "\n# Comparison: SvGrid vs AG Grid vs TanStack Table\n\nThe three projects solve overlapping problems, and the right choice\ndepends on the framework you ship on, how much of the grid you want to\nbuild yourself, and your budget. Every number on this page is read from\nnpm or measured by a script in this repository on the date shown; the\nblock below is regenerated by `node tools/sync-guide-facts.mjs` and the\nper-competitor pages under [/compare/](https://svgrid.com/compare/) list\nthe sources for every feature claim.\n\n<!-- facts:start ag-grid,tanstack-table -->\n> **Facts, checked 12 Sep 2026.** `ag-grid-community` 36.1.0, MIT, last published 5 Aug 2026, 12,400,000 npm downloads in the 30 days to 10 Sep 2026. `@tanstack/svelte-table` 9.2.4, MIT, last published 28 Aug 2026, 231,000 npm downloads in the same window. `@svgrid/grid` 3.0.3, MIT, last published 11 Sep 2026, 16,900 npm downloads in the same window. Bundle, minified and gzipped, each package built alone with Svelte external: SvGrid 3.0.4 97.6 KB JS + 10.5 KB CSS (measured 22 Sep 2026); `ag-grid-community` 36.1.0 317.5 KB JS, no separate stylesheet (measured 12 Sep 2026); `@tanstack/svelte-table` 9.2.4 36.3 KB JS, no separate stylesheet, svelte external (measured 12 Sep 2026). AG Grid pricing, as its site states it: AG Grid Community is free under MIT. AG Grid Enterprise is listed at $999 USD per developer with one year of updates and Zendesk support; the Enterprise Bundle with AG Charts Enterprise is $1,498 USD per developer (https://www.ag-grid.com/license-pricing/, read 12 Sep 2026). TanStack Table pricing, as its site states it: TanStack Table is MIT and free; tanstack.com is sponsor-supported and offers Enterprise Support as private consulting and expert support, with no licence sold for the table (https://tanstack.com/table/latest, read 12 Sep 2026). SvGrid: MIT core; @svgrid/enterprise from $299 per developer per year (Indie, under $1M revenue), $599 for Enterprise. Side by side, with sources: [SvGrid vs AG Grid (community + enterprise)](https://svgrid.com/compare/ag-grid/), [SvGrid vs TanStack Table (Svelte)](https://svgrid.com/compare/tanstack-table/).\n<!-- facts:end -->\n\n## TL;DR\n\n| Project | Lives in | Ships | Licence |\n| --------------------- | ---------------------------------------- | ---------------------------------------------------------- | ----------------------------------------- |\n| **SvGrid** | Svelte 5 | Headless core + Svelte render component + Enterprise pack | MIT (Community) / commercial (Enterprise) |\n| **AG Grid Community** | React, Angular, Vue, plain JS | Full grid + renderer; Svelte through community integrations | MIT |\n| **AG Grid Enterprise**| same | Adds the set filter, range selection, master/detail, tree, grouping, pivot, charts, Excel export, server-side row model | Commercial, per developer |\n| **TanStack Table** | React, Vue, Svelte, Solid, Qwik, Lit, JS | Headless engine **only**; the Svelte adapter is Svelte 5 | MIT |\n\nThe bundle sizes and prices are in the facts block above, with the date\nthey were read and the method behind the sizes.\n\n## When SvGrid is the right choice\n\n- You're on **Svelte 5** and want a grid that uses the runtime's idioms\n (snippets for cells, `$state` for data, `$derived` for aggregates) -\n not a React-port pretending to be Svelte.\n- You want a **headless core you can render yourself** AND a\n default-styled component for the 80% case. Most \"headless\" libraries\n make you write the markup; most \"monolith\" libraries make you fight\n the markup. SvGrid does both in one package.\n- You need **clean theming via CSS custom properties** and a documented\n `--sg-*` token surface, not a hard-coded class soup.\n- You ship under **strict CSP** (no `eval`, no `new Function`, no\n inline scripts). SvGrid runs clean; AG Grid Community does too.\n TanStack Table is engine-only so the question doesn't apply.\n- You want **SSR markup that is meaningful before hydration** (good\n first paint, SEO, SvelteKit `+page.server` integration). SvGrid +\n TanStack Table both qualify. AG Grid renders client-side.\n- You want the features AG Grid sells as Enterprise - the set filter,\n range selection, master/detail, tree data, grouping with aggregation,\n integrated charts, flat server-side infinite scroll - **in the MIT\n core**. (Server-side grouping, tree, pivot and transactions are\n Enterprise in both.)\n\n## When AG Grid is the right choice\n\n- You're on **React, Angular, or Vue**, not Svelte. SvGrid is\n Svelte-only, and ag-grid.com points Svelte users to community-built\n integrations rather than an official package.\n- You need a push-based **viewport row model** for a real-time trading\n blotter, where the server streams the visible window over a socket.\n SvGrid's server-side row model covers sort, filter, grouping, tree,\n pivot, transactions and select-all across unloaded rows, and its\n real-time page merges deltas into loaded rows, but it does not have the\n viewport model.\n- You need **pluggable custom filter components or custom tool panels**.\n SvGrid's tool panel is a fixed Columns + Filters pair.\n- You need **AG Charts** for maps, network graphs, 3D or a canvas renderer.\n SvGrid's chart is SVG with decimation, and it ships 29 types, a configurable\n axis model, synchronized charts, a financial toolkit with indicator panes\n and drawing tools, a builder and PDF export in the MIT core; it does not\n draw maps.\n- You want a vendor with **a decade of enterprise procurement paperwork**\n already on file.\n\n## When TanStack Table is the right choice\n\n- You want a **rendering-framework-agnostic engine** so the same\n business logic powers React + Svelte + Solid surfaces in your\n monorepo.\n- You're already in the TanStack ecosystem (Query, Router, Form,\n Virtual) and want one mental model.\n- You're happy writing **all the markup yourself** - the row recycling,\n the keyboard map, the ARIA roles, the focus management, the\n drag-to-resize. That's the cost of \"engine only\".\n\n## Feature parity at a glance\n\nYes: shipped in that edition. Partial: needs extra work or a companion\npackage. No: not available. The AG Grid columns follow ag-grid.com's\nCommunity vs Enterprise page and its Set Filter, Tree Data and CSV export\npages; the dated list of those sources is on the\n[SvGrid vs AG Grid](https://svgrid.com/compare/ag-grid/) page, and the\nTanStack sources on [SvGrid vs TanStack Table](https://svgrid.com/compare/tanstack-table/).\n\n| | SvGrid Community | SvGrid Enterprise | AG Grid Community | AG Grid Enterprise | TanStack Table |\n| ------------------------------- | ---------------- | ----------------- | ----------------- | ------------------ | -------------- |\n| Headless core (engine only) | Yes | Yes | No | No | Yes |\n| Default render component | Yes (Svelte 5) | Yes | Yes (each framework) | Yes | No |\n| Sort (multi-column) | Yes | Yes | Yes | Yes | Yes (engine) |\n| Filter menu (operator + facet) | Yes | Yes | Partial (text and number filters; the set filter is Enterprise) | Yes | No |\n| Filter row | Yes | Yes | Yes | Yes | No |\n| Pagination | Yes | Yes | Yes | Yes | Yes (engine) |\n| Grouping + aggregation | Yes | Yes | No (Enterprise) | Yes | Yes (engine) |\n| Tree / expand-collapse rows | Yes | Yes | No (Enterprise) | Yes | Yes (engine) |\n| Master/detail | Yes | Yes | No (Enterprise) | Yes | No |\n| Cell range selection + copy/paste | Yes | Yes | No (Enterprise) | Yes | No |\n| Fill handle | Yes | Yes | No (Enterprise) | Yes | No |\n| Inline editing (typed editors) | Yes | Yes | Yes | Yes | No |\n| Column virtualization | Yes | Yes | Yes | Yes | No |\n| Row virtualization | Yes | Yes | Yes | Yes | Partial (pair with TanStack Virtual) |\n| Column pinning (left/right) | Yes | Yes | Yes | Yes | No |\n| Column tool panel | Yes | Yes | No (Enterprise) | Yes | No |\n| Context menu | Yes | Yes | No (Enterprise) | Yes | No |\n| Fit-to-width with shrink | Yes | Yes | Partial | Yes | No |\n| WAI-ARIA grid pattern | Yes | Yes | Yes | Yes | No (your markup) |\n| Server-side data, flat (sort / filter / paging / infinite scroll) | Yes (built in) | Yes (built in) | Yes (infinite row model) | Yes (built in) | Partial (manual flags) |\n| Server-side row model (lazy grouping, tree, pivot, transactions, select-all across unloaded rows) | No (Enterprise) | Yes (built in) | No (Enterprise) | Yes (built in) | No |\n| Global search over server-side data | Yes (`filterModel.global`) | Yes | No (the quick filter is client-side only) | No | n/a |\n| CSP-clean (no eval, no inline) | Yes | Yes | Yes | Yes | n/a |\n| Meaningful SSR markup | Yes | Yes | No | No | depends on your markup |\n| CSV / TSV / JSON export | Yes | Yes | Yes (CSV) | Yes | No |\n| Excel (.xlsx) / PDF export | No | Yes | No | Yes (Excel) | No |\n| Print | No | Yes | No | No | No |\n| Excel / CSV import | No | Yes | No | No | No |\n| In-grid AI helpers (bring your own model) | Yes | Yes | No | No | No |\n| MCP server for AI assistants | Yes (`@svgrid/mcp`) | Yes | Yes (`ag-mcp`) | Yes | No (agent skills ship in the package) |\n| Pivot table | No | Yes | No | Yes | No (custom) |\n| Integrated charts | Yes (29 types, builder, financial toolkit) | Yes | No | Yes (AG Charts) | No |\n| Support response time | GitHub (best-effort) | 1 business day; 1h sev-1 on Enterprise | GitHub (best-effort) | no number published | GitHub; paid Enterprise Support offered |\n| Theming via CSS variables | Yes (`--sg-*`) | Yes | Yes (theme builder) | Yes | n/a |\n| Source button per demo | Yes (gallery) | Yes | No | No | No |\n\n## Bundle size\n\nThe facts block at the top of the page has the gzipped size of each\npackage, measured by the same method: each package built alone with Vite\nin library mode, minified, gzip level 9, Svelte external because it is a\npeer dependency. SvGrid's own number comes from `pnpm size:json`\n(`packages/grid/scripts/measure-size.mjs`), the competitors' from\n`pnpm competitors:measure`; both write `docs/_data` and the block is\nregenerated from there, so the page cannot quote a size nobody measured.\n\nWhat the SvGrid number contains: the whole render component -\nvirtualization, Excel-style filters, inline editing, grouping, tree,\nmaster/detail, cell selection and accessibility - in one import, plus a\nseparate stylesheet. Charts, date and time editors, menus and export split\ninto `import()` chunks that load on demand rather than shipping in the\ninitial bundle; the headless core on its own is a small fraction of the\nrender component, and `@svgrid/enterprise` features are separate subpath\nimports. The per-entry breakdown, including the headless core and the\nlazy chunks, is in the [bundle size reference](./bundle-size.md).\n\nWhat the AG Grid number contains: `createGrid` with every Community\nmodule registered and the Quartz theme, whose CSS ships inside the script\nunder the theming API, so there is no separate stylesheet to add. The\nTanStack number is the whole Svelte adapter with its engine: no\nvirtualizer, filter UI or editors, because a headless library has none.\n\n## Measured performance\n\nA vendor's own benchmark is worth nothing unless you can rerun it, so the\nharness is in the repository and takes an adapter per grid. Adding another\ngrid is about forty lines in `examples/src/bench/adapters.ts` and nothing\nelse.\n\n```bash\n# headless, prints the table and writes tests/perf/.last-compare.json\nBENCH_REPEATS=7 BENCH_GRIDS=svgrid,aggrid,svar,tanstack pnpm bench:compare\n\n# move that run into docs/_data/competitors.json, then regenerate this page\nnode tools/record-benchmarks.mjs && node tools/sync-guide-facts.mjs\n\n# or drive it by hand\npnpm --filter @svgrid/grid-example-gallery dev # then open /bench.html\n```\n\nEach operation is timed until the grid's DOM reflects it, plus one frame:\n\n- **Mount** - fresh container, create the grid, until rows are painted.\n- **Sort** - change the sort on one column, until the new order is painted.\n Text sorts `name` (tens of thousands of distinct values), numeric sorts\n `amount`.\n- **Filter** - apply a `contains` filter to one column.\n- **Scroll** - 180 frames at 60 px/frame, sampling frame intervals.\n\n### Results\n\nThe table is generated from the ledger by `tools/sync-guide-facts.mjs`;\nthe versions and the date are part of it.\n\n<!-- bench:start -->\nMeasured 12 Sep 2026 with `pnpm bench:compare` (tests/perf/compare.spec.ts, recorded by tools/record-benchmarks.mjs): 100,000 rows x 9 columns; 1000 x 520 px container, 32 px rows, 140 px columns, each grid with its default theme; fastest of 7 samples per operation, each grid in a fresh page; Chromium through Playwright, headless; win32 10.0.26200 x64, Intel(R) Core(TM) Ultra 7 255U; a developer workstation, not a dedicated bench rig. Lower is better.\n\n| Operation | SvGrid | AG Grid Community | SVAR Svelte DataGrid | TanStack Table, minimal DOM |\n| --- | --- | --- | --- | --- |\n| Mount | 75.5 ms | 51.4 ms | 11.4 ms | 196 ms |\n| Sort, text column | 149 ms | 282.8 ms | 187.2 ms | 466 ms |\n| Sort, numeric column | 115.7 ms | 270 ms | 165.1 ms | 374.6 ms |\n| Filter, one column (indicative only, see below) | 99.6 ms | 170.4 ms | 32.6 ms | 111.7 ms |\n| Scroll, p95 frame | 34.6 ms | 35.9 ms | 34.8 ms | 35.3 ms |\n| Frames dropped while scrolling, of 180 | 0 | 0 | 0 | 1 |\n| Rows kept in the DOM (virtualization on) | 27 | 35 | 19 | 27 |\n\nVersions: `@svgrid/grid` 3.0.3, `ag-grid-community` 36.1.0, `wx-svelte-grid` 2.7.0, `@tanstack/svelte-table` 9.2.4.\n<!-- bench:end -->\n\nRead that as: **SvGrid sorts faster than AG Grid Community and SVAR on both\ncolumn types, scrolling is level across all four, and mounting is where the\nlighter grids are ahead** - SVAR paints its first rows fastest, AG Grid is\nahead of SvGrid, and the TanStack renderer pays for building a row object\nper record before it can draw anything. Anyone claiming a single headline\nnumber for a grid comparison is selling something.\n\n**The filter row is not a like-for-like comparison and is shown only because\nhiding a number we win looks worse than explaining it.** The grids' single-\ncolumn filter APIs differ: the harness drives AG Grid's quick filter, which\nsearches every column and is strictly more work than the single-column\n`contains` SvGrid performs, while SVAR and TanStack run a predicate over the\none column. Do not read a ratio into that row.\n\n**The TanStack column is an engine plus the least DOM an app could draw.**\nTanStack Table renders nothing, so its adapter draws the row model through\na fixed-height windowed table with no virtualizer library, no filter UI and\nno editors. Its mount and sort numbers include creating the engine's row\nobjects for every record; the grids around it do the same work inside their\nown models.\n\nSorting is where SvGrid's distance comes from, and it is not subtle: SvGrid\nresolves each sort clause once and precomputes a key array, and for a\ncolumn whose distinct values are far fewer than its rows it collates the\ndistinct values once and sorts by rank. Mounting is the other direction -\nsee the accounting in [benchmarks](./benchmarks.md).\n\nTwo methodology notes, both of which were bugs in this harness before they\nwere notes, and both of which changed the numbers by more than any code\nchange did:\n\n- **Each grid runs in a fresh page.** They used to run one after another in\n the same one, where the first warmed the JIT, fragmented the heap and\n left a hundred thousand rows of garbage for the next. Back-to-back runs\n on an idle machine disagreed with themselves by a factor of two.\n Run-to-run variance is now a few percent.\n- **Operations are timed until the DOM changes, not for a fixed two\n frames.** The old fixed wait put a two-frame floor under every number -\n an operation that recomputed nothing at all measured the floor, and a\n mount whose rows were already in the DOM measured pure waiting - which\n flattered whichever grid was slower.\n- **Each figure is the fastest of the samples, not the median.** Noise\n only ever adds time, so the minimum is the cleanest estimate of what the\n machine can do, and medians on this box drifted by a third between\n consecutive runs - more than most of the differences being measured.\n Applied identically to every grid.\n\nWhat this table deliberately does not include:\n\n- **Features the grids do not measure the same way.** Grouping, pivot,\n editing and export all differ enough in shape that a single timing would\n compare two different amounts of work. The filter row above is the one\n case we publish anyway, labelled, rather than quietly dropping a result\n that happens to favour us.\n- **Any grid we cannot license for this.** The harness is grid-agnostic on\n purpose, but some commercial grids licence their software in ways that\n restrict using it to produce competitive claims. Where that is the case\n we do not ship an adapter, and we would rather have an obviously\n incomplete table than a legally dubious one. Run your own shortlist.\n- **Your workload.** One dataset shape, one machine, one browser, default\n configuration for every grid. If a grid decision rides on this, clone the\n harness and put your own data in it.\n\n## Migrating from AG Grid\n\nThe most common starting point. See\n[Migrating from AG Grid](./migrating-from-ag-grid.md) for a side-by-side\ntranslation of column defs, features, filtering, editing, and the\nimperative API. The example that guide is built around runs here:\n\n<div data-docs-demo=\"139-migration-from-ag-grid\" data-height=\"560\"></div>\n\n## Migrating from TanStack Table\n\nThe map is one-to-one - SvGrid's headless core speaks the vocabulary\nTanStack Table v9 introduced. The big differences:\n\n- Replace `createTable(opts)` with `createSvGrid(opts)`. Same state\n machine, same features object.\n- Replace the row-model factories with the same names from\n `@svgrid/grid`.\n- The render layer changes - TanStack hands you `FlexRender` + the\n row model; SvGrid lets you keep that headless approach OR drop in\n the default `<SvGrid>` component.\n\nThe headless demo below drives a plain `<table>` from `createSvGrid`, the\nway a TanStack app would:\n\n<div data-docs-demo=\"186-headless-table\" data-height=\"520\"></div>\n\nAnd the same rows through the render component, which is the part TanStack\nleaves to you. The example runs against these rows:\n\n```svelte {preamble}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns } from '@svgrid/grid'\n\n type Order = {\n id: number\n customer: string\n region: string\n status: string\n amount: number\n }\n\n const orders: Order[] = [\n { id: 1, customer: 'Ada Lovelace', region: 'EMEA', status: 'open', amount: 1240 },\n { id: 2, customer: 'Grace Hopper', region: 'AMER', status: 'shipped', amount: 860 },\n { id: 3, customer: 'Linus Torvalds', region: 'EMEA', status: 'closed', amount: 2210 },\n { id: 4, customer: 'Radia Perlman', region: 'APAC', status: 'open', amount: 430 },\n { id: 5, customer: 'Barbara Liskov', region: 'AMER', status: 'pending', amount: 1780 },\n ]\n\n let rows = $state<Order[]>(orders)\n\n const columns: GridColumns<Order> = [\n { field: 'customer', header: 'Customer', width: 180 },\n { field: 'region', header: 'Region', width: 110 },\n { field: 'status', header: 'Status', width: 110 },\n { field: 'amount', header: 'Amount', width: 120, editorType: 'number', format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n```\n\n```svelte {runnable}\n<SvGrid data={rows} {columns} sortable filterable selectable />\n```\n\n## Pricing\n\nSvGrid Community is MIT - free for commercial use, no attribution\nrequired at runtime. SvGrid Enterprise is a per-developer licence: $599\nper developer per year for the enterprise grid or $999 with the\nspreadsheet and Studio, both for unlimited applications; see\n<https://svgrid.com/pricing/> for what each tier adds.\n\nAG Grid Community is MIT. AG Grid Enterprise is priced per developer with\none year of updates; the figure ag-grid.com listed on the date we read it\nis in the facts block at the top of this page, and the\n[SvGrid vs AG Grid](https://svgrid.com/compare/ag-grid/) page carries the\nlink. Compare what each paid tier adds before comparing prices: most of\nwhat AG Grid sells as Enterprise is in SvGrid's free core.\n\nTanStack Table is MIT and sponsor-funded; tanstack.com offers paid\nEnterprise Support as private consulting.\n\n## Frequently asked questions\n\n### What is the best data grid for Svelte 5?\n\nFor a Svelte-5-native grid with a batteries-included render component, SvGrid is\nbuilt around runes and snippets. TanStack Table is a strong headless-only\nchoice if you want to build the DOM layer yourself across frameworks, and its\nv9 Svelte adapter targets Svelte 5. AG Grid is the most feature-complete but\nlives in React, Angular and Vue first and reaches Svelte through\ncommunity-built integrations.\n\n### Is SvGrid a good AG Grid alternative?\n\nYes, for Svelte projects. SvGrid is a fraction of AG Grid Community's\nmeasured size on a Svelte page (the facts block has both numbers and the\nmethod), is MIT-licensed for commercial use, and offers `@svgrid/enterprise`\nfor export, print, import and pivot at a per-developer price. On the\nbenchmark above it sorts a hundred-thousand-row column faster on both\ncolumn types and scrolls level; AG Grid mounts quicker. It does not match\nevery AG Grid Enterprise feature - see the\n[missing-features list](./missing-features.md) for the gaps.\n\n### SvGrid vs TanStack Table - which should I pick?\n\nPick SvGrid if you want virtualization, Excel-style filters, selection, and\ninline editing working out of the box on Svelte 5. Pick TanStack Table if you\nwant a framework-agnostic headless engine and are happy to build the rendering,\nvirtualization, and editing UI yourself. Both are MIT-licensed.\n\n### Does the TanStack Svelte adapter support Svelte 5?\n\nYes. The `@tanstack/svelte-table` v9 line declares a Svelte 5 peer range\nand passes data through getters rather than stores; the facts block above\nshows the version read from npm. The earlier `createSvelteTable` constructor\nis gone in v9.\n\n### Does AG Grid have an MCP server like SvGrid?\n\nYes. `ag-mcp` (MIT) gives an assistant AG Grid's docs, examples and\nversion-specific upgrade prompts. `@svgrid/mcp` does the same for SvGrid\nand adds a code checker that validates props and imports against the real\nAPI surface, a scaffold tool and a preview. Neither is an in-grid feature;\nSvGrid's natural-language filter, smart fill and summarise helpers are, and\nthey ship free in `@svgrid/grid` with a model provider you register.\n\n### How big is the SvGrid bundle?\n\nThe facts block at the top of this page has the measured gzipped size of the\nfull `<SvGrid>` render component and its stylesheet, read from\n`docs/_data/svgrid-size.json`, which `pnpm size:json` writes. The\n[bundle size reference](./bundle-size.md) breaks it down by entry:\nthe headless core on its own, the core subpath, the charts, and the lazy\nchunks that load on demand.\n\n## See also\n\n- [Why headless?](../why-headless.md) - the design decision behind\n SvGrid's two-layer architecture.\n- [Migrating from AG Grid](./migrating-from-ag-grid.md) - the\n practical recipe.\n- [Migrating from TanStack Table](./migrating-from-tanstack-table.md) - the\n one-to-one API map.\n- [Benchmarks](./benchmarks.md) - how the numbers are produced and what\n they leave out.\n- [Enterprise feature pack](../enterprise/README.md) - what SvGrid charges for.\n- [Missing features](./missing-features.md) - the honest gap list\n versus AG Grid Enterprise.\n- [All comparisons](https://svgrid.com/compare/) - one page per grid, with\n dated sources for every claim.\n"
|
|
5346
|
+
"markdown": "\n# Comparison: SvGrid vs AG Grid vs TanStack Table\n\nThe three projects solve overlapping problems, and the right choice\ndepends on the framework you ship on, how much of the grid you want to\nbuild yourself, and your budget. Every number on this page is read from\nnpm or measured by a script in this repository on the date shown; the\nblock below is regenerated by `node tools/sync-guide-facts.mjs` and the\nper-competitor pages under [/compare/](https://svgrid.com/compare/) list\nthe sources for every feature claim.\n\n<!-- facts:start ag-grid,tanstack-table -->\n> **Facts, checked 12 Sep 2026.** `ag-grid-community` 36.1.0, MIT, last published 5 Aug 2026, 12,400,000 npm downloads in the 30 days to 10 Sep 2026. `@tanstack/svelte-table` 9.2.4, MIT, last published 28 Aug 2026, 231,000 npm downloads in the same window. `@svgrid/grid` 3.0.3, MIT, last published 11 Sep 2026, 16,900 npm downloads in the same window. Bundle, minified and gzipped, each package built alone with Svelte external: SvGrid 3.0.4 97.6 KB JS + 10.5 KB CSS (measured 22 Sep 2026); `ag-grid-community` 36.1.0 317.5 KB JS, no separate stylesheet (measured 12 Sep 2026); `@tanstack/svelte-table` 9.2.4 36.3 KB JS, no separate stylesheet, svelte external (measured 12 Sep 2026). AG Grid pricing, as its site states it: AG Grid Community is free under MIT. AG Grid Enterprise is listed at $999 USD per developer with one year of updates and Zendesk support; the Enterprise Bundle with AG Charts Enterprise is $1,498 USD per developer (https://www.ag-grid.com/license-pricing/, read 12 Sep 2026). TanStack Table pricing, as its site states it: TanStack Table is MIT and free; tanstack.com is sponsor-supported and offers Enterprise Support as private consulting and expert support, with no licence sold for the table (https://tanstack.com/table/latest, read 12 Sep 2026). SvGrid: MIT core; @svgrid/enterprise from $299 per developer per year (Indie, under $1M revenue), $599 for Enterprise. Side by side, with sources: [SvGrid vs AG Grid (community + enterprise)](https://svgrid.com/compare/ag-grid/), [SvGrid vs TanStack Table (Svelte)](https://svgrid.com/compare/tanstack-table/).\n<!-- facts:end -->\n\n## TL;DR\n\n| Project | Lives in | Ships | Licence |\n| --------------------- | ---------------------------------------- | ---------------------------------------------------------- | ----------------------------------------- |\n| **SvGrid** | Svelte 5 | Headless core + Svelte render component + Enterprise pack | MIT (Community) / commercial (Enterprise) |\n| **AG Grid Community** | React, Angular, Vue, plain JS | Full grid + renderer; Svelte through community integrations | MIT |\n| **AG Grid Enterprise**| same | Adds the set filter, range selection, master/detail, tree, grouping, pivot, charts, Excel export, server-side row model | Commercial, per developer |\n| **TanStack Table** | React, Vue, Svelte, Solid, Qwik, Lit, JS | Headless engine **only**; the Svelte adapter is Svelte 5 | MIT |\n\nThe bundle sizes and prices are in the facts block above, with the date\nthey were read and the method behind the sizes.\n\n## When SvGrid is the right choice\n\n- You're on **Svelte 5** and want a grid that uses the runtime's idioms\n (snippets for cells, `$state` for data, `$derived` for aggregates) -\n not a React-port pretending to be Svelte.\n- You want a **headless core you can render yourself** AND a\n default-styled component for the 80% case. Most \"headless\" libraries\n make you write the markup; most \"monolith\" libraries make you fight\n the markup. SvGrid does both in one package.\n- You need **clean theming via CSS custom properties** and a documented\n `--sg-*` token surface, not a hard-coded class soup.\n- You ship under **strict CSP** (no `eval`, no `new Function`, no\n inline scripts). SvGrid runs clean; AG Grid Community does too.\n TanStack Table is engine-only so the question doesn't apply.\n- You want **SSR markup that is meaningful before hydration** (good\n first paint, SEO, SvelteKit `+page.server` integration). SvGrid +\n TanStack Table both qualify. AG Grid renders client-side.\n- You want the features AG Grid sells as Enterprise - the set filter,\n range selection, master/detail, tree data, grouping with aggregation,\n integrated charts, flat server-side infinite scroll - **in the MIT\n core**. (Server-side grouping, tree, pivot and transactions are\n Enterprise in both.)\n\n## When AG Grid is the right choice\n\n- You're on **React, Angular, or Vue**, not Svelte. SvGrid is\n Svelte-only, and ag-grid.com points Svelte users to community-built\n integrations rather than an official package.\n- You need a push-based **viewport row model** for a real-time trading\n blotter, where the server streams the visible window over a socket.\n SvGrid's server-side row model covers sort, filter, grouping, tree,\n pivot, transactions and select-all across unloaded rows, and its\n real-time page merges deltas into loaded rows, but it does not have the\n viewport model.\n- You need **custom tool panels**. SvGrid's tool panel is a fixed\n Columns + Filters pair. Custom filter components are covered by a\n column's `customFilter`.\n- You need **AG Charts** for maps, network graphs, 3D or a canvas renderer.\n SvGrid's chart is SVG with decimation, and it ships 29 types, a configurable\n axis model, synchronized charts, a financial toolkit with indicator panes\n and drawing tools, a builder and PDF export in the MIT core; it does not\n draw maps.\n- You want a vendor with **a decade of enterprise procurement paperwork**\n already on file.\n\n## When TanStack Table is the right choice\n\n- You want a **rendering-framework-agnostic engine** so the same\n business logic powers React + Svelte + Solid surfaces in your\n monorepo.\n- You're already in the TanStack ecosystem (Query, Router, Form,\n Virtual) and want one mental model.\n- You're happy writing **all the markup yourself** - the row recycling,\n the keyboard map, the ARIA roles, the focus management, the\n drag-to-resize. That's the cost of \"engine only\".\n\n## Feature parity at a glance\n\nYes: shipped in that edition. Partial: needs extra work or a companion\npackage. No: not available. The AG Grid columns follow ag-grid.com's\nCommunity vs Enterprise page and its Set Filter, Tree Data and CSV export\npages; the dated list of those sources is on the\n[SvGrid vs AG Grid](https://svgrid.com/compare/ag-grid/) page, and the\nTanStack sources on [SvGrid vs TanStack Table](https://svgrid.com/compare/tanstack-table/).\n\n| | SvGrid Community | SvGrid Enterprise | AG Grid Community | AG Grid Enterprise | TanStack Table |\n| ------------------------------- | ---------------- | ----------------- | ----------------- | ------------------ | -------------- |\n| Headless core (engine only) | Yes | Yes | No | No | Yes |\n| Default render component | Yes (Svelte 5) | Yes | Yes (each framework) | Yes | No |\n| Sort (multi-column) | Yes | Yes | Yes | Yes | Yes (engine) |\n| Filter menu (operator + facet) | Yes | Yes | Partial (text and number filters; the set filter is Enterprise) | Yes | No |\n| Filter row | Yes | Yes | Yes | Yes | No |\n| Pagination | Yes | Yes | Yes | Yes | Yes (engine) |\n| Grouping + aggregation | Yes | Yes | No (Enterprise) | Yes | Yes (engine) |\n| Tree / expand-collapse rows | Yes | Yes | No (Enterprise) | Yes | Yes (engine) |\n| Master/detail | Yes | Yes | No (Enterprise) | Yes | No |\n| Cell range selection + copy/paste | Yes | Yes | No (Enterprise) | Yes | No |\n| Fill handle | Yes | Yes | No (Enterprise) | Yes | No |\n| Inline editing (typed editors) | Yes | Yes | Yes | Yes | No |\n| Column virtualization | Yes | Yes | Yes | Yes | No |\n| Row virtualization | Yes | Yes | Yes | Yes | Partial (pair with TanStack Virtual) |\n| Column pinning (left/right) | Yes | Yes | Yes | Yes | No |\n| Column tool panel | Yes | Yes | No (Enterprise) | Yes | No |\n| Context menu | Yes | Yes | No (Enterprise) | Yes | No |\n| Fit-to-width with shrink | Yes | Yes | Partial | Yes | No |\n| WAI-ARIA grid pattern | Yes | Yes | Yes | Yes | No (your markup) |\n| Server-side data, flat (sort / filter / paging / infinite scroll) | Yes (built in) | Yes (built in) | Yes (infinite row model) | Yes (built in) | Partial (manual flags) |\n| Server-side row model (lazy grouping, tree, pivot, transactions, select-all across unloaded rows) | No (Enterprise) | Yes (built in) | No (Enterprise) | Yes (built in) | No |\n| Global search over server-side data | Yes (`filterModel.global`) | Yes | No (the quick filter is client-side only) | No | n/a |\n| CSP-clean (no eval, no inline) | Yes | Yes | Yes | Yes | n/a |\n| Meaningful SSR markup | Yes | Yes | No | No | depends on your markup |\n| CSV / TSV / JSON export | Yes | Yes | Yes (CSV) | Yes | No |\n| Excel (.xlsx) / PDF export | No | Yes | No | Yes (Excel) | No |\n| Print | No | Yes | No | No | No |\n| Excel / CSV import | No | Yes | No | No | No |\n| In-grid AI helpers (bring your own model) | Yes | Yes | No | No | No |\n| MCP server for AI assistants | Yes (`@svgrid/mcp`) | Yes | Yes (`ag-mcp`) | Yes | No (agent skills ship in the package) |\n| Pivot table | No | Yes | No | Yes | No (custom) |\n| Integrated charts | Yes (29 types, builder, financial toolkit) | Yes | No | Yes (AG Charts) | No |\n| Support response time | GitHub (best-effort) | 1 business day; 1h sev-1 on Enterprise | GitHub (best-effort) | no number published | GitHub; paid Enterprise Support offered |\n| Theming via CSS variables | Yes (`--sg-*`) | Yes | Yes (theme builder) | Yes | n/a |\n| Source button per demo | Yes (gallery) | Yes | No | No | No |\n\n## Bundle size\n\nThe facts block at the top of the page has the gzipped size of each\npackage, measured by the same method: each package built alone with Vite\nin library mode, minified, gzip level 9, Svelte external because it is a\npeer dependency. SvGrid's own number comes from `pnpm size:json`\n(`packages/grid/scripts/measure-size.mjs`), the competitors' from\n`pnpm competitors:measure`; both write `docs/_data` and the block is\nregenerated from there, so the page cannot quote a size nobody measured.\n\nWhat the SvGrid number contains: the whole render component -\nvirtualization, Excel-style filters, inline editing, grouping, tree,\nmaster/detail, cell selection and accessibility - in one import, plus a\nseparate stylesheet. Charts, date and time editors, menus and export split\ninto `import()` chunks that load on demand rather than shipping in the\ninitial bundle; the headless core on its own is a small fraction of the\nrender component, and `@svgrid/enterprise` features are separate subpath\nimports. The per-entry breakdown, including the headless core and the\nlazy chunks, is in the [bundle size reference](./bundle-size.md).\n\nWhat the AG Grid number contains: `createGrid` with every Community\nmodule registered and the Quartz theme, whose CSS ships inside the script\nunder the theming API, so there is no separate stylesheet to add. The\nTanStack number is the whole Svelte adapter with its engine: no\nvirtualizer, filter UI or editors, because a headless library has none.\n\n## Measured performance\n\nA vendor's own benchmark is worth nothing unless you can rerun it, so the\nharness is in the repository and takes an adapter per grid. Adding another\ngrid is about forty lines in `examples/src/bench/adapters.ts` and nothing\nelse.\n\n```bash\n# headless, prints the table and writes tests/perf/.last-compare.json\nBENCH_REPEATS=7 BENCH_GRIDS=svgrid,aggrid,svar,tanstack pnpm bench:compare\n\n# move that run into docs/_data/competitors.json, then regenerate this page\nnode tools/record-benchmarks.mjs && node tools/sync-guide-facts.mjs\n\n# or drive it by hand\npnpm --filter @svgrid/grid-example-gallery dev # then open /bench.html\n```\n\nEach operation is timed until the grid's DOM reflects it, plus one frame:\n\n- **Mount** - fresh container, create the grid, until rows are painted.\n- **Sort** - change the sort on one column, until the new order is painted.\n Text sorts `name` (tens of thousands of distinct values), numeric sorts\n `amount`.\n- **Filter** - apply a `contains` filter to one column.\n- **Scroll** - 180 frames at 60 px/frame, sampling frame intervals.\n\n### Results\n\nThe table is generated from the ledger by `tools/sync-guide-facts.mjs`;\nthe versions and the date are part of it.\n\n<!-- bench:start -->\nMeasured 12 Sep 2026 with `pnpm bench:compare` (tests/perf/compare.spec.ts, recorded by tools/record-benchmarks.mjs): 100,000 rows x 9 columns; 1000 x 520 px container, 32 px rows, 140 px columns, each grid with its default theme; fastest of 7 samples per operation, each grid in a fresh page; Chromium through Playwright, headless; win32 10.0.26200 x64, Intel(R) Core(TM) Ultra 7 255U; a developer workstation, not a dedicated bench rig. Lower is better.\n\n| Operation | SvGrid | AG Grid Community | SVAR Svelte DataGrid | TanStack Table, minimal DOM |\n| --- | --- | --- | --- | --- |\n| Mount | 75.5 ms | 51.4 ms | 11.4 ms | 196 ms |\n| Sort, text column | 149 ms | 282.8 ms | 187.2 ms | 466 ms |\n| Sort, numeric column | 115.7 ms | 270 ms | 165.1 ms | 374.6 ms |\n| Filter, one column (indicative only, see below) | 99.6 ms | 170.4 ms | 32.6 ms | 111.7 ms |\n| Scroll, p95 frame | 34.6 ms | 35.9 ms | 34.8 ms | 35.3 ms |\n| Frames dropped while scrolling, of 180 | 0 | 0 | 0 | 1 |\n| Rows kept in the DOM (virtualization on) | 27 | 35 | 19 | 27 |\n\nVersions: `@svgrid/grid` 3.0.3, `ag-grid-community` 36.1.0, `wx-svelte-grid` 2.7.0, `@tanstack/svelte-table` 9.2.4.\n<!-- bench:end -->\n\nRead that as: **SvGrid sorts faster than AG Grid Community and SVAR on both\ncolumn types, scrolling is level across all four, and mounting is where the\nlighter grids are ahead** - SVAR paints its first rows fastest, AG Grid is\nahead of SvGrid, and the TanStack renderer pays for building a row object\nper record before it can draw anything. Anyone claiming a single headline\nnumber for a grid comparison is selling something.\n\n**The filter row is not a like-for-like comparison and is shown only because\nhiding a number we win looks worse than explaining it.** The grids' single-\ncolumn filter APIs differ: the harness drives AG Grid's quick filter, which\nsearches every column and is strictly more work than the single-column\n`contains` SvGrid performs, while SVAR and TanStack run a predicate over the\none column. Do not read a ratio into that row.\n\n**The TanStack column is an engine plus the least DOM an app could draw.**\nTanStack Table renders nothing, so its adapter draws the row model through\na fixed-height windowed table with no virtualizer library, no filter UI and\nno editors. Its mount and sort numbers include creating the engine's row\nobjects for every record; the grids around it do the same work inside their\nown models.\n\nSorting is where SvGrid's distance comes from, and it is not subtle: SvGrid\nresolves each sort clause once and precomputes a key array, and for a\ncolumn whose distinct values are far fewer than its rows it collates the\ndistinct values once and sorts by rank. Mounting is the other direction -\nsee the accounting in [benchmarks](./benchmarks.md).\n\nTwo methodology notes, both of which were bugs in this harness before they\nwere notes, and both of which changed the numbers by more than any code\nchange did:\n\n- **Each grid runs in a fresh page.** They used to run one after another in\n the same one, where the first warmed the JIT, fragmented the heap and\n left a hundred thousand rows of garbage for the next. Back-to-back runs\n on an idle machine disagreed with themselves by a factor of two.\n Run-to-run variance is now a few percent.\n- **Operations are timed until the DOM changes, not for a fixed two\n frames.** The old fixed wait put a two-frame floor under every number -\n an operation that recomputed nothing at all measured the floor, and a\n mount whose rows were already in the DOM measured pure waiting - which\n flattered whichever grid was slower.\n- **Each figure is the fastest of the samples, not the median.** Noise\n only ever adds time, so the minimum is the cleanest estimate of what the\n machine can do, and medians on this box drifted by a third between\n consecutive runs - more than most of the differences being measured.\n Applied identically to every grid.\n\nWhat this table deliberately does not include:\n\n- **Features the grids do not measure the same way.** Grouping, pivot,\n editing and export all differ enough in shape that a single timing would\n compare two different amounts of work. The filter row above is the one\n case we publish anyway, labelled, rather than quietly dropping a result\n that happens to favour us.\n- **Any grid we cannot license for this.** The harness is grid-agnostic on\n purpose, but some commercial grids licence their software in ways that\n restrict using it to produce competitive claims. Where that is the case\n we do not ship an adapter, and we would rather have an obviously\n incomplete table than a legally dubious one. Run your own shortlist.\n- **Your workload.** One dataset shape, one machine, one browser, default\n configuration for every grid. If a grid decision rides on this, clone the\n harness and put your own data in it.\n\n## Migrating from AG Grid\n\nThe most common starting point. See\n[Migrating from AG Grid](./migrating-from-ag-grid.md) for a side-by-side\ntranslation of column defs, features, filtering, editing, and the\nimperative API. The example that guide is built around runs here:\n\n<div data-docs-demo=\"139-migration-from-ag-grid\" data-height=\"560\"></div>\n\n## Migrating from TanStack Table\n\nThe map is one-to-one - SvGrid's headless core speaks the vocabulary\nTanStack Table v9 introduced. The big differences:\n\n- Replace `createTable(opts)` with `createSvGrid(opts)`. Same state\n machine, same features object.\n- Replace the row-model factories with the same names from\n `@svgrid/grid`.\n- The render layer changes - TanStack hands you `FlexRender` + the\n row model; SvGrid lets you keep that headless approach OR drop in\n the default `<SvGrid>` component.\n\nThe headless demo below drives a plain `<table>` from `createSvGrid`, the\nway a TanStack app would:\n\n<div data-docs-demo=\"186-headless-table\" data-height=\"520\"></div>\n\nAnd the same rows through the render component, which is the part TanStack\nleaves to you. The example runs against these rows:\n\n```svelte {preamble}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns } from '@svgrid/grid'\n\n type Order = {\n id: number\n customer: string\n region: string\n status: string\n amount: number\n }\n\n const orders: Order[] = [\n { id: 1, customer: 'Ada Lovelace', region: 'EMEA', status: 'open', amount: 1240 },\n { id: 2, customer: 'Grace Hopper', region: 'AMER', status: 'shipped', amount: 860 },\n { id: 3, customer: 'Linus Torvalds', region: 'EMEA', status: 'closed', amount: 2210 },\n { id: 4, customer: 'Radia Perlman', region: 'APAC', status: 'open', amount: 430 },\n { id: 5, customer: 'Barbara Liskov', region: 'AMER', status: 'pending', amount: 1780 },\n ]\n\n let rows = $state<Order[]>(orders)\n\n const columns: GridColumns<Order> = [\n { field: 'customer', header: 'Customer', width: 180 },\n { field: 'region', header: 'Region', width: 110 },\n { field: 'status', header: 'Status', width: 110 },\n { field: 'amount', header: 'Amount', width: 120, editorType: 'number', format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n```\n\n```svelte {runnable}\n<SvGrid data={rows} {columns} sortable filterable selectable />\n```\n\n## Pricing\n\nSvGrid Community is MIT - free for commercial use, no attribution\nrequired at runtime. SvGrid Enterprise is a per-developer licence: $599\nper developer per year for the enterprise grid or $999 with the\nspreadsheet and Studio, both for unlimited applications; see\n<https://svgrid.com/pricing/> for what each tier adds.\n\nAG Grid Community is MIT. AG Grid Enterprise is priced per developer with\none year of updates; the figure ag-grid.com listed on the date we read it\nis in the facts block at the top of this page, and the\n[SvGrid vs AG Grid](https://svgrid.com/compare/ag-grid/) page carries the\nlink. Compare what each paid tier adds before comparing prices: most of\nwhat AG Grid sells as Enterprise is in SvGrid's free core.\n\nTanStack Table is MIT and sponsor-funded; tanstack.com offers paid\nEnterprise Support as private consulting.\n\n## Frequently asked questions\n\n### What is the best data grid for Svelte 5?\n\nFor a Svelte-5-native grid with a batteries-included render component, SvGrid is\nbuilt around runes and snippets. TanStack Table is a strong headless-only\nchoice if you want to build the DOM layer yourself across frameworks, and its\nv9 Svelte adapter targets Svelte 5. AG Grid is the most feature-complete but\nlives in React, Angular and Vue first and reaches Svelte through\ncommunity-built integrations.\n\n### Is SvGrid a good AG Grid alternative?\n\nYes, for Svelte projects. SvGrid is a fraction of AG Grid Community's\nmeasured size on a Svelte page (the facts block has both numbers and the\nmethod), is MIT-licensed for commercial use, and offers `@svgrid/enterprise`\nfor export, print, import and pivot at a per-developer price. On the\nbenchmark above it sorts a hundred-thousand-row column faster on both\ncolumn types and scrolls level; AG Grid mounts quicker. It does not match\nevery AG Grid Enterprise feature - see the\n[missing-features list](./missing-features.md) for the gaps.\n\n### SvGrid vs TanStack Table - which should I pick?\n\nPick SvGrid if you want virtualization, Excel-style filters, selection, and\ninline editing working out of the box on Svelte 5. Pick TanStack Table if you\nwant a framework-agnostic headless engine and are happy to build the rendering,\nvirtualization, and editing UI yourself. Both are MIT-licensed.\n\n### Does the TanStack Svelte adapter support Svelte 5?\n\nYes. The `@tanstack/svelte-table` v9 line declares a Svelte 5 peer range\nand passes data through getters rather than stores; the facts block above\nshows the version read from npm. The earlier `createSvelteTable` constructor\nis gone in v9.\n\n### Does AG Grid have an MCP server like SvGrid?\n\nYes. `ag-mcp` (MIT) gives an assistant AG Grid's docs, examples and\nversion-specific upgrade prompts. `@svgrid/mcp` does the same for SvGrid\nand adds a code checker that validates props and imports against the real\nAPI surface, a scaffold tool and a preview. Neither is an in-grid feature;\nSvGrid's natural-language filter, smart fill and summarise helpers are, and\nthey ship free in `@svgrid/grid` with a model provider you register.\n\n### How big is the SvGrid bundle?\n\nThe facts block at the top of this page has the measured gzipped size of the\nfull `<SvGrid>` render component and its stylesheet, read from\n`docs/_data/svgrid-size.json`, which `pnpm size:json` writes. The\n[bundle size reference](./bundle-size.md) breaks it down by entry:\nthe headless core on its own, the core subpath, the charts, and the lazy\nchunks that load on demand.\n\n## See also\n\n- [Why headless?](../why-headless.md) - the design decision behind\n SvGrid's two-layer architecture.\n- [Migrating from AG Grid](./migrating-from-ag-grid.md) - the\n practical recipe.\n- [Migrating from TanStack Table](./migrating-from-tanstack-table.md) - the\n one-to-one API map.\n- [Benchmarks](./benchmarks.md) - how the numbers are produced and what\n they leave out.\n- [Enterprise feature pack](../enterprise/README.md) - what SvGrid charges for.\n- [Missing features](./missing-features.md) - the honest gap list\n versus AG Grid Enterprise.\n- [All comparisons](https://svgrid.com/compare/) - one page per grid, with\n dated sources for every claim.\n"
|
|
5347
5347
|
},
|
|
5348
5348
|
{
|
|
5349
5349
|
"slug": "help/conditional-form-schema",
|
|
@@ -5490,7 +5490,7 @@ export const docs = [
|
|
|
5490
5490
|
"path": "docs/help/filtering/custom-column-filters.md",
|
|
5491
5491
|
"title": "Custom column filters",
|
|
5492
5492
|
"section": "Filtering",
|
|
5493
|
-
"markdown": "# Custom column filters\n\nWhen the built-in operators and the set-filter pattern are not enough,\
|
|
5493
|
+
"markdown": "# Custom column filters\n\nWhen the built-in operators and the set-filter pattern are not enough,\ngive the column its own filter, or take over the filter pipeline.\n<div data-docs-demo=\"03-excel-filters\" data-height=\"540\"></div>\n\n## Your own filter control: `customFilter`\n\nA column's `customFilter` replaces the operator condition with your own\nmodel and control: a range slider that holds `[min, max]`, a tag picker that\nholds `string[]`, a \"last N days\" widget that holds a number.\n\n- `predicate(model)` returns the row test. It runs once per filter change,\n so parse dates or build a `Set` there, not inside the returned function.\n The test gets the column's value and the row's data.\n- `menu` is a snippet for the filter menu and the tool panel's Filters tab.\n `floating` is one for the floating filter row. Each falls back to the\n other, so one snippet can serve all three.\n- The snippet receives `{ column, model, setModel, surface, values, close }`.\n `setModel(null)` clears the filter. `values()` returns the column's\n distinct values under the other columns' filters, the same list the Values\n checklist shows.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type CustomFilterContext, type GridColumns } from '@svgrid/grid'\n\n type Deal = { id: number; company: string; stage: string; amount: number }\n\n const deals: Deal[] = [\n { id: 1, company: 'Northwind', stage: 'Proposal', amount: 4200 },\n { id: 2, company: 'Contoso', stage: 'Negotiation', amount: 18500 },\n { id: 3, company: 'Fabrikam', stage: 'Closed won', amount: 32000 },\n { id: 4, company: 'Tailspin', stage: 'Proposal', amount: 950 },\n { id: 5, company: 'Litware', stage: 'Negotiation', amount: 27400 },\n { id: 6, company: 'Adventure', stage: 'Closed won', amount: 12800 },\n ]\n\n type Range = [number, number]\n\n const columns: GridColumns<Deal> = [\n { field: 'company', header: 'Company', width: 160 },\n { field: 'stage', header: 'Stage', width: 140 },\n {\n field: 'amount',\n header: 'Amount',\n width: 220,\n format: { type: 'currency', currency: 'USD' },\n customFilter: {\n predicate: ([min, max]: Range) => (value) => {\n const n = Number(value)\n return n >= min && n <= max\n },\n menu: amountRange,\n floating: amountRange,\n },\n },\n ]\n</script>\n\n{#snippet amountRange(f: CustomFilterContext<Deal, Range>)}\n {@const max = f.model?.[1] ?? 40000}\n <label style=\"display: flex; align-items: center; gap: 6px; font-size: 12px;\">\n <input\n type=\"range\"\n min=\"0\"\n max=\"40000\"\n step=\"1000\"\n value={max}\n oninput={(e) => {\n const next = Number(e.currentTarget.value)\n f.setModel(next >= 40000 ? null : [0, next])\n }}\n />\n <span>{max >= 40000 ? 'Any' : `up to ${max.toLocaleString()}`}</span>\n </label>\n{/snippet}\n\n<SvGrid data={deals} {columns} filterable filterMode=\"row\" />\n```\n\nThe model is ANDed with the column's operator condition and its Values\nchecklist, and is part of the rest of the filter state:\n\n- `api.setCustomFilter(columnId, model)` sets it, `null` clears it, and\n `api.getCustomFilter(columnId)` reads it. `api.clearFilter` and\n `api.clearAllFilters` clear it with everything else.\n- `api.getState()` returns `customFilters` while any model is set, and\n `api.setState({ customFilters })` restores them. Keep the model\n JSON-serializable if you save views.\n- `onFiltersChange` reports active models under `custom`, keyed by column id.\n With `externalFilter` the grid only records the model, so a server\n translates it into its query; `createServerDataSource` forwards it as\n `filterModel.custom`.\n- An inactive model filters nothing and does not light the funnel. By\n default `null`, `undefined`, `''` and `[]` are inactive; pass\n `isActive(model)` for anything else.\n- `showValues: false` drops the Values checklist from the menu when it adds\n nothing, as for a slider over thousands of distinct prices.\n\nA `customFilter` with only `predicate` keeps the built-in UI; the model is\nthen set only through the API.\n\n## Option A - supply a `filterFn`\n\nThe simplest extension: register your own filter function and reference it\nper column.\n\n```ts\n// types\ndeclare module '@svgrid/grid' {\n interface FilterFnsRegistry {\n inListCSV: (value: unknown, query: string) => boolean\n }\n}\n\n// register\nimport { filterFns } from '@svgrid/grid'\n;(filterFns as any).inListCSV = (value: unknown, query: string) => {\n const items = String(query).split(',').map((s) => s.trim().toLowerCase())\n return items.includes(String(value ?? '').toLowerCase())\n}\n\n// use\nconst columnFilters = [{ id: 'status', value: 'active,pending', fn: 'inListCSV' }]\n```\n\n`filterFns` is a plain object so monkey-patching works - but the\n`FilterFnsRegistry` module augmentation is what tells TypeScript about the\nnew key.\n\n## Option B - control the entire filter pipeline\n\nPass `externalFilter={true}` and feed the grid a pre-filtered array. The\ngrid still records the in-UI filter state (so the menu and chips light\nup correctly) but does **not** filter rows itself - you do, in response\nto the `onFiltersChange` callback. This is the path server-side data\nsources, large remote datasets, and tree data take.\n\n```svelte\n<script lang=\"ts\">\n let rawRows = $state<Person[]>([])\n let filters = $state<{ id: string; operator: string; value: string }[]>([])\n const filtered = $derived(myComplexFilter(rawRows, filters))\n</script>\n\n<SvGrid\n data={filtered}\n {columns}\n features={features}\n filterMode=\"menu\"\n externalFilter={true}\n onFiltersChange={(next) => (filters = next.columns)}\n/>\n```\n\nThe same pattern exists for sort (`externalSort` + `onSortingChange`)\nand works on the same grid - see the [server-side demo](../../../examples/src/demos/09-server-side.svelte).\n\nIf you only want a single pre-filtered array and don't need the grid's\nfilter UI at all, pass `filterMode=\"none\"` instead and skip the\ncallbacks entirely.\n\n## Option C - custom column UI\n\nRender a custom header (via `header: () => renderSnippet(...)`) that\nincludes your own filter widget. Update controlled state from the widget;\nthe grid will react. See [Custom header components](../columns/custom-header-components.md).\n\n## A control that lives outside the grid\n\nWhen the filter control sits outside the grid, narrowing the array is the\nshortest path: filter it, hand the result to `data`. Everything downstream -\nsort, selection, the row count - follows the filtered set. For a control in\nthe grid's own filter surfaces, use `customFilter` above.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, renderSnippet, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n active: boolean\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000, active: true },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000, active: true },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000, active: false },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000, active: true },\n ]\n\n let query = $state('')\n\n // Match on initials: \"gh\" finds Grace Hopper. No operator expresses this.\n function initials(p: Person) {\n return p.name.split(' ').map((w) => w[0]!.toLowerCase()).join('')\n }\n\n const shown = $derived(\n query.trim() ? people.filter((p) => initials(p).startsWith(query.trim().toLowerCase())) : people,\n )\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 170 },\n ]\n</script>\n\n<input placeholder=\"Try: gh\" bind:value={query} />\n\n<SvGrid data={shown} {columns} sortable />\n```\n\n\n## Combining with the built-in filters\n\nYour predicate and the column filters compose without knowing about each\nother: yours narrows `data`, the grid narrows what it renders. Order does not\nmatter because both are pure.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, renderSnippet, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n active: boolean\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000, active: true },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000, active: true },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000, active: false },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000, active: true },\n ]\n\n let activeOnly = $state(true)\n\n const shown = $derived(activeOnly ? people.filter((p) => p.active) : people)\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 180 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'city', header: 'City', width: 140 },\n ]\n</script>\n\n<label><input type=\"checkbox\" bind:checked={activeOnly} /> Active only</label>\n\n<SvGrid data={shown} {columns} filterable filterMode=\"row\" sortable />\n```\n\n## See also\n\n- [Filter API](./filter-api.md)\n- [Set filter](./set-filter.md)\n"
|
|
5494
5494
|
},
|
|
5495
5495
|
{
|
|
5496
5496
|
"slug": "help/filtering/date-filter",
|
|
@@ -5504,7 +5504,7 @@ export const docs = [
|
|
|
5504
5504
|
"path": "docs/help/filtering/filter-api.md",
|
|
5505
5505
|
"title": "Filter API",
|
|
5506
5506
|
"section": "Filtering",
|
|
5507
|
-
"markdown": "# Filter API\n\nTwo surfaces - pick based on whether the caller is inside or outside the\ncomponent that owns the grid state.\n<div data-docs-demo=\"64-filter-between-operator\" data-height=\"540\"></div>\n\n## `SvGridApi` (imperative)\n\nAvailable via `<SvGrid onApiReady={(api) => /* … */}>`.\n\n```ts\napi.setFilter(\n columnId: string,\n filter: { operator: SvGridFilterOperator; value?: string } | null,\n): void\n\napi.clearFilter(columnId: string): void\n```\n\n`SvGridFilterOperator` is the union: `'contains' | 'equals' | 'startsWith'\n| 'greaterThan' | 'lessThan' | 'isBlank'`.\n\nPassing `null` clears the filter on that column.\n\n```ts\napi.setFilter('status', { operator: 'equals', value: 'active' })\napi.setFilter('age', { operator: 'greaterThan', value: '30' })\napi.setFilter('email', { operator: 'contains', value: '@example.com' })\napi.setFilter('department', null) // clear\napi.clearFilter('age') // same effect, sugared\n```\n\nThe wrapper internally writes into the `columnFilters` state slice and\nre-runs the filtered row model.\n\n## Observe filter state\n\nFor history, persistence, or server-side, subscribe via\n`onFiltersChange`:\n\n```svelte\n<script lang=\"ts\">\n let filters = $state<Array<{\n id: string\n operator: string\n value: string\n selectedValues?: Array<string>\n }>>([])\n let globalFilter = $state('')\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n filterMode=\"menu\"\n onFiltersChange={(next) => { filters = next.columns; globalFilter = next.global }}\n/>\n```\n\nThe callback receives a consolidated `{ global, columns }` shape so the\nthree internal stores (global search, per-column operator filter, facet\nchecklist) collapse into one payload you can serialise.\n\nFor server-side / external pipelines, also set `externalFilter={true}`\nso the grid doesn't filter the rows locally - see [Custom column\nfilters](./custom-column-filters.md).\n\n## Read the current filters\n\nFrom the imperative API there is no `getFilters()` getter today - read the\nunderlying state via the grid instance:\n\n```ts\nconst grid = api as unknown as { /* internals are not part of the public type */ }\n```\n\nThis is not a stable surface. If you need to read filters from outside,\ncontrol the state.\n\n## Setting a filter from code\n\n`setFilter(columnId, { operator, value })` applies the same filter the menu\nwould, so a \"show me only Platform\" button and the UI end up in one state rather\nthan two.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000 },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000 },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000 },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000 },\n { id: 5, name: 'Barbara Liskov', department: 'Platform', city: 'Boston', age: 52, salary: 172000 },\n ]\n\n let api = $state<SvGridApi<{}, Person> | null>(null)\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'salary', header: 'Salary', width: 140,\n format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n\n<div>\n <button type=\"button\" onclick={() => api?.setFilter('department', { operator: 'equals', value: 'Platform' })}>\n Platform only\n </button>\n <button type=\"button\" onclick={() => api?.setFilter('department', null)}>Clear</button>\n</div>\n\n<SvGrid data={people} {columns} filterable filterMode=\"menu\" onApiReady={(next) => (api = next)} />\n```\n\n\n## Reading what is applied\n\n`getFilters()` returns the live model, which is what you persist to a URL or a\nsaved view. Apply a filter above and the shape below is what you would store.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000 },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000 },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000 },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000 },\n { id: 5, name: 'Barbara Liskov', department: 'Platform', city: 'Boston', age: 52, salary: 172000 },\n ]\n\n let api = $state<SvGridApi<{}, Person> | null>(null)\n let model = $state('(no filters)')\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'age', header: 'Age', width: 90 },\n ]\n</script>\n\n<button type=\"button\" onclick={() => (model = JSON.stringify(api?.getFilters() ?? {}, null, 2))}>\n Read the filter model\n</button>\n\n<SvGrid data={people} {columns} filterable filterMode=\"row\" onApiReady={(next) => (api = next)} />\n\n<pre>{model}</pre>\n```\n\n## See also\n\n- [Filter conditions](./filter-conditions.md)\n- [Applying filters](./applying-filters.md)\n- [Missing features](../missing-features.md) - `api.getFilters()` would be nice.\n"
|
|
5507
|
+
"markdown": "# Filter API\n\nTwo surfaces - pick based on whether the caller is inside or outside the\ncomponent that owns the grid state.\n<div data-docs-demo=\"64-filter-between-operator\" data-height=\"540\"></div>\n\n## `SvGridApi` (imperative)\n\nAvailable via `<SvGrid onApiReady={(api) => /* … */}>`.\n\n```ts\napi.setFilter(\n columnId: string,\n filter: { operator: SvGridFilterOperator; value?: string } | null,\n): void\n\napi.clearFilter(columnId: string): void\n```\n\n`SvGridFilterOperator` is the union: `'contains' | 'equals' | 'startsWith'\n| 'greaterThan' | 'lessThan' | 'isBlank'`.\n\nPassing `null` clears the filter on that column.\n\n```ts\napi.setFilter('status', { operator: 'equals', value: 'active' })\napi.setFilter('age', { operator: 'greaterThan', value: '30' })\napi.setFilter('email', { operator: 'contains', value: '@example.com' })\napi.setFilter('department', null) // clear\napi.clearFilter('age') // same effect, sugared\n```\n\nThe wrapper internally writes into the `columnFilters` state slice and\nre-runs the filtered row model.\n\nA column with a [`customFilter`](./custom-column-filters.md) holds its own\nmodel rather than an operator and a value:\n\n```ts\napi.setCustomFilter('amount', [1000, 5000]) // whatever its predicate takes\napi.getCustomFilter('amount') // [1000, 5000]\napi.setCustomFilter('amount', null) // clear\n```\n\n## Observe filter state\n\nFor history, persistence, or server-side, subscribe via\n`onFiltersChange`:\n\n```svelte\n<script lang=\"ts\">\n let filters = $state<Array<{\n id: string\n operator: string\n value: string\n selectedValues?: Array<string>\n }>>([])\n let globalFilter = $state('')\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n filterMode=\"menu\"\n onFiltersChange={(next) => { filters = next.columns; globalFilter = next.global }}\n/>\n```\n\nThe callback receives a consolidated `{ global, columns }` shape so the\nthree internal stores (global search, per-column operator filter, facet\nchecklist) collapse into one payload you can serialise.\n\nFor server-side / external pipelines, also set `externalFilter={true}`\nso the grid doesn't filter the rows locally - see [Custom column\nfilters](./custom-column-filters.md).\n\n## Read the current filters\n\nFrom the imperative API there is no `getFilters()` getter today - read the\nunderlying state via the grid instance:\n\n```ts\nconst grid = api as unknown as { /* internals are not part of the public type */ }\n```\n\nThis is not a stable surface. If you need to read filters from outside,\ncontrol the state.\n\n## Setting a filter from code\n\n`setFilter(columnId, { operator, value })` applies the same filter the menu\nwould, so a \"show me only Platform\" button and the UI end up in one state rather\nthan two.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000 },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000 },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000 },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000 },\n { id: 5, name: 'Barbara Liskov', department: 'Platform', city: 'Boston', age: 52, salary: 172000 },\n ]\n\n let api = $state<SvGridApi<{}, Person> | null>(null)\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'salary', header: 'Salary', width: 140,\n format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n\n<div>\n <button type=\"button\" onclick={() => api?.setFilter('department', { operator: 'equals', value: 'Platform' })}>\n Platform only\n </button>\n <button type=\"button\" onclick={() => api?.setFilter('department', null)}>Clear</button>\n</div>\n\n<SvGrid data={people} {columns} filterable filterMode=\"menu\" onApiReady={(next) => (api = next)} />\n```\n\n\n## Reading what is applied\n\n`getFilters()` returns the live model, which is what you persist to a URL or a\nsaved view. Apply a filter above and the shape below is what you would store.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000 },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000 },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000 },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000 },\n { id: 5, name: 'Barbara Liskov', department: 'Platform', city: 'Boston', age: 52, salary: 172000 },\n ]\n\n let api = $state<SvGridApi<{}, Person> | null>(null)\n let model = $state('(no filters)')\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'age', header: 'Age', width: 90 },\n ]\n</script>\n\n<button type=\"button\" onclick={() => (model = JSON.stringify(api?.getFilters() ?? {}, null, 2))}>\n Read the filter model\n</button>\n\n<SvGrid data={people} {columns} filterable filterMode=\"row\" onApiReady={(next) => (api = next)} />\n\n<pre>{model}</pre>\n```\n\n## See also\n\n- [Filter conditions](./filter-conditions.md)\n- [Applying filters](./applying-filters.md)\n- [Missing features](../missing-features.md) - `api.getFilters()` would be nice.\n"
|
|
5508
5508
|
},
|
|
5509
5509
|
{
|
|
5510
5510
|
"slug": "help/filtering/filter-conditions",
|
|
@@ -5518,7 +5518,7 @@ export const docs = [
|
|
|
5518
5518
|
"path": "docs/help/filtering/floating-filters.md",
|
|
5519
5519
|
"title": "Floating filters",
|
|
5520
5520
|
"section": "Filtering",
|
|
5521
|
-
"markdown": "# Floating filters\n\n\"Floating filters\" are the always-visible filter inputs that sit **between** the\nheader row and the body. In SvGrid the filter row is **per-operator and\nper-type**: each cell has a funnel to pick the operator, the value input matches\nthe column's type, and `between` shows a second inline \"To\" input.\n<div data-docs-demo=\"179-floating-filters\" data-height=\"520\"></div>\n\n## Enable the filter row\n\n```svelte\n<SvGrid {data} {columns} features={features} filterMode=\"row\" />\n```\n\n## Per-operator, per-type\n\n- **Operator per cell** - click the funnel in a filter-row cell to switch the\n operator (`contains`, `equals`, `greaterThan`, `between`, `isBlank`, …). The\n chosen operator drives that column's filtering; it is the same value the column\n menu uses, so the two surfaces stay in sync.\n- **Input matches the column** - a `number` column gets a number input, a `date`\n column a date picker, text a text input. This is driven by the column's\n `editorType`.\n- **`between` inline** - selecting Between shows a second **To** input right in\n the row, so ranges work without opening the full menu. A date column swaps in\n a single **range field** instead: one popover, two-month calendar, both ends\n picked in one gesture. A range needs both ends - the filter stays inactive\n until the second one is picked.\n\n```ts\nconst columns = [\n { field: 'age', header: 'Age', editorType: 'number' }, // number input\n { field: 'joinedAt', header: 'Joined', editorType: 'date' }, // date picker\n { field: 'name', header: 'Name', editorType: 'text' }, // text input\n]\n```\n\nTo show both the filter row and the column-menu funnel, set both surfaces:\n\n```svelte\n<SvGrid\n {data} {columns} features={features}\n showFilterRow={true}\n showColumnFilters={true}\n/>\n```\n\n## The filter row\n\n`showFilterRow` puts a persistent input under every header. It wins over\n`filterMode` when both are set, which is how you get a filter row alongside the\nmenu rather than instead of it.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n joined: string\n active: boolean\n trend: number[]\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000, joined: '2021-03-01', active: true, trend: [4, 6, 5, 9, 12, 11, 15] },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000, joined: '2019-07-15', active: true, trend: [9, 8, 11, 10, 14, 16, 15] },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000, joined: '2020-01-20', active: false, trend: [12, 10, 9, 7, 6, 6, 4] },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000, joined: '2022-09-05', active: true, trend: [3, 5, 4, 8, 7, 11, 13] },\n ]\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'age', header: 'Age', width: 90, editorType: 'number' },\n ]\n</script>\n\n<SvGrid data={people} {columns} filterable showFilterRow sortable />\n```\n\n\n## Row and menu together\n\nThe row handles the common case in one keystroke; the menu behind each header\nstill carries the full operator list for the times that is not enough.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n joined: string\n active: boolean\n trend: number[]\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000, joined: '2021-03-01', active: true, trend: [4, 6, 5, 9, 12, 11, 15] },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000, joined: '2019-07-15', active: true, trend: [9, 8, 11, 10, 14, 16, 15] },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000, joined: '2020-01-20', active: false, trend: [12, 10, 9, 7, 6, 6, 4] },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000, joined: '2022-09-05', active: true, trend: [3, 5, 4, 8, 7, 11, 13] },\n ]\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'city', header: 'City', width: 150 },\n { field: 'salary', header: 'Salary', width: 150, editorType: 'number',\n format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n\n<SvGrid data={people} {columns} filterable filterMode=\"menu\" showFilterRow />\n```\n\n## See also\n\n- [Overview](./overview.md)\n- [Filter conditions](./filter-conditions.md) - two conditions per column\n- [Custom header components](../columns/custom-header-components.md)\n"
|
|
5521
|
+
"markdown": "# Floating filters\n\n\"Floating filters\" are the always-visible filter inputs that sit **between** the\nheader row and the body. In SvGrid the filter row is **per-operator and\nper-type**: each cell has a funnel to pick the operator, the value input matches\nthe column's type, and `between` shows a second inline \"To\" input.\n<div data-docs-demo=\"179-floating-filters\" data-height=\"520\"></div>\n\n## Enable the filter row\n\n```svelte\n<SvGrid {data} {columns} features={features} filterMode=\"row\" />\n```\n\n## Per-operator, per-type\n\n- **Operator per cell** - click the funnel in a filter-row cell to switch the\n operator (`contains`, `equals`, `greaterThan`, `between`, `isBlank`, …). The\n chosen operator drives that column's filtering; it is the same value the column\n menu uses, so the two surfaces stay in sync.\n- **Input matches the column** - a `number` column gets a number input, a `date`\n column a date picker, text a text input. This is driven by the column's\n `editorType`.\n- **`between` inline** - selecting Between shows a second **To** input right in\n the row, so ranges work without opening the full menu. A date column swaps in\n a single **range field** instead: one popover, two-month calendar, both ends\n picked in one gesture. A range needs both ends - the filter stays inactive\n until the second one is picked.\n\n```ts\nconst columns = [\n { field: 'age', header: 'Age', editorType: 'number' }, // number input\n { field: 'joinedAt', header: 'Joined', editorType: 'date' }, // date picker\n { field: 'name', header: 'Name', editorType: 'text' }, // text input\n]\n```\n\nTo show both the filter row and the column-menu funnel, set both surfaces:\n\n```svelte\n<SvGrid\n {data} {columns} features={features}\n showFilterRow={true}\n showColumnFilters={true}\n/>\n```\n\n## The filter row\n\n`showFilterRow` puts a persistent input under every header. It wins over\n`filterMode` when both are set, which is how you get a filter row alongside the\nmenu rather than instead of it.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n joined: string\n active: boolean\n trend: number[]\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000, joined: '2021-03-01', active: true, trend: [4, 6, 5, 9, 12, 11, 15] },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000, joined: '2019-07-15', active: true, trend: [9, 8, 11, 10, 14, 16, 15] },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000, joined: '2020-01-20', active: false, trend: [12, 10, 9, 7, 6, 6, 4] },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000, joined: '2022-09-05', active: true, trend: [3, 5, 4, 8, 7, 11, 13] },\n ]\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'department', header: 'Department', width: 160 },\n { field: 'age', header: 'Age', width: 90, editorType: 'number' },\n ]\n</script>\n\n<SvGrid data={people} {columns} filterable showFilterRow sortable />\n```\n\n\n## Row and menu together\n\nThe row handles the common case in one keystroke; the menu behind each header\nstill carries the full operator list for the times that is not enough.\n\n```svelte {runnable}\n<script lang=\"ts\">\n import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'\n\n type Person = {\n id: number\n name: string\n department: string\n city: string\n age: number\n salary: number\n joined: string\n active: boolean\n trend: number[]\n }\n\n const people: Person[] = [\n { id: 1, name: 'Ada Lovelace', department: 'Engineering', city: 'London', age: 36, salary: 142000, joined: '2021-03-01', active: true, trend: [4, 6, 5, 9, 12, 11, 15] },\n { id: 2, name: 'Grace Hopper', department: 'Engineering', city: 'New York', age: 45, salary: 168000, joined: '2019-07-15', active: true, trend: [9, 8, 11, 10, 14, 16, 15] },\n { id: 3, name: 'Linus Torvalds', department: 'Platform', city: 'Portland', age: 54, salary: 155000, joined: '2020-01-20', active: false, trend: [12, 10, 9, 7, 6, 6, 4] },\n { id: 4, name: 'Radia Perlman', department: 'Networking', city: 'Seattle', age: 49, salary: 161000, joined: '2022-09-05', active: true, trend: [3, 5, 4, 8, 7, 11, 13] },\n ]\n\n const columns: GridColumns<Person> = [\n { field: 'name', header: 'Name', width: 190 },\n { field: 'city', header: 'City', width: 150 },\n { field: 'salary', header: 'Salary', width: 150, editorType: 'number',\n format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n\n<SvGrid data={people} {columns} filterable filterMode=\"menu\" showFilterRow />\n```\n\n## Your own control in the row\n\nA column's `customFilter.floating` snippet takes the place of the operator\nbutton and input in that column's cell; the other columns keep theirs. See\n[Custom column filters](./custom-column-filters.md).\n\n## See also\n\n- [Overview](./overview.md)\n- [Filter conditions](./filter-conditions.md) - two conditions per column\n- [Custom header components](../columns/custom-header-components.md)\n"
|
|
5522
5522
|
},
|
|
5523
5523
|
{
|
|
5524
5524
|
"slug": "help/filtering/number-filter",
|
|
@@ -5672,7 +5672,7 @@ export const docs = [
|
|
|
5672
5672
|
"path": "docs/help/migrating-from-ag-grid.md",
|
|
5673
5673
|
"title": "Migrating from AG Grid to SvGrid",
|
|
5674
5674
|
"section": "Help",
|
|
5675
|
-
"markdown": "# Migrating from AG Grid to SvGrid\n\nIf you tried AG Grid on a Svelte 5 project - via `ag-grid-svelte`, the\nold `ag-grid-community/svelte`, or a hand-rolled wrapper - you probably\nhit the same friction everyone hits: the bridge between AG Grid's\nReact/Angular-first API and Svelte 5 runes is brittle, the bundle is\nheavy, and the Enterprise pricing only makes sense at scale.\n\n<!-- facts:start ag-grid -->\n> **Facts, checked 12 Sep 2026.** `ag-grid-community` 36.1.0, MIT, last published 5 Aug 2026, 12,400,000 npm downloads in the 30 days to 10 Sep 2026. `@svgrid/grid` 3.0.3, MIT, last published 11 Sep 2026, 16,900 npm downloads in the same window. Bundle, minified and gzipped, each package built alone with Svelte external: SvGrid 3.0.4 97.6 KB JS + 10.5 KB CSS (measured 22 Sep 2026); `ag-grid-community` 36.1.0 317.5 KB JS, no separate stylesheet (measured 12 Sep 2026). AG Grid pricing, as its site states it: AG Grid Community is free under MIT. AG Grid Enterprise is listed at $999 USD per developer with one year of updates and Zendesk support; the Enterprise Bundle with AG Charts Enterprise is $1,498 USD per developer (https://www.ag-grid.com/license-pricing/, read 12 Sep 2026). SvGrid: MIT core; @svgrid/enterprise from $299 per developer per year (Indie, under $1M revenue), $599 for Enterprise. Side by side, with sources: [SvGrid vs AG Grid (community + enterprise)](https://svgrid.com/compare/ag-grid/).\n<!-- facts:end -->\n\nThis page is a 30-minute migration recipe from AG Grid to SvGrid. It\ncovers what maps 1:1, what's different by design, and what you'll lose.\nWe tell you when **not** to switch at the bottom.\n\n## TL;DR\n\n| | AG Grid Community | AG Grid Enterprise | SvGrid Community | @svgrid/enterprise |\n| --- | --- | --- | --- | --- |\n| **Licence** | MIT | Commercial, per developer (the price is in the facts box above) | **MIT** | $599/dev/yr (grid) or $999/dev/yr (grid + spreadsheet + Studio) |\n| **Svelte 5 native** | No (community-built integration) | No (community-built integration) | Yes | Yes |\n| **Bundle (gzipped)** | measured, in the facts box above | not measured (licence) | measured, in the facts box above | lazy-loaded subpaths |\n| **Sorting / filtering** | Yes | Yes | Yes | (in Community) |\n| **Row grouping + aggregation** | No (Enterprise) | Yes | Yes (free) | (in Community) |\n| **Master/detail, tree, range select** | No (Enterprise) | Yes | Yes (free) | (in Community) |\n| **Set filter / Excel-style filter menu** | No (Enterprise) | Yes | Yes (free) | (in Community) |\n| **Server-side data, flat (infinite row model)** | Yes | Yes | Yes (free) | (in Community) |\n| **Server-side row model (grouping, tree, pivot, transactions, selection)** | No (Enterprise) | Yes | No | Yes |\n| **Integrated charts** | No (Enterprise) | Yes, AG Charts | Yes (free) | (in Community) |\n| **CSV export** | Yes (API) | Yes | Yes, with TSV and JSON | (in Community) |\n| **Excel export** | No | Yes | No | Yes |\n| **PDF / styled-HTML export, print** | No | No | No | Yes |\n| **Pivot tables** | No | Yes | No | Yes |\n| **MCP server for AI assistants** | Yes, `ag-mcp` | Yes | Yes, `@svgrid/mcp` | (in Community) |\n| **In-grid AI helpers** | No | No | Yes (bring your own model) | (in Community) |\n\n**SvGrid Community gives you most of what AG Grid sells as Enterprise for\nfree**, and `@svgrid/enterprise` adds Excel, PDF and print output, import,\npivot tables and the server-side row model per developer per year. Compare what each paid tier adds\nbefore comparing prices; both are in the facts box. The trade-offs are\nSvelte-only and a much smaller ecosystem. Every AG Grid cell above follows\nag-grid.com's own pages; the dated list is on the\n[SvGrid vs AG Grid](https://svgrid.com/compare/ag-grid/) page.\n\n## Mental model - what changes\n\nAG Grid is one big object you configure declaratively. SvGrid is a\n**headless engine** (`createSvGrid`) with an optional **render\ncomponent** (`<SvGrid>`) on top - the same split TanStack Table made\npopular. You can use either layer; most projects use the render\ncomponent.\n\nEvery example below runs against this setup:\n\n```svelte {preamble}\n<script lang=\"ts\">\n import { SvGrid, tableFeatures, rowSortingFeature, columnFilteringFeature, type ColumnDef } from '@svgrid/grid'\n\n type Row = { id: number; make: string; model: string; price: number }\n\n const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })\n\n let rows = $state<Row[]>([\n { id: 1, make: 'Toyota', model: 'Celica', price: 35000 },\n { id: 2, make: 'Ford', model: 'Mondeo', price: 32000 },\n { id: 3, make: 'Porsche', model: 'Boxster', price: 72000 },\n ])\n const data = rows\n\n const columns: ColumnDef<typeof features, Row>[] = [\n { field: 'make', header: 'Make' },\n { field: 'model', header: 'Model' },\n { field: 'price', header: 'Price', format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n```\n\n```svelte\n<!-- AG Grid (via a Svelte wrapper) -->\n<AgGridSvelte\n gridOptions={{\n rowData: rows,\n columnDefs: columns,\n onGridReady: (params) => (gridApi = params.api),\n }}\n/>\n\n<!-- SvGrid -->\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n onApiReady={(api) => (gridApi = api)}\n/>\n```\n\nThree things to note:\n\n1. **No giant `gridOptions` blob.** Each capability is a top-level prop.\n2. **Features are opt-in.** You pass a `features` object built with\n `tableFeatures({...})` - only the features you list ship JS.\n3. **`onApiReady`** gives you a typed `SvGridApi` that is roughly the\n AG Grid `gridApi` equivalent (see the API-mapping table below).\n\n## Column definitions - direct translation\n\nThe shapes are similar enough that you can usually translate by hand\nwithout thinking too hard.\n\n```ts\n// AG Grid\nconst columnDefs: ColDef[] = [\n { field: 'name', headerName: 'Name', sortable: true, filter: true, width: 200 },\n { field: 'price', headerName: 'Price', type: 'numericColumn',\n valueFormatter: ({ value }) => `$${value.toFixed(2)}` },\n { field: 'date', headerName: 'Date',\n valueGetter: ({ data }) => new Date(data.date).toISOString().slice(0, 10) },\n { field: 'status', headerName: 'Status',\n cellRenderer: StatusCellRenderer,\n cellRendererParams: { onChange: handleStatusChange } },\n]\n```\n\n```ts\n// SvGrid\nimport { renderComponent, type ColumnDef } from '@svgrid/grid'\nimport StatusCell from './StatusCell.svelte'\n\nconst columns: ColumnDef<typeof features, Row>[] = [\n { field: 'name', header: 'Name', width: 200 }, // sortable + filterable by default\n { field: 'price', header: 'Price',\n format: { type: 'currency', currency: 'USD' } },\n { field: 'date', header: 'Date',\n format: { type: 'date', pattern: 'y-m-d' } },\n { field: 'status', header: 'Status',\n cell: renderComponent(StatusCell, (ctx) => ({\n value: ctx.getValue(),\n onChange: handleStatusChange,\n })),\n },\n]\n```\n\n### Property mapping\n\n| AG Grid | SvGrid | Notes |\n| --- | --- | --- |\n| `field` | `field` | Same. |\n| `headerName` | `header` | Accepts a string or a snippet/component. |\n| `width` | `width` | Same. |\n| `minWidth` / `maxWidth` | `minWidth` / `maxWidth` | Same. |\n| `sortable: true` | (default) | Sorting is on when `rowSortingFeature` is registered. |\n| `filter: true` | (default) | Filtering is on when `columnFilteringFeature` is registered. |\n| `valueFormatter` | `format: { ... }` | Built-in types: `number`, `currency`, `percent`, `date`. For custom, use `cell`. |\n| `valueGetter` | `fieldFn` | Returns the value for sorting/filtering. |\n| `cellRenderer` + `cellRendererParams` | `cell: renderComponent(C, ctx => props)` | One call, type-checked. |\n| `cellEditor: 'agTextCellEditor'` | `editorType: 'text'` | Built-in: `text`, `number`, `checkbox`, `date`. |\n| `editable: true` | `enableInlineEditing` prop on `<SvGrid>` | Per-grid, not per-column. (Per-column control on the roadmap.) |\n| `pinned: 'left'` / `'right'` | Right-click column menu → Pin | Set programmatically via the api. |\n| `rowGroup: true` | Via `setGroupBy([colId])` | See Grouping below. |\n| `aggFunc: 'sum'` | `aggregate: 'sum'` | Built-in: `sum`, `avg`, `min`, `max`, `count`. |\n\n## Feature registration - the one new thing\n\nAG Grid auto-enables most features; you turn them off. SvGrid is the\nopposite - features are opt-in. The result is a smaller bundle.\n\n```ts\nimport {\n tableFeatures,\n rowSortingFeature,\n columnFilteringFeature,\n columnGroupingFeature,\n rowExpandingFeature,\n rowPaginationFeature,\n rowSelectionFeature,\n} from '@svgrid/grid'\n\nconst features = tableFeatures({\n rowSortingFeature,\n columnFilteringFeature,\n rowSelectionFeature,\n // omit any you don't need - their code won't ship\n})\n```\n\nPass `features` to `<SvGrid>` once. From then on the grid behaves like\nAG Grid's `enableSorting`, `enableFilter`, `rowSelection`, etc. are all\non for the registered features.\n\n## API mapping (`gridApi` → `SvGridApi`)\n\nYou get the SvGrid API from `onApiReady` (equivalent to AG Grid's\n`onGridReady`).\n\n| AG Grid `gridApi.X()` | SvGrid `api.X()` |\n| --- | --- |\n| `setRowData(rows)` | (declarative - just update `data` prop) |\n| `addRow(row)` / `applyTransaction({ add: [row] })` | `api.addRow(row)` / `api.addRows(rows)` |\n| `applyTransaction({ remove: [row] })` | `api.removeRow(rowIndex)` |\n| `getValue(colId, rowNode)` | `api.getCellValue(rowIndex, columnId)` |\n| `setValue(...)` | `api.setCellValue(rowIndex, columnId, value)` |\n| `setColumnVisible(colId, visible)` | `api.setColumnVisible(columnId, visible)` |\n| `getSortModel()` / `setSortModel()` | `api.setSort(columnId, 'asc'\\|'desc'\\|null)` |\n| `setFilterModel({...})` | `api.setFilter(columnId, { operator, value })` |\n| `getDisplayedRowAtIndex(i)` / `forEachNodeAfterFilterAndSort(...)` | `api.getDisplayedRows()` |\n| `getModel()` (raw rows) | `api.getData()` |\n\n## Common patterns\n\n### Sorting + filtering + pagination (the 80% case)\n\n```svelte\n<script lang=\"ts\">\n import {\n SvGrid,\n tableFeatures,\n rowSortingFeature,\n columnFilteringFeature,\n rowPaginationFeature,\n } from '@svgrid/grid'\n\n const features = tableFeatures({\n rowSortingFeature,\n columnFilteringFeature,\n rowPaginationFeature,\n })\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n showPagination\n showColumnFilters\n/>\n```\n\nAG Grid equivalent: `gridOptions: { defaultColDef: { sortable: true, filter: true }, pagination: true }`.\n\n### Cell editing with persistence\n\n```svelte\n<script lang=\"ts\">\n function onCellValueChange(e: { rowIndex: number; columnId: string; value: unknown }) {\n // Persist however you like (fetch to backend, optimistic local update, etc.)\n console.log('cell changed', e)\n }\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n enableInlineEditing\n onCellValueChange={onCellValueChange}\n/>\n```\n\nAG Grid equivalent: `onCellValueChanged: ({ data, colDef, newValue, oldValue }) => ...`.\nSvGrid's event payload is column-id + row-index based rather than node-based; the row data is yours\nto mutate (or not) on the `rows` array you passed in.\n\n### Grouping + aggregation\n\n```svelte\n<script lang=\"ts\">\n import {\n SvGrid,\n tableFeatures,\n columnGroupingFeature,\n rowSortingFeature,\n rowExpandingFeature,\n } from '@svgrid/grid'\n\n const features = tableFeatures({\n rowSortingFeature,\n columnGroupingFeature,\n rowExpandingFeature,\n })\n\n const columns = [\n { field: 'department', header: 'Department' },\n { field: 'team', header: 'Team' },\n { field: 'salary', header: 'Salary', aggregate: 'sum',\n format: { type: 'currency', currency: 'USD' } },\n ]\n\n function setGroup(api) {\n api.setGroupBy(['department', 'team'])\n }\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n showGroupingControls\n onApiReady={setGroup}\n/>\n```\n\nAG Grid Enterprise's `rowGroupPanelShow: 'always'` + `aggFunc: 'sum'` translates 1:1.\n\n### Master / detail\n\nAG Grid Enterprise feature; **free in SvGrid Community**. See\n[demo 08](https://svgrid.com/demos/08-tree-and-master-detail/) for the exact pattern.\n\n### Server-side data\n\nAG Grid's Infinite Row Model (Community) and Server-Side Row Model\n(Enterprise) both take an `IServerSideDatasource` with a `getRows(params)`\nthat answers through `params.success({ rowData, rowCount })`. SvGrid has one\n`ServerDataSource` whose `getRows(request)` returns `{ rows, rowCount }`,\nand two controllers over it: `createServerDataSource` (free: paging or\ninfinite block scroll, sort, filter, CRUD) and `createServerRowModel`\n(Enterprise: lazy grouping, tree, pivot, transactions, selection). Both\nmount through one prop, `<SvGrid rowModel={ctl} />`.\n\nAn existing datasource written for AG Grid keeps working behind one call:\n\n```ts\nimport { adaptCallbackDatasource, createServerRowModel } from '@svgrid/enterprise'\n\nconst ctl = createServerRowModel(adaptCallbackDatasource(myServerSideDatasource), {\n groupBy: ['country'],\n aggregations: [{ col: 'amount', fn: 'sum' }],\n})\n```\n\n`adaptCallbackDatasource` maps the request both ways (`rowGroupCols` ->\n`groupBy`, `valueCols` -> `aggregations`, `pivotCols` -> `pivotBy`, the\nsort and filter models, `context`, `parentNode`) and turns `success` /\n`fail` into a resolved or rejected promise.\n\nMore often the datasource is a thin `fetch` and the real investment is the\nendpoint behind it - a Java or Node service that parses AG Grid's\n`IServerSideGetRowsRequest` JSON. That endpoint stays as it is: post\n`toCallbackRequest(request)` and read the answer back.\n\n```ts\nimport { toCallbackRequest } from '@svgrid/enterprise'\n\nconst source = {\n async getRows(request) {\n const res = await fetch('/api/sales', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(toCallbackRequest(request)),\n })\n const { rowData, rowCount, pivotResultFields } = await res.json()\n return { rows: rowData, rowCount: rowCount ?? -1, pivotResultFields }\n },\n}\n```\n\n`fromCallbackRequest` is the other direction, for a SvelteKit endpoint that\nhas to keep serving an AG Grid client during the switch. A saved selection\nor a bulk endpoint written against `getServerSideSelectionState()` keeps\nits shape the same way: `toCallbackSelectionState(ctl.getSelectionState())`\nproduces `{ selectAll, toggledNodes }` (or the `nodeId` /\n`selectAllChildren` / `toggledNodes` tree), `ctl.setSelectionState` takes\nthat shape as it is when your group node ids are the group keys, and\n`fromCallbackSelectionState(state, { groupKey, isGroup })` maps them when\na `getRowId` gave group rows ids of their own. Option by option:\n\n| AG Grid | SvGrid |\n| --- | --- |\n| `rowModelType: 'infinite'` + `datasource` | `createServerDataSource(source, { mode: 'infinite' })` + `rowModel={ctl}` |\n| `rowModelType: 'serverSide'` + `serverSideDatasource` | `createServerRowModel(source, options)` + `rowModel={ctl}` |\n| `getRows(params)` + `params.success({ rowData, rowCount })` / `params.fail()` | `getRows(request)` returning `{ rows, rowCount }` / throwing |\n| `cacheBlockSize`, `maxBlocksInCache`, `maxConcurrentDatasourceRequests`, `blockLoadDebounceMillis` | `blockSize`, `maxBlocksInCache`, `maxConcurrentRequests`, `blockLoadDebounceMs` |\n| `infiniteInitialRowCount` / `serverSideInitialRowCount` | `initialRowCount` |\n| `rowCount: -1` (unknown) | `rowCount: -1` |\n| `refreshInfiniteCache()` / `purgeInfiniteCache()` | `ctl.refresh()` / `ctl.purge()` |\n| `request.rowGroupCols`, `groupKeys`, `valueCols`, `pivotCols`, `pivotMode` | `request.groupBy`, `groupKeys`, `aggregations`, `pivotBy`, `pivotMode` |\n| `getChildCount(data)` | `childCount: (row) => row.childCount` |\n| `isServerSideGroupOpenByDefault` | `isGroupOpenByDefault(route, row)` |\n| `getServerSideGroupLevelParams` | `levelParams(level, route)` |\n| `isServerSideGroup` / `getServerSideGroupKey` (tree) | `treeData: true` + `hasChildren` / `getRowId` |\n| `grandTotalRow` / `groupTotalRow` | `grandTotalRow` / `groupFooters` |\n| `refreshServerSide({ route, purge })` / `retryServerSideLoads()` | `ctl.refresh({ route, purge })` / `ctl.retryLoads()` |\n| `applyServerSideTransaction` / `applyServerSideTransactionAsync` / `flushServerSideAsyncTransactions` | `ctl.applyTransaction` / `applyTransactionAsync` / `flushAsyncTransactions` |\n| `applyServerSideRowData` | `ctl.applyRowData` |\n| `serverSideSortAllLevels`, `serverSideEnableClientSideSort`, `serverSideOnlyRefreshFilteredGroups` | `sortAllLevels`, `clientSideSort`, `onlyRefreshFilteredGroups` |\n| `getServerSideSelectionState` / `setServerSideSelectionState` | `ctl.getSelectionState()` / `setSelectionState()` (the `toggledNodes` shape is accepted; `toCallbackSelectionState` / `fromCallbackSelectionState` convert) |\n| `ServerSideTransactionResult.status`: `Applied`, `StoreNotFound`, `StoreLoading`, `StoreWaitingToLoad`, `StoreLoadingFailed`, `Cancelled` | `ServerTransactionResult.status`: the same six, lower camel case (`applied`, `storeNotFound`, ...) - a `switch` on the old spelling falls through silently |\n| `groupSelects: 'descendants'` | `selection: { groupSelects: 'descendants' }` |\n| `pivotResultFields` in `success`, `setPivotResultColumns`, `processPivotResultColDef` | `pivotResultFields` / `pivotResultColumns` in the result, `pivotResultColumn(field, def)` |\n| `pagination` + `paginateChildRows` | `pagination: { pageSize, paginateChildRows }` |\n| `getCacheBlockState()` / `getServerSideGroupLevelState()` | `ctl.getCacheState()` / `ctl.levelStates()` |\n| `serverSideDatasource.destroy()` | `source.destroy()` |\n\nTwo differences worth knowing before you start: SvGrid sends the global\nsearch box to the server as `filterModel.global` (AG Grid's quick filter is\nclient-side only and unsupported under its infinite and server-side models),\nand SvGrid has no viewport row model. See\n[Server grouping](./server/server-grouping.md) and\n[demo 467](https://svgrid.com/demos/467-server-row-model-1m/).\n\n### Excel / PDF export\n\nAG Grid: `gridApi.exportDataAsExcel({...})` (Enterprise-only).\nSvGrid: install `@svgrid/enterprise`, call `api.exportData({ format: 'xlsx', ... })`. See [Data export and printing](./export.md).\n\n```ts\nimport { installEnterprise, setLicenseKey } from '@svgrid/enterprise'\nsetLicenseKey('YOUR-LICENSE-KEY') // your Enterprise key\n\n// inside onApiReady:\nconst pro = installEnterprise(api)\nawait pro.exportData({ format: 'xlsx', filename: 'orders' })\n```\n\n## Gotchas - things that don't translate directly\n\n### 1. Per-column `editable: true`\nEditing is switched on at the grid level (`enableInlineEditing`) and\nnarrowed per column with `editable: boolean | (ctx) => boolean` on the\ncolumn definition, so AG Grid's per-column flag and its callback form both\nmap directly. What does not map is AG Grid's `editable` on a column\ngroup: set it on each child column.\n\n### 2. Column drag-to-reorder\nHeader drag is `enableColumnReorder` on `<SvGrid>`; `setColumnOrder`\non the API does the same from code. See\n[column moving](./columns/column-moving.md) for the events.\n\n### 3. AG Grid `valueGetter` chains\nAG Grid's `valueGetter` can read other column values via the API. In\nSvGrid, `fieldFn` only receives the row; if you need cross-column\ncomputed values, do it in the cell renderer with `ctx.row.original` or\ncompute the derived value upstream and store it in the row.\n\n### 4. `cellClass` / `rowClass` callbacks\nOn the roadmap. For now, render a wrapper element in your `cell` snippet\nwith the conditional class.\n\n### 5. The Status Bar / Side Bar / Tool Panels\nAG Grid's chrome (status bar with row count, side bar with filters and\ncolumns panels) doesn't exist in SvGrid - build it as plain Svelte\nmarkup around the grid. Most teams build their own anyway because\nAG Grid's defaults rarely match a polished design system.\n\n### 6. Set filter (the Excel-style funnel popup)\nSvGrid ships an Excel-style filter menu (free in Community). API surface\nis similar but not identical - see [Set filter](./filtering/set-filter.md).\n\n## When NOT to migrate\n\nBe honest. Stay on AG Grid if you:\n\n- **Use multiple frameworks** - AG Grid has React, Angular, Vue, Solid, Qwik, vanilla adapters. SvGrid is Svelte-only.\n- **Need a push-based viewport row model** - SvGrid's server-side row model covers grouping, tree, pivot, transactions and select-all across unloaded rows, and its real-time page merges deltas into loaded rows, but the server cannot push the visible window over a socket the way the viewport model does.\n- **Need pluggable custom filter components or custom tool panels** - SvGrid's tool panel is a fixed Columns + Filters pair.\n- **Are mid-project and shipping in <2 weeks** - the migration is a few hours per grid, but only do it when you have buffer.\n- **Have a Svelte 4 codebase you can't upgrade** - SvGrid requires Svelte 5 runes. (Consider [htmlelements.com](https://www.htmlelements.com) for vanilla / multi-framework.)\n\nIf none of those apply: switching drops a per-developer Enterprise licence\nfor features that are in SvGrid's MIT core, cuts the bundle by the\ndifference in the facts box above, and gives you a Svelte-native API that\nplays well with runes.\n\n## Step-by-step migration\n\nA typical migration of a single grid takes 1-3 hours:\n\n1. **Install** - `pnpm add @svgrid/grid` (and `@svgrid/enterprise` if you need export).\n2. **Translate columnDefs** - use the mapping table above. Most columns are 1:1.\n3. **Wrap features** - figure out which AG Grid features you actually use; register only those in `tableFeatures({...})`.\n4. **Swap the component** - `<AgGridSvelte gridOptions={...}>` → `<SvGrid data={rows} columns={columns} features={features}>`.\n5. **Move event handlers** - AG Grid `onCellValueChanged` → SvGrid `onCellValueChange` (signature differs slightly, see above).\n6. **Move API calls** - AG Grid `gridApi.X()` → SvGrid `api.X()` per the API table.\n7. **Test interactions** - sort, filter, edit, select. Most \"just works.\"\n8. **Remove `ag-grid-*` packages** - `pnpm remove ag-grid-community ag-grid-svelte` etc. Inspect your bundle to confirm the drop; the facts box above has both measured sizes.\n\n## Need help migrating?\n\nEnterprise customers get **migration help included** with the support plan\n(architecture review, port one grid for you as a reference). Email\n`support@jqwidgets.com` after purchase, or `sales@jqwidgets.com` for\npre-sales questions.\n\n## Frequently asked questions\n\n### Is SvGrid a drop-in replacement for AG Grid in Svelte?\n\nNot a literal drop-in - there is no `ag-grid-svelte` shim to swap. But the\nconcepts map closely: column definitions, row models, sorting, filtering,\ngrouping, and an imperative API all have direct SvGrid equivalents, so most\nteams port a grid in 30 minutes to a day. It is a configuration translation,\nnot a rewrite.\n\n### What is the SvGrid equivalent of AG Grid Enterprise?\n\n`@svgrid/enterprise`. It adds Excel/PDF/styled-HTML export (CSV, TSV and JSON export are already free in the grid), a printable view, pivot\ntables and data import. The in-grid AI helpers are free in `@svgrid/grid`.\nIt is licensed per developer ($599 for the grid, $999 with the spreadsheet\nand Studio, both covering unlimited apps), and the\nCommunity package is MIT-licensed and free for commercial use.\n\n### Does SvGrid use Svelte 5 runes, or is it a wrapper?\n\nIt is Svelte-5-native. State is `$state` / `$derived` / `$effect` and cells\nrender through Svelte snippets - there is no React or Angular core underneath\nand no framework bridge to keep in sync.\n\n### Will my AG Grid bundle size shrink?\n\nAlmost always. The facts box at the top of this page has both packages\nmeasured the same way on the same date: the full SvGrid render component\nwith its stylesheet against `createGrid` with every AG Grid Community\nmodule registered. You add `@svgrid/enterprise` features only where you\nimport them, and the headless core on its own is a small fraction of the\nrender component.\n\n## More examples\n\n### AG Grid ↔ sv-grid side-by-side\n\nTwo real grids over the same dataset: the installed AG Grid Community on the left, sv-grid on the right. Same global filter drives both. Source code panels for either side.\n\n<div data-docs-demo=\"139-migration-from-ag-grid\" data-height=\"460\"></div>\n\n## See also\n\n- [Getting started](../getting-started.md) - full SvGrid walkthrough\n- [Why headless?](../why-headless.md) - the headless / render-component split\n- [Data export and printing](./export.md) - the `@svgrid/enterprise` feature pack\n- [Comparison: SvGrid vs AG Grid vs TanStack Table](./comparison.md) - the\n measured benchmark and the feature matrix\n- [SvGrid vs AG Grid comparison page](https://svgrid.com/compare/ag-grid/) -\n every claim with its source and date\n"
|
|
5675
|
+
"markdown": "# Migrating from AG Grid to SvGrid\n\nIf you tried AG Grid on a Svelte 5 project - via `ag-grid-svelte`, the\nold `ag-grid-community/svelte`, or a hand-rolled wrapper - you probably\nhit the same friction everyone hits: the bridge between AG Grid's\nReact/Angular-first API and Svelte 5 runes is brittle, the bundle is\nheavy, and the Enterprise pricing only makes sense at scale.\n\n<!-- facts:start ag-grid -->\n> **Facts, checked 12 Sep 2026.** `ag-grid-community` 36.1.0, MIT, last published 5 Aug 2026, 12,400,000 npm downloads in the 30 days to 10 Sep 2026. `@svgrid/grid` 3.0.3, MIT, last published 11 Sep 2026, 16,900 npm downloads in the same window. Bundle, minified and gzipped, each package built alone with Svelte external: SvGrid 3.0.4 97.6 KB JS + 10.5 KB CSS (measured 22 Sep 2026); `ag-grid-community` 36.1.0 317.5 KB JS, no separate stylesheet (measured 12 Sep 2026). AG Grid pricing, as its site states it: AG Grid Community is free under MIT. AG Grid Enterprise is listed at $999 USD per developer with one year of updates and Zendesk support; the Enterprise Bundle with AG Charts Enterprise is $1,498 USD per developer (https://www.ag-grid.com/license-pricing/, read 12 Sep 2026). SvGrid: MIT core; @svgrid/enterprise from $299 per developer per year (Indie, under $1M revenue), $599 for Enterprise. Side by side, with sources: [SvGrid vs AG Grid (community + enterprise)](https://svgrid.com/compare/ag-grid/).\n<!-- facts:end -->\n\nThis page is a 30-minute migration recipe from AG Grid to SvGrid. It\ncovers what maps 1:1, what's different by design, and what you'll lose.\nWe tell you when **not** to switch at the bottom.\n\n## TL;DR\n\n| | AG Grid Community | AG Grid Enterprise | SvGrid Community | @svgrid/enterprise |\n| --- | --- | --- | --- | --- |\n| **Licence** | MIT | Commercial, per developer (the price is in the facts box above) | **MIT** | $599/dev/yr (grid) or $999/dev/yr (grid + spreadsheet + Studio) |\n| **Svelte 5 native** | No (community-built integration) | No (community-built integration) | Yes | Yes |\n| **Bundle (gzipped)** | measured, in the facts box above | not measured (licence) | measured, in the facts box above | lazy-loaded subpaths |\n| **Sorting / filtering** | Yes | Yes | Yes | (in Community) |\n| **Row grouping + aggregation** | No (Enterprise) | Yes | Yes (free) | (in Community) |\n| **Master/detail, tree, range select** | No (Enterprise) | Yes | Yes (free) | (in Community) |\n| **Set filter / Excel-style filter menu** | No (Enterprise) | Yes | Yes (free) | (in Community) |\n| **Server-side data, flat (infinite row model)** | Yes | Yes | Yes (free) | (in Community) |\n| **Server-side row model (grouping, tree, pivot, transactions, selection)** | No (Enterprise) | Yes | No | Yes |\n| **Integrated charts** | No (Enterprise) | Yes, AG Charts | Yes (free) | (in Community) |\n| **CSV export** | Yes (API) | Yes | Yes, with TSV and JSON | (in Community) |\n| **Excel export** | No | Yes | No | Yes |\n| **PDF / styled-HTML export, print** | No | No | No | Yes |\n| **Pivot tables** | No | Yes | No | Yes |\n| **MCP server for AI assistants** | Yes, `ag-mcp` | Yes | Yes, `@svgrid/mcp` | (in Community) |\n| **In-grid AI helpers** | No | No | Yes (bring your own model) | (in Community) |\n\n**SvGrid Community gives you most of what AG Grid sells as Enterprise for\nfree**, and `@svgrid/enterprise` adds Excel, PDF and print output, import,\npivot tables and the server-side row model per developer per year. Compare what each paid tier adds\nbefore comparing prices; both are in the facts box. The trade-offs are\nSvelte-only and a much smaller ecosystem. Every AG Grid cell above follows\nag-grid.com's own pages; the dated list is on the\n[SvGrid vs AG Grid](https://svgrid.com/compare/ag-grid/) page.\n\n## Mental model - what changes\n\nAG Grid is one big object you configure declaratively. SvGrid is a\n**headless engine** (`createSvGrid`) with an optional **render\ncomponent** (`<SvGrid>`) on top - the same split TanStack Table made\npopular. You can use either layer; most projects use the render\ncomponent.\n\nEvery example below runs against this setup:\n\n```svelte {preamble}\n<script lang=\"ts\">\n import { SvGrid, tableFeatures, rowSortingFeature, columnFilteringFeature, type ColumnDef } from '@svgrid/grid'\n\n type Row = { id: number; make: string; model: string; price: number }\n\n const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })\n\n let rows = $state<Row[]>([\n { id: 1, make: 'Toyota', model: 'Celica', price: 35000 },\n { id: 2, make: 'Ford', model: 'Mondeo', price: 32000 },\n { id: 3, make: 'Porsche', model: 'Boxster', price: 72000 },\n ])\n const data = rows\n\n const columns: ColumnDef<typeof features, Row>[] = [\n { field: 'make', header: 'Make' },\n { field: 'model', header: 'Model' },\n { field: 'price', header: 'Price', format: { type: 'currency', currency: 'USD' } },\n ]\n</script>\n```\n\n```svelte\n<!-- AG Grid (via a Svelte wrapper) -->\n<AgGridSvelte\n gridOptions={{\n rowData: rows,\n columnDefs: columns,\n onGridReady: (params) => (gridApi = params.api),\n }}\n/>\n\n<!-- SvGrid -->\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n onApiReady={(api) => (gridApi = api)}\n/>\n```\n\nThree things to note:\n\n1. **No giant `gridOptions` blob.** Each capability is a top-level prop.\n2. **Features are opt-in.** You pass a `features` object built with\n `tableFeatures({...})` - only the features you list ship JS.\n3. **`onApiReady`** gives you a typed `SvGridApi` that is roughly the\n AG Grid `gridApi` equivalent (see the API-mapping table below).\n\n## Column definitions - direct translation\n\nThe shapes are similar enough that you can usually translate by hand\nwithout thinking too hard.\n\n```ts\n// AG Grid\nconst columnDefs: ColDef[] = [\n { field: 'name', headerName: 'Name', sortable: true, filter: true, width: 200 },\n { field: 'price', headerName: 'Price', type: 'numericColumn',\n valueFormatter: ({ value }) => `$${value.toFixed(2)}` },\n { field: 'date', headerName: 'Date',\n valueGetter: ({ data }) => new Date(data.date).toISOString().slice(0, 10) },\n { field: 'status', headerName: 'Status',\n cellRenderer: StatusCellRenderer,\n cellRendererParams: { onChange: handleStatusChange } },\n]\n```\n\n```ts\n// SvGrid\nimport { renderComponent, type ColumnDef } from '@svgrid/grid'\nimport StatusCell from './StatusCell.svelte'\n\nconst columns: ColumnDef<typeof features, Row>[] = [\n { field: 'name', header: 'Name', width: 200 }, // sortable + filterable by default\n { field: 'price', header: 'Price',\n format: { type: 'currency', currency: 'USD' } },\n { field: 'date', header: 'Date',\n format: { type: 'date', pattern: 'y-m-d' } },\n { field: 'status', header: 'Status',\n cell: renderComponent(StatusCell, (ctx) => ({\n value: ctx.getValue(),\n onChange: handleStatusChange,\n })),\n },\n]\n```\n\n### Property mapping\n\n| AG Grid | SvGrid | Notes |\n| --- | --- | --- |\n| `field` | `field` | Same. |\n| `headerName` | `header` | Accepts a string or a snippet/component. |\n| `width` | `width` | Same. |\n| `minWidth` / `maxWidth` | `minWidth` / `maxWidth` | Same. |\n| `sortable: true` | (default) | Sorting is on when `rowSortingFeature` is registered. |\n| `filter: true` | (default) | Filtering is on when `columnFilteringFeature` is registered. |\n| `valueFormatter` | `format: { ... }` | Built-in types: `number`, `currency`, `percent`, `date`. For custom, use `cell`. |\n| `valueGetter` | `fieldFn` | Returns the value for sorting/filtering. |\n| `cellRenderer` + `cellRendererParams` | `cell: renderComponent(C, ctx => props)` | One call, type-checked. |\n| `cellEditor: 'agTextCellEditor'` | `editorType: 'text'` | Built-in: `text`, `number`, `checkbox`, `date`. |\n| `editable: true` | `enableInlineEditing` prop on `<SvGrid>` | Per-grid, not per-column. (Per-column control on the roadmap.) |\n| `pinned: 'left'` / `'right'` | Right-click column menu → Pin | Set programmatically via the api. |\n| `rowGroup: true` | Via `setGroupBy([colId])` | See Grouping below. |\n| `aggFunc: 'sum'` | `aggregate: 'sum'` | Built-in: `sum`, `avg`, `min`, `max`, `count`. |\n\n## Feature registration - the one new thing\n\nAG Grid auto-enables most features; you turn them off. SvGrid is the\nopposite - features are opt-in. The result is a smaller bundle.\n\n```ts\nimport {\n tableFeatures,\n rowSortingFeature,\n columnFilteringFeature,\n columnGroupingFeature,\n rowExpandingFeature,\n rowPaginationFeature,\n rowSelectionFeature,\n} from '@svgrid/grid'\n\nconst features = tableFeatures({\n rowSortingFeature,\n columnFilteringFeature,\n rowSelectionFeature,\n // omit any you don't need - their code won't ship\n})\n```\n\nPass `features` to `<SvGrid>` once. From then on the grid behaves like\nAG Grid's `enableSorting`, `enableFilter`, `rowSelection`, etc. are all\non for the registered features.\n\n## API mapping (`gridApi` → `SvGridApi`)\n\nYou get the SvGrid API from `onApiReady` (equivalent to AG Grid's\n`onGridReady`).\n\n| AG Grid `gridApi.X()` | SvGrid `api.X()` |\n| --- | --- |\n| `setRowData(rows)` | (declarative - just update `data` prop) |\n| `addRow(row)` / `applyTransaction({ add: [row] })` | `api.addRow(row)` / `api.addRows(rows)` |\n| `applyTransaction({ remove: [row] })` | `api.removeRow(rowIndex)` |\n| `getValue(colId, rowNode)` | `api.getCellValue(rowIndex, columnId)` |\n| `setValue(...)` | `api.setCellValue(rowIndex, columnId, value)` |\n| `setColumnVisible(colId, visible)` | `api.setColumnVisible(columnId, visible)` |\n| `getSortModel()` / `setSortModel()` | `api.setSort(columnId, 'asc'\\|'desc'\\|null)` |\n| `setFilterModel({...})` | `api.setFilter(columnId, { operator, value })` |\n| `getDisplayedRowAtIndex(i)` / `forEachNodeAfterFilterAndSort(...)` | `api.getDisplayedRows()` |\n| `getModel()` (raw rows) | `api.getData()` |\n\n## Common patterns\n\n### Sorting + filtering + pagination (the 80% case)\n\n```svelte\n<script lang=\"ts\">\n import {\n SvGrid,\n tableFeatures,\n rowSortingFeature,\n columnFilteringFeature,\n rowPaginationFeature,\n } from '@svgrid/grid'\n\n const features = tableFeatures({\n rowSortingFeature,\n columnFilteringFeature,\n rowPaginationFeature,\n })\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n showPagination\n showColumnFilters\n/>\n```\n\nAG Grid equivalent: `gridOptions: { defaultColDef: { sortable: true, filter: true }, pagination: true }`.\n\n### Cell editing with persistence\n\n```svelte\n<script lang=\"ts\">\n function onCellValueChange(e: { rowIndex: number; columnId: string; value: unknown }) {\n // Persist however you like (fetch to backend, optimistic local update, etc.)\n console.log('cell changed', e)\n }\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n enableInlineEditing\n onCellValueChange={onCellValueChange}\n/>\n```\n\nAG Grid equivalent: `onCellValueChanged: ({ data, colDef, newValue, oldValue }) => ...`.\nSvGrid's event payload is column-id + row-index based rather than node-based; the row data is yours\nto mutate (or not) on the `rows` array you passed in.\n\n### Grouping + aggregation\n\n```svelte\n<script lang=\"ts\">\n import {\n SvGrid,\n tableFeatures,\n columnGroupingFeature,\n rowSortingFeature,\n rowExpandingFeature,\n } from '@svgrid/grid'\n\n const features = tableFeatures({\n rowSortingFeature,\n columnGroupingFeature,\n rowExpandingFeature,\n })\n\n const columns = [\n { field: 'department', header: 'Department' },\n { field: 'team', header: 'Team' },\n { field: 'salary', header: 'Salary', aggregate: 'sum',\n format: { type: 'currency', currency: 'USD' } },\n ]\n\n function setGroup(api) {\n api.setGroupBy(['department', 'team'])\n }\n</script>\n\n<SvGrid\n data={rows}\n columns={columns}\n features={features}\n showGroupingControls\n onApiReady={setGroup}\n/>\n```\n\nAG Grid Enterprise's `rowGroupPanelShow: 'always'` + `aggFunc: 'sum'` translates 1:1.\n\n### Master / detail\n\nAG Grid Enterprise feature; **free in SvGrid Community**. See\n[demo 08](https://svgrid.com/demos/08-tree-and-master-detail/) for the exact pattern.\n\n### Server-side data\n\nAG Grid's Infinite Row Model (Community) and Server-Side Row Model\n(Enterprise) both take an `IServerSideDatasource` with a `getRows(params)`\nthat answers through `params.success({ rowData, rowCount })`. SvGrid has one\n`ServerDataSource` whose `getRows(request)` returns `{ rows, rowCount }`,\nand two controllers over it: `createServerDataSource` (free: paging or\ninfinite block scroll, sort, filter, CRUD) and `createServerRowModel`\n(Enterprise: lazy grouping, tree, pivot, transactions, selection). Both\nmount through one prop, `<SvGrid rowModel={ctl} />`.\n\nAn existing datasource written for AG Grid keeps working behind one call:\n\n```ts\nimport { adaptCallbackDatasource, createServerRowModel } from '@svgrid/enterprise'\n\nconst ctl = createServerRowModel(adaptCallbackDatasource(myServerSideDatasource), {\n groupBy: ['country'],\n aggregations: [{ col: 'amount', fn: 'sum' }],\n})\n```\n\n`adaptCallbackDatasource` maps the request both ways (`rowGroupCols` ->\n`groupBy`, `valueCols` -> `aggregations`, `pivotCols` -> `pivotBy`, the\nsort and filter models, `context`, `parentNode`) and turns `success` /\n`fail` into a resolved or rejected promise.\n\nMore often the datasource is a thin `fetch` and the real investment is the\nendpoint behind it - a Java or Node service that parses AG Grid's\n`IServerSideGetRowsRequest` JSON. That endpoint stays as it is: post\n`toCallbackRequest(request)` and read the answer back.\n\n```ts\nimport { toCallbackRequest } from '@svgrid/enterprise'\n\nconst source = {\n async getRows(request) {\n const res = await fetch('/api/sales', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(toCallbackRequest(request)),\n })\n const { rowData, rowCount, pivotResultFields } = await res.json()\n return { rows: rowData, rowCount: rowCount ?? -1, pivotResultFields }\n },\n}\n```\n\n`fromCallbackRequest` is the other direction, for a SvelteKit endpoint that\nhas to keep serving an AG Grid client during the switch. A saved selection\nor a bulk endpoint written against `getServerSideSelectionState()` keeps\nits shape the same way: `toCallbackSelectionState(ctl.getSelectionState())`\nproduces `{ selectAll, toggledNodes }` (or the `nodeId` /\n`selectAllChildren` / `toggledNodes` tree), `ctl.setSelectionState` takes\nthat shape as it is when your group node ids are the group keys, and\n`fromCallbackSelectionState(state, { groupKey, isGroup })` maps them when\na `getRowId` gave group rows ids of their own. Option by option:\n\n| AG Grid | SvGrid |\n| --- | --- |\n| `rowModelType: 'infinite'` + `datasource` | `createServerDataSource(source, { mode: 'infinite' })` + `rowModel={ctl}` |\n| `rowModelType: 'serverSide'` + `serverSideDatasource` | `createServerRowModel(source, options)` + `rowModel={ctl}` |\n| `getRows(params)` + `params.success({ rowData, rowCount })` / `params.fail()` | `getRows(request)` returning `{ rows, rowCount }` / throwing |\n| `cacheBlockSize`, `maxBlocksInCache`, `maxConcurrentDatasourceRequests`, `blockLoadDebounceMillis` | `blockSize`, `maxBlocksInCache`, `maxConcurrentRequests`, `blockLoadDebounceMs` |\n| `infiniteInitialRowCount` / `serverSideInitialRowCount` | `initialRowCount` |\n| `rowCount: -1` (unknown) | `rowCount: -1` |\n| `refreshInfiniteCache()` / `purgeInfiniteCache()` | `ctl.refresh()` / `ctl.purge()` |\n| `request.rowGroupCols`, `groupKeys`, `valueCols`, `pivotCols`, `pivotMode` | `request.groupBy`, `groupKeys`, `aggregations`, `pivotBy`, `pivotMode` |\n| `getChildCount(data)` | `childCount: (row) => row.childCount` |\n| `isServerSideGroupOpenByDefault` | `isGroupOpenByDefault(route, row)` |\n| `getServerSideGroupLevelParams` | `levelParams(level, route)` |\n| `isServerSideGroup` / `getServerSideGroupKey` (tree) | `treeData: true` + `hasChildren` / `getRowId` |\n| `grandTotalRow` / `groupTotalRow` | `grandTotalRow` / `groupFooters` |\n| `refreshServerSide({ route, purge })` / `retryServerSideLoads()` | `ctl.refresh({ route, purge })` / `ctl.retryLoads()` |\n| `applyServerSideTransaction` / `applyServerSideTransactionAsync` / `flushServerSideAsyncTransactions` | `ctl.applyTransaction` / `applyTransactionAsync` / `flushAsyncTransactions` |\n| `applyServerSideRowData` | `ctl.applyRowData` |\n| `serverSideSortAllLevels`, `serverSideEnableClientSideSort`, `serverSideOnlyRefreshFilteredGroups` | `sortAllLevels`, `clientSideSort`, `onlyRefreshFilteredGroups` |\n| `getServerSideSelectionState` / `setServerSideSelectionState` | `ctl.getSelectionState()` / `setSelectionState()` (the `toggledNodes` shape is accepted; `toCallbackSelectionState` / `fromCallbackSelectionState` convert) |\n| `ServerSideTransactionResult.status`: `Applied`, `StoreNotFound`, `StoreLoading`, `StoreWaitingToLoad`, `StoreLoadingFailed`, `Cancelled` | `ServerTransactionResult.status`: the same six, lower camel case (`applied`, `storeNotFound`, ...) - a `switch` on the old spelling falls through silently |\n| `groupSelects: 'descendants'` | `selection: { groupSelects: 'descendants' }` |\n| `pivotResultFields` in `success`, `setPivotResultColumns`, `processPivotResultColDef` | `pivotResultFields` / `pivotResultColumns` in the result, `pivotResultColumn(field, def)` |\n| `pagination` + `paginateChildRows` | `pagination: { pageSize, paginateChildRows }` |\n| `getCacheBlockState()` / `getServerSideGroupLevelState()` | `ctl.getCacheState()` / `ctl.levelStates()` |\n| `serverSideDatasource.destroy()` | `source.destroy()` |\n\nTwo differences worth knowing before you start: SvGrid sends the global\nsearch box to the server as `filterModel.global` (AG Grid's quick filter is\nclient-side only and unsupported under its infinite and server-side models),\nand SvGrid has no viewport row model. See\n[Server grouping](./server/server-grouping.md) and\n[demo 467](https://svgrid.com/demos/467-server-row-model-1m/).\n\n### Excel / PDF export\n\nAG Grid: `gridApi.exportDataAsExcel({...})` (Enterprise-only).\nSvGrid: install `@svgrid/enterprise`, call `api.exportData({ format: 'xlsx', ... })`. See [Data export and printing](./export.md).\n\n```ts\nimport { installEnterprise, setLicenseKey } from '@svgrid/enterprise'\nsetLicenseKey('YOUR-LICENSE-KEY') // your Enterprise key\n\n// inside onApiReady:\nconst pro = installEnterprise(api)\nawait pro.exportData({ format: 'xlsx', filename: 'orders' })\n```\n\n## Gotchas - things that don't translate directly\n\n### 1. Per-column `editable: true`\nEditing is switched on at the grid level (`enableInlineEditing`) and\nnarrowed per column with `editable: boolean | (ctx) => boolean` on the\ncolumn definition, so AG Grid's per-column flag and its callback form both\nmap directly. What does not map is AG Grid's `editable` on a column\ngroup: set it on each child column.\n\n### 2. Column drag-to-reorder\nHeader drag is `enableColumnReorder` on `<SvGrid>`; `setColumnOrder`\non the API does the same from code. See\n[column moving](./columns/column-moving.md) for the events.\n\n### 3. AG Grid `valueGetter` chains\nAG Grid's `valueGetter` can read other column values via the API. In\nSvGrid, `fieldFn` only receives the row; if you need cross-column\ncomputed values, do it in the cell renderer with `ctx.row.original` or\ncompute the derived value upstream and store it in the row.\n\n### 4. `cellClass` / `rowClass` callbacks\nOn the roadmap. For now, render a wrapper element in your `cell` snippet\nwith the conditional class.\n\n### 5. The Status Bar / Side Bar / Tool Panels\nAG Grid's chrome (status bar with row count, side bar with filters and\ncolumns panels) doesn't exist in SvGrid - build it as plain Svelte\nmarkup around the grid. Most teams build their own anyway because\nAG Grid's defaults rarely match a polished design system.\n\n### 6. Set filter (the Excel-style funnel popup)\nSvGrid ships an Excel-style filter menu (free in Community). API surface\nis similar but not identical - see [Set filter](./filtering/set-filter.md).\n\n## When NOT to migrate\n\nBe honest. Stay on AG Grid if you:\n\n- **Use multiple frameworks** - AG Grid has React, Angular, Vue, Solid, Qwik, vanilla adapters. SvGrid is Svelte-only.\n- **Need a push-based viewport row model** - SvGrid's server-side row model covers grouping, tree, pivot, transactions and select-all across unloaded rows, and its real-time page merges deltas into loaded rows, but the server cannot push the visible window over a socket the way the viewport model does.\n- **Need custom tool panels** - SvGrid's tool panel is a fixed Columns + Filters pair. (Custom filter components are covered: a column's `customFilter` renders your own control in the filter menu, the floating filter row and the Filters tab.)\n- **Are mid-project and shipping in <2 weeks** - the migration is a few hours per grid, but only do it when you have buffer.\n- **Have a Svelte 4 codebase you can't upgrade** - SvGrid requires Svelte 5 runes. (Consider [htmlelements.com](https://www.htmlelements.com) for vanilla / multi-framework.)\n\nIf none of those apply: switching drops a per-developer Enterprise licence\nfor features that are in SvGrid's MIT core, cuts the bundle by the\ndifference in the facts box above, and gives you a Svelte-native API that\nplays well with runes.\n\n## Step-by-step migration\n\nA typical migration of a single grid takes 1-3 hours:\n\n1. **Install** - `pnpm add @svgrid/grid` (and `@svgrid/enterprise` if you need export).\n2. **Translate columnDefs** - use the mapping table above. Most columns are 1:1.\n3. **Wrap features** - figure out which AG Grid features you actually use; register only those in `tableFeatures({...})`.\n4. **Swap the component** - `<AgGridSvelte gridOptions={...}>` → `<SvGrid data={rows} columns={columns} features={features}>`.\n5. **Move event handlers** - AG Grid `onCellValueChanged` → SvGrid `onCellValueChange` (signature differs slightly, see above).\n6. **Move API calls** - AG Grid `gridApi.X()` → SvGrid `api.X()` per the API table.\n7. **Test interactions** - sort, filter, edit, select. Most \"just works.\"\n8. **Remove `ag-grid-*` packages** - `pnpm remove ag-grid-community ag-grid-svelte` etc. Inspect your bundle to confirm the drop; the facts box above has both measured sizes.\n\n## Need help migrating?\n\nEnterprise customers get **migration help included** with the support plan\n(architecture review, port one grid for you as a reference). Email\n`support@jqwidgets.com` after purchase, or `sales@jqwidgets.com` for\npre-sales questions.\n\n## Frequently asked questions\n\n### Is SvGrid a drop-in replacement for AG Grid in Svelte?\n\nNot a literal drop-in - there is no `ag-grid-svelte` shim to swap. But the\nconcepts map closely: column definitions, row models, sorting, filtering,\ngrouping, and an imperative API all have direct SvGrid equivalents, so most\nteams port a grid in 30 minutes to a day. It is a configuration translation,\nnot a rewrite.\n\n### What is the SvGrid equivalent of AG Grid Enterprise?\n\n`@svgrid/enterprise`. It adds Excel/PDF/styled-HTML export (CSV, TSV and JSON export are already free in the grid), a printable view, pivot\ntables and data import. The in-grid AI helpers are free in `@svgrid/grid`.\nIt is licensed per developer ($599 for the grid, $999 with the spreadsheet\nand Studio, both covering unlimited apps), and the\nCommunity package is MIT-licensed and free for commercial use.\n\n### Does SvGrid use Svelte 5 runes, or is it a wrapper?\n\nIt is Svelte-5-native. State is `$state` / `$derived` / `$effect` and cells\nrender through Svelte snippets - there is no React or Angular core underneath\nand no framework bridge to keep in sync.\n\n### Will my AG Grid bundle size shrink?\n\nAlmost always. The facts box at the top of this page has both packages\nmeasured the same way on the same date: the full SvGrid render component\nwith its stylesheet against `createGrid` with every AG Grid Community\nmodule registered. You add `@svgrid/enterprise` features only where you\nimport them, and the headless core on its own is a small fraction of the\nrender component.\n\n## More examples\n\n### AG Grid ↔ sv-grid side-by-side\n\nTwo real grids over the same dataset: the installed AG Grid Community on the left, sv-grid on the right. Same global filter drives both. Source code panels for either side.\n\n<div data-docs-demo=\"139-migration-from-ag-grid\" data-height=\"460\"></div>\n\n## See also\n\n- [Getting started](../getting-started.md) - full SvGrid walkthrough\n- [Why headless?](../why-headless.md) - the headless / render-component split\n- [Data export and printing](./export.md) - the `@svgrid/enterprise` feature pack\n- [Comparison: SvGrid vs AG Grid vs TanStack Table](./comparison.md) - the\n measured benchmark and the feature matrix\n- [SvGrid vs AG Grid comparison page](https://svgrid.com/compare/ag-grid/) -\n every claim with its source and date\n"
|
|
5676
5676
|
},
|
|
5677
5677
|
{
|
|
5678
5678
|
"slug": "help/migrating-from-airtable",
|
|
@@ -5826,7 +5826,7 @@ export const docs = [
|
|
|
5826
5826
|
"path": "docs/help/missing-features.md",
|
|
5827
5827
|
"title": "Missing features",
|
|
5828
5828
|
"section": "Help",
|
|
5829
|
-
"markdown": "# Missing features\n\nAn honest accounting of what is **not yet built**, audited against the shipped\ndemo catalog. Most of what used to live here has shipped; the remaining gaps\nare small and clearly marked. Each entry has a rough effort estimate (S / M / L).\n\nShipped items are struck through with the demo or API that covers them, so you\ncan see both the trajectory and the (short) list of real gaps.\n\n## Columns\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~`getRowId` prop~~ | **shipped** | ✓ |\n| ~~`cellClass(ctx)` / `rowClass(ctx)` callbacks~~ | **shipped** | ✓ |\n| ~~`getColumnWidths()` / `setColumnWidth()`~~ | **shipped** | ✓ |\n| ~~`setColumnPinning()` / `getColumnPinning()`~~ | **shipped** | ✓ |\n| ~~Header drag-to-reorder~~ | **shipped** - `enableColumnReorder`; demo `109-column-reorder-engine` | ✓ |\n| ~~Per-column disable sort / filter~~ | **shipped** - `sortable` / `filterable` on `ColumnDef` | ✓ |\n| ~~Column spanning~~ | **shipped** - cell merging via `MergeSpec` + `spreadsheetLayout` (demo `170`), **plus** declarative value-driven `colSpan` / `rowSpan` via `spansToMerges` | ✓ |\n\n## Rows\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~Row pinning (top / bottom)~~ | **shipped** - `pinnedTopRows` / `pinnedBottomRows`; demos `107-pinned-rows`, `108-pinned-rows-engine` | ✓ |\n| ~~Row spanning (merged cells across rows)~~ | **shipped as cell merging** - `rowspan` in `MergeSpec`; demo `170-cell-merging` | ✓ |\n| ~~Full-width / detail row API~~ | **shipped** - `isDetailRow`; demo `106-detail-rows` | ✓ |\n| ~~Variable row height with `<SvGrid>`~~ | **shipped** - `rowHeight` accepts `(rowIndex) => px` | ✓ |\n| ~~Auto row height (measure content)~~ | **shipped** - `autoRowHeight` wraps cell text and measures each row, virtualization included | ✓ |\n| ~~`api.getDisplayedRows()`~~ | **shipped** | ✓ |\n| ~~Client-side tree data (hierarchical rows)~~ | **shipped** - `treeData` nests by parent id, `flattenTreeData` converts nested children; treegrid role + arrow-key expand; demo `426-tree-data` | ✓ |\n| ~~Built-in row dragging~~ | **shipped** - `rowDragManaged` reorders in-grid and moves rows **grid-to-grid** via a shared `rowDragGroup`; `onRowDragEnd` on the receiver; demos `105-row-reorder` (custom) + `180-row-dragging` (managed) | ✓ |\n\n## Cells\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~Built-in tooltip API on `ColumnDef`~~ | **shipped** - `tooltip`; demo `85-tooltips-and-notes` | ✓ |\n| ~~Formula language~~ | **shipped** - a real module in `@svgrid/enterprise/sheet`: parser, evaluator, the common functions plus financial, math, statistics, text and date packs (the [formulas page](./spreadsheet-formulas.md) lists them), cross-sheet and whole-column refs, short-circuiting `IF` / `IFERROR`, a dependency graph for incremental recalc, and `translateFormula` / `fixupReferences`. Absolute refs are now absolute (the demo copies stripped `$`, so anything filled or copied was quietly wrong) | ✓ |\n| ~~Formula editor (bar, autocomplete, Name Box)~~ | **shipped** - `<SvFormulaBar>` shows the raw text behind the active cell, autocompletes function names closest-match-first with signature hints, and its Name Box jumps to an address or a defined name; `createNames` defines them. Demo `453` | ✓ |\n| ~~Per-cell number formats and styles~~ | **shipped** - an Excel format-string compiler (`compileNumberFormat`) plus a store keyed on row id so formatting survives a sort, wired to Ctrl+1, Ctrl+Shift+1..6 and Ctrl+B/I/U. Demo `453` | ✓ |\n| ~~AutoSum~~ | **shipped** - Alt+= over the run Excel would guess | ✓ |\n| ~~Reference highlighting~~ | **shipped** - the sheet shell outlines every cell a formula in progress refers to, each in its own colour, whether the formula is typed in the cell or in the bar | ✓ |\n| ~~Find-in-grid~~ | **shipped** - Ctrl+F marks every match (the current one more strongly) and matches what a cell shows as well as its value; demo `87-find-in-grid` | ✓ |\n| ~~Excel keyboard shortcuts~~ | **shipped** - `enableSheet()` binds Ctrl+Arrow (a run-boundary search, so it hops gaps), Ctrl+Shift+Arrow, Ctrl+A region-then-sheet, Ctrl+Space / Shift+Space, Ctrl+D / Ctrl+R, Ctrl+; and Ctrl+'; demo `452-excel-shortcuts`. Bind your own through the free `registerGridShortcuts` seam | ✓ |\n| ~~Reference-aware fill~~ | **shipped** - `enableSheet()` wires `translateFormula` into Ctrl+D / Ctrl+R, so a filled formula shifts its relative refs and keeps its pinned ones | ✓ |\n| ~~Paste Special~~ | **shipped** - values / formulas / formats / transpose / add-subtract-multiply-divide / skip-blanks, plus a `text/html` clipboard flavour so formats and formulas survive a round trip through Excel. `Ctrl+Shift+V` | ✓ |\n| ~~Find and Replace~~ | **shipped** - match case, whole cell, look in values or formulas, scope to the selection, Replace All as one undo. `Ctrl+H` | ✓ |\n| ~~Text to Columns, Remove Duplicates~~ | **shipped** - `textToColumns` / `splitText` / `guessDelimiter` split a column, quoted fields and doubled quotes included; `findDuplicates` / `removeDuplicates` report before they remove, and match case-insensitively as Excel does | done |\n| ~~Open and save spreadsheet files~~ | **shipped** - the shell's File tab opens an .xlsx, an .ods, an .xls or a .csv into the document (decided by the bytes, not the file name) and saves the document back as any of the first three, formulas, formats, sizes, hidden lines and sheets, panes, merges, validation, conditional formatting, protection, comments and names included as far as each format carries them; `documentToXlsx` / `documentFromXlsx`, `documentToOds` / `documentFromOds`, `documentToXls` / `documentFromXls` and `documentFromFile` in `@svgrid/enterprise/sheet` | done |\n| ~~Multi-sheet workbook~~ | **shipped** - `createWorkbook` holds named sheets that read each other, with the dependency graph and structural edits spanning them; `<SvSheetTabs>` plus Ctrl+PageUp/PageDown and Shift+F11 | done |\n| ~~Goal Seek~~ | **shipped** - `goalSeekCell` solves for the input that makes a formula hit a target, secant with a bisection fallback, restoring the input cell so a dialog can ask before applying | done |\n| ~~Tables with structured references~~ | **shipped** - `Orders[Amount]`, `[@Amount]`, `[#Headers]` / `[#Totals]` / `[#All]`, column spans and auto-expansion. Resolved at evaluation, so a total keeps covering rows added after it was typed | done |\n| ~~Rewriting formulas on a sheet RENAME~~ | **shipped** - `renameSheet` rewrites every qualified reference and defined name through the parser, so a string literal that happens to hold the old name is untouched and a new name with a space comes out quoted | ✓ |\n| ~~Insert / delete with reference fixup~~ | **shipped** - `Ctrl+Shift+Plus` / `Ctrl+Minus` rewrite every formula and named range through `fixupReferences` | ✓ |\n| ~~Freeze panes: the ROW half~~ | **shipped** - `frozenRows={N}` on the grid keeps the first N rows under the header, the grid's own rows still, with the virtual window skipping them; `applyFreeze` / `freezeAtActiveCell` set both halves through the api and the shell keeps the panes per sheet | ✓ |\n| ~~`Alt+Enter`, `F4`~~ | **shipped** - Alt+Enter is a line break in the grid's multiline text editor and turns on Wrap Text in the sheet, whose rows grow to fit; F4 turns the reference at the caret through its anchorings, in the cell and in the formula bar | ✓ |\n| ~~Notes~~ | **shipped** - `notes` prop + cell comments; demos `85-tooltips-and-notes`, `91-cell-comments` | ✓ |\n| ~~Built-in cell flash / animated change highlight~~ | **shipped** - `cellFlash` on `ColumnDef` | ✓ |\n| ~~Drag a selected range to move / copy it~~ | **shipped** - `moveCells`, on by default with cell selection; demo `429-move-cells` | ✓ |\n| ~~Render inside a shadow root~~ | **shipped** - `<sv-grid-shadow>` in `@svgrid/grid-wc`. Open roots only; closed is unsupported, because a null `shadowRoot` leaves you unable to style, query or test your own grid | ✓ |\n| Eight draggable resize handles on a selection | **declined** - shift-click, shift-arrow, pointer drag and Ctrl+drag already resize a range four ways. Eight more hit targets per range in the hottest render path buys an interaction users already have. | - |\n\n## Export / Print\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~Excel / xlsx, PDF, styled-HTML export, Print~~ | **shipped** in `@svgrid/enterprise` (CSV / TSV / JSON export is free in `@svgrid/grid`) - demos `21`, `56`-`59`, `93`, `101`, `119`, `126`, `127` | ✓ |\n\n## Filtering\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~`between` operator in the column menu~~ | **shipped** - demo `64-filter-between-operator` | ✓ |\n| ~~Set filter (tree-list, async, Excel-mode)~~ | **shipped** - demo `111-set-filter-advanced` | ✓ |\n| ~~Locale-aware text filtering~~ | **shipped** - demo `110-locale-aware-filter` | ✓ |\n| ~~`clearAllFilters()` / `getFilters()`~~ | **shipped** | ✓ |\n| ~~Floating filters (per-operator)~~ | **shipped** - filter row honours every operator per column with typed inputs + inline `between`; demo `179` | ✓ |\n| ~~Multi-condition filter within one column (AND / OR)~~ | **shipped** - two conditions per column via the funnel or `api.setFilter`; demo `178` | ✓ |\n\n## Editing\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~`cellEditor` slot for custom inline editors~~ | **shipped** - demos `84-editor-types`, `66-custom-cell-editors` | ✓ |\n| ~~Built-in select & rich-select editors~~ | **shipped** - `editorType: 'list' / 'rich-select'`; demo `84-editor-types` | ✓ |\n| ~~Built-in large-text (textarea) editor~~ | **shipped** - demo `84-editor-types` | ✓ |\n| ~~Per-column `validate()`~~ | **shipped** - demos `24-validation`, `103-async-validation` | ✓ |\n| ~~Built-in undo / redo stack~~ | **shipped** - `api.undo()` / `redo()`; demo `86-undo-redo` | ✓ |\n| ~~Batch / staged editing mode~~ | **shipped** - demo `88-staged-editing` | ✓ |\n| ~~Per-column `valueParser`~~ | **shipped** - `valueParser` on `ColumnDef`; demo `175` | ✓ |\n| ~~Programmatic `api.startEditing()` / `stopEditing()`~~ | **shipped** - demo `176` | ✓ |\n| ~~Full-row editing mode~~ | **shipped** - `fullRowEditing`; demo `177` | ✓ |\n| ~~Async / server-loaded editor option lists~~ | **shipped** - `editorOptions` may return a Promise (per column or per row), with a loading state, caching and `api.refreshEditorOptions()`; demo `428-async-editor-options` | ✓ |\n\n## The real remaining gaps (short list)\n\nThe previous round shipped declarative col/row spanning, cell flash,\n`valueParser`, programmatic start/stop editing, full-row editing, multi-condition\nfilters, per-operator floating filters, and managed grid-to-grid row dragging -\nall with demos and docs. What is left is a short list of AG-Grid-Enterprise\nparity items, mostly UX affordances on top of engines that already exist:\n\nAudited against the code and the 171-demo catalog (four-way inventory, June 2026).\nThis list is deliberately short - most AG-Grid-Enterprise parity items already\nship (row-group panel `89`, status bar `144`, tool panel `146`, pivot + designer,\nserver-side row model `148`, export with images/styles `56`/`58`, charts,\nsparklines, collaboration). The genuine remaining gaps:\n\n| Gap | What exists today | Effort |\n| --- | ----------------- | ------ |\n| ~~**Multiple range selection** (Ctrl-drag additional cell ranges)~~ | **shipped** - Ctrl/Cmd+drag adds ranges; all highlight + copy together; `api.selectCells([...])` takes many; demo `118` | ✓ |\n| ~~**Cell data-type inference** (`cellDataType`)~~ | **shipped** - `cellDataType` on `ColumnDef` + grid-level `inferColumnTypes` | ✓ |\n| ~~**Merged-cell export to xlsx**~~ | **shipped** - `merges` option on `exportData` (single-sheet), lines up with `MergeSpec` | ✓ |\n| ~~**Filters tool panel tab**~~ | **shipped** - Columns \\| Filters tabs in the tool panel (`146`), in sync with the column menu | ✓ |\n| ~~**Copy with headers**~~ | **shipped** - `copyHeadersToClipboard` + `processCellForClipboard` hook | ✓ |\n| ~~**Aligned grids**~~ | **shipped** - `alignedGridGroup` syncs horizontal scroll + column-resize widths; demo `182` | ✓ |\n| ~~**Collapsible column groups**~~ | **shipped** - `columnGroupShow: 'open' \\| 'closed'` + `openByDefault`; demo `183` | ✓ |\n| ~~**Column menu tabs** (General / Filter / Columns)~~ | **shipped** - tabbed column menu; demo any filterable grid | ✓ |\n| ~~**External row-drag drop zones**~~ | **shipped** - `rowDropZone` action (drop rows onto any element); demo `184` | ✓ |\n| ~~**Nested master/detail grids**~~ | **shipped** - `isDetailRow` + `renderDetailRow` hosting a child grid; demo `181` | ✓ |\n\n### Still open (medium / large)\n\n| Gap | Note | Effort |\n| --- | ---- | ------ |\n| ~~**Multi Filter** (two conditions on one column)~~ | **shipped** - a column filter takes a second condition joined by AND / OR, in the menu and via `api.setFilter` | ✓ |\n| **Custom filter / floating-filter component** slot | first-class pluggable filter. Needs a MODEL seam as well as a render one: menu filters compile through `compileExcelFilter` from the closed `FilterOperator` union, so there is nowhere for a consumer predicate to enter today | L |\n| **Custom tool panels** | panel is fixed Columns + Filters (`toolPanelDefaultTab` is a `\"columns\" \\| \"filters\"` union, with no registry) | M |\n| ~~**UI-string localisation**~~ | **shipped** - the `localization` prop takes `{ locale, text }`, where `text` is a partial `GridMessages`; every menu/panel/chrome string is overridable. Note there is no `localeText` prop, and no translation catalogues ship in the box - you supply the strings. See [i18n and RTL](./i18n-rtl.md) | ✓ |\n| ~~**Row-grouping display modes** + group-level footers~~ | **shipped** - `groupDisplayMode: 'groupRows' \\| 'singleColumn' \\| 'multipleColumns'` plus `groupFooters` and `grandTotalRow`; demo `427-group-footers` | ✓ |\n| ~~**In-grid pivot mode** (toggle on the main grid)~~ | **shipped** - `enablePivot()` registers the engine and the main grid pivots in place | ✓ |\n| ~~**Integrated-chart depth** (chart toolbar, cross-filtering)~~ | **shipped** - `crossFilter` config plus `applyChartCrossFilter` / `clearChartCrossFilter`, wired from chart selection in `SvGridChartPanel`, with a Clear filter button; the panel toolbar has chart-type switching, export, AI, add-chart, tabs, maximize, dock and pop out. The picker now reaches all 15 types (it offered 4), gated on whether the current columns can feed each one, and `--sg-chart-*` tokens let a theme recolour the series | ✓ |\n| ~~**Chart depth program** (axis model, 13 more series types, interaction, financial toolkit, builder)~~ | **shipped** 12 Sep 2026, all free in `@svgrid/grid` - per-axis config with a numeric x axis and reference bands; histogram, range, lollipop, dumbbell, pareto, stream, sunburst, radial, nightingale, chord and bullet; wheel / pinch / pan zoom, range presets, `syncGroup`, a context menu, animation, drilldown, point selection, LTTB decimation; eight indicators with `SvChartPanes`, `rowsToOhlcSpec` + resampling, a last-price line, flags, drawing tools; PDF and print; the in-panel builder with a type gallery, Format tab and link / unlink; `<sv-chart>` with React / Vue / Angular wrappers. Demos 434 to 437 | ✓ |\n| ~~**Server-side pivot**~~ | **shipped** (Enterprise) - `createServerRowModel` sends `pivotBy` + `pivotMode`, builds the columns from `pivotResultFields`, and `SvPivotDesigner` drives it in server mode; see [Server pivot](./server/server-pivot.md) | ✓ |\n| **Viewport row model** (the server pushes the visible window over a socket) | not built; [real-time](./real-time.md) merges deltas into loaded rows instead, and a socket can push a whole level through `applyRowData` | M |\n| **3D charts** | not built; the 29 chart types are all 2D | L |\n| **More render modes, starting with Canvas** | not built; the grid renders to the DOM, with row and column virtualization | L |\n| **Spreadsheet gaps** (track changes, page layout, chart formatting, pivot field list and slicers, Data > Subtotal, server-backed collaboration) | listed with what ships today under [What it does not do](./cells/spreadsheet-shell.md#what-it-does-not-do) | M to L |\n\n## What's already there\n\nThe stable, built-in feature surface is large. Highlights: sorting (single +\nmulti), per-column filtering (menu + row + global) with a `between` range\noperator and set/tree/async filters, pagination, grouping + aggregation, tree\ndata, master/detail + full-width detail rows, row + column virtualization\n(100k+ and a 1M-row demo), cell-range selection + copy/paste + Excel-style fill\nhandle + drag-the-border move/copy, inline editing with 14 editor types plus a custom `cellEditor` slot,\nundo/redo, staged editing, find-in-grid, notes + cell comments, tooltips,\nconditional formatting, sparklines, cell merging, column pinning/reorder/resize,\nrow pinning, a formula engine (+ HyperFormula adapter), a server-side row model (flat paging and infinite scroll free; grouping, tree, pivot, transactions and selection across unloaded rows in Enterprise),\nExcel/PDF/CSV/HTML export + print (Enterprise), pivot + charts + AI (Enterprise),\nWAI-ARIA + keyboard nav, RTL, i18n, theming via `--sg-*` tokens, SSR, and a\nCSP-clean runtime.\n\n## How to contribute\n\n1. Pick a gap from **The real remaining gaps** above.\n2. Open an issue describing the API you'd want - names, types, the minimal change.\n3. If you can write the patch, do so, and keep tests with the change.\n"
|
|
5829
|
+
"markdown": "# Missing features\n\nAn honest accounting of what is **not yet built**, audited against the shipped\ndemo catalog. Most of what used to live here has shipped; the remaining gaps\nare small and clearly marked. Each entry has a rough effort estimate (S / M / L).\n\nShipped items are struck through with the demo or API that covers them, so you\ncan see both the trajectory and the (short) list of real gaps.\n\n## Columns\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~`getRowId` prop~~ | **shipped** | ✓ |\n| ~~`cellClass(ctx)` / `rowClass(ctx)` callbacks~~ | **shipped** | ✓ |\n| ~~`getColumnWidths()` / `setColumnWidth()`~~ | **shipped** | ✓ |\n| ~~`setColumnPinning()` / `getColumnPinning()`~~ | **shipped** | ✓ |\n| ~~Header drag-to-reorder~~ | **shipped** - `enableColumnReorder`; demo `109-column-reorder-engine` | ✓ |\n| ~~Per-column disable sort / filter~~ | **shipped** - `sortable` / `filterable` on `ColumnDef` | ✓ |\n| ~~Column spanning~~ | **shipped** - cell merging via `MergeSpec` + `spreadsheetLayout` (demo `170`), **plus** declarative value-driven `colSpan` / `rowSpan` via `spansToMerges` | ✓ |\n\n## Rows\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~Row pinning (top / bottom)~~ | **shipped** - `pinnedTopRows` / `pinnedBottomRows`; demos `107-pinned-rows`, `108-pinned-rows-engine` | ✓ |\n| ~~Row spanning (merged cells across rows)~~ | **shipped as cell merging** - `rowspan` in `MergeSpec`; demo `170-cell-merging` | ✓ |\n| ~~Full-width / detail row API~~ | **shipped** - `isDetailRow`; demo `106-detail-rows` | ✓ |\n| ~~Variable row height with `<SvGrid>`~~ | **shipped** - `rowHeight` accepts `(rowIndex) => px` | ✓ |\n| ~~Auto row height (measure content)~~ | **shipped** - `autoRowHeight` wraps cell text and measures each row, virtualization included | ✓ |\n| ~~`api.getDisplayedRows()`~~ | **shipped** | ✓ |\n| ~~Client-side tree data (hierarchical rows)~~ | **shipped** - `treeData` nests by parent id, `flattenTreeData` converts nested children; treegrid role + arrow-key expand; demo `426-tree-data` | ✓ |\n| ~~Built-in row dragging~~ | **shipped** - `rowDragManaged` reorders in-grid and moves rows **grid-to-grid** via a shared `rowDragGroup`; `onRowDragEnd` on the receiver; demos `105-row-reorder` (custom) + `180-row-dragging` (managed) | ✓ |\n\n## Cells\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~Built-in tooltip API on `ColumnDef`~~ | **shipped** - `tooltip`; demo `85-tooltips-and-notes` | ✓ |\n| ~~Formula language~~ | **shipped** - a real module in `@svgrid/enterprise/sheet`: parser, evaluator, the common functions plus financial, math, statistics, text and date packs (the [formulas page](./spreadsheet-formulas.md) lists them), cross-sheet and whole-column refs, short-circuiting `IF` / `IFERROR`, a dependency graph for incremental recalc, and `translateFormula` / `fixupReferences`. Absolute refs are now absolute (the demo copies stripped `$`, so anything filled or copied was quietly wrong) | ✓ |\n| ~~Formula editor (bar, autocomplete, Name Box)~~ | **shipped** - `<SvFormulaBar>` shows the raw text behind the active cell, autocompletes function names closest-match-first with signature hints, and its Name Box jumps to an address or a defined name; `createNames` defines them. Demo `453` | ✓ |\n| ~~Per-cell number formats and styles~~ | **shipped** - an Excel format-string compiler (`compileNumberFormat`) plus a store keyed on row id so formatting survives a sort, wired to Ctrl+1, Ctrl+Shift+1..6 and Ctrl+B/I/U. Demo `453` | ✓ |\n| ~~AutoSum~~ | **shipped** - Alt+= over the run Excel would guess | ✓ |\n| ~~Reference highlighting~~ | **shipped** - the sheet shell outlines every cell a formula in progress refers to, each in its own colour, whether the formula is typed in the cell or in the bar | ✓ |\n| ~~Find-in-grid~~ | **shipped** - Ctrl+F marks every match (the current one more strongly) and matches what a cell shows as well as its value; demo `87-find-in-grid` | ✓ |\n| ~~Excel keyboard shortcuts~~ | **shipped** - `enableSheet()` binds Ctrl+Arrow (a run-boundary search, so it hops gaps), Ctrl+Shift+Arrow, Ctrl+A region-then-sheet, Ctrl+Space / Shift+Space, Ctrl+D / Ctrl+R, Ctrl+; and Ctrl+'; demo `452-excel-shortcuts`. Bind your own through the free `registerGridShortcuts` seam | ✓ |\n| ~~Reference-aware fill~~ | **shipped** - `enableSheet()` wires `translateFormula` into Ctrl+D / Ctrl+R, so a filled formula shifts its relative refs and keeps its pinned ones | ✓ |\n| ~~Paste Special~~ | **shipped** - values / formulas / formats / transpose / add-subtract-multiply-divide / skip-blanks, plus a `text/html` clipboard flavour so formats and formulas survive a round trip through Excel. `Ctrl+Shift+V` | ✓ |\n| ~~Find and Replace~~ | **shipped** - match case, whole cell, look in values or formulas, scope to the selection, Replace All as one undo. `Ctrl+H` | ✓ |\n| ~~Text to Columns, Remove Duplicates~~ | **shipped** - `textToColumns` / `splitText` / `guessDelimiter` split a column, quoted fields and doubled quotes included; `findDuplicates` / `removeDuplicates` report before they remove, and match case-insensitively as Excel does | done |\n| ~~Open and save spreadsheet files~~ | **shipped** - the shell's File tab opens an .xlsx, an .ods, an .xls or a .csv into the document (decided by the bytes, not the file name) and saves the document back as any of the first three, formulas, formats, sizes, hidden lines and sheets, panes, merges, validation, conditional formatting, protection, comments and names included as far as each format carries them; `documentToXlsx` / `documentFromXlsx`, `documentToOds` / `documentFromOds`, `documentToXls` / `documentFromXls` and `documentFromFile` in `@svgrid/enterprise/sheet` | done |\n| ~~Multi-sheet workbook~~ | **shipped** - `createWorkbook` holds named sheets that read each other, with the dependency graph and structural edits spanning them; `<SvSheetTabs>` plus Ctrl+PageUp/PageDown and Shift+F11 | done |\n| ~~Goal Seek~~ | **shipped** - `goalSeekCell` solves for the input that makes a formula hit a target, secant with a bisection fallback, restoring the input cell so a dialog can ask before applying | done |\n| ~~Tables with structured references~~ | **shipped** - `Orders[Amount]`, `[@Amount]`, `[#Headers]` / `[#Totals]` / `[#All]`, column spans and auto-expansion. Resolved at evaluation, so a total keeps covering rows added after it was typed | done |\n| ~~Rewriting formulas on a sheet RENAME~~ | **shipped** - `renameSheet` rewrites every qualified reference and defined name through the parser, so a string literal that happens to hold the old name is untouched and a new name with a space comes out quoted | ✓ |\n| ~~Insert / delete with reference fixup~~ | **shipped** - `Ctrl+Shift+Plus` / `Ctrl+Minus` rewrite every formula and named range through `fixupReferences` | ✓ |\n| ~~Freeze panes: the ROW half~~ | **shipped** - `frozenRows={N}` on the grid keeps the first N rows under the header, the grid's own rows still, with the virtual window skipping them; `applyFreeze` / `freezeAtActiveCell` set both halves through the api and the shell keeps the panes per sheet | ✓ |\n| ~~`Alt+Enter`, `F4`~~ | **shipped** - Alt+Enter is a line break in the grid's multiline text editor and turns on Wrap Text in the sheet, whose rows grow to fit; F4 turns the reference at the caret through its anchorings, in the cell and in the formula bar | ✓ |\n| ~~Notes~~ | **shipped** - `notes` prop + cell comments; demos `85-tooltips-and-notes`, `91-cell-comments` | ✓ |\n| ~~Built-in cell flash / animated change highlight~~ | **shipped** - `cellFlash` on `ColumnDef` | ✓ |\n| ~~Drag a selected range to move / copy it~~ | **shipped** - `moveCells`, on by default with cell selection; demo `429-move-cells` | ✓ |\n| ~~Render inside a shadow root~~ | **shipped** - `<sv-grid-shadow>` in `@svgrid/grid-wc`. Open roots only; closed is unsupported, because a null `shadowRoot` leaves you unable to style, query or test your own grid | ✓ |\n| Eight draggable resize handles on a selection | **declined** - shift-click, shift-arrow, pointer drag and Ctrl+drag already resize a range four ways. Eight more hit targets per range in the hottest render path buys an interaction users already have. | - |\n\n## Export / Print\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~Excel / xlsx, PDF, styled-HTML export, Print~~ | **shipped** in `@svgrid/enterprise` (CSV / TSV / JSON export is free in `@svgrid/grid`) - demos `21`, `56`-`59`, `93`, `101`, `119`, `126`, `127` | ✓ |\n\n## Filtering\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~`between` operator in the column menu~~ | **shipped** - demo `64-filter-between-operator` | ✓ |\n| ~~Set filter (tree-list, async, Excel-mode)~~ | **shipped** - demo `111-set-filter-advanced` | ✓ |\n| ~~Locale-aware text filtering~~ | **shipped** - demo `110-locale-aware-filter` | ✓ |\n| ~~`clearAllFilters()` / `getFilters()`~~ | **shipped** | ✓ |\n| ~~Floating filters (per-operator)~~ | **shipped** - filter row honours every operator per column with typed inputs + inline `between`; demo `179` | ✓ |\n| ~~Multi-condition filter within one column (AND / OR)~~ | **shipped** - two conditions per column via the funnel or `api.setFilter`; demo `178` | ✓ |\n\n## Editing\n\n| Gap | Status | Effort |\n| --- | ------ | ------ |\n| ~~`cellEditor` slot for custom inline editors~~ | **shipped** - demos `84-editor-types`, `66-custom-cell-editors` | ✓ |\n| ~~Built-in select & rich-select editors~~ | **shipped** - `editorType: 'list' / 'rich-select'`; demo `84-editor-types` | ✓ |\n| ~~Built-in large-text (textarea) editor~~ | **shipped** - demo `84-editor-types` | ✓ |\n| ~~Per-column `validate()`~~ | **shipped** - demos `24-validation`, `103-async-validation` | ✓ |\n| ~~Built-in undo / redo stack~~ | **shipped** - `api.undo()` / `redo()`; demo `86-undo-redo` | ✓ |\n| ~~Batch / staged editing mode~~ | **shipped** - demo `88-staged-editing` | ✓ |\n| ~~Per-column `valueParser`~~ | **shipped** - `valueParser` on `ColumnDef`; demo `175` | ✓ |\n| ~~Programmatic `api.startEditing()` / `stopEditing()`~~ | **shipped** - demo `176` | ✓ |\n| ~~Full-row editing mode~~ | **shipped** - `fullRowEditing`; demo `177` | ✓ |\n| ~~Async / server-loaded editor option lists~~ | **shipped** - `editorOptions` may return a Promise (per column or per row), with a loading state, caching and `api.refreshEditorOptions()`; demo `428-async-editor-options` | ✓ |\n\n## The real remaining gaps (short list)\n\nThe previous round shipped declarative col/row spanning, cell flash,\n`valueParser`, programmatic start/stop editing, full-row editing, multi-condition\nfilters, per-operator floating filters, and managed grid-to-grid row dragging -\nall with demos and docs. What is left is a short list of AG-Grid-Enterprise\nparity items, mostly UX affordances on top of engines that already exist:\n\nAudited against the code and the 171-demo catalog (four-way inventory, June 2026).\nThis list is deliberately short - most AG-Grid-Enterprise parity items already\nship (row-group panel `89`, status bar `144`, tool panel `146`, pivot + designer,\nserver-side row model `148`, export with images/styles `56`/`58`, charts,\nsparklines, collaboration). The genuine remaining gaps:\n\n| Gap | What exists today | Effort |\n| --- | ----------------- | ------ |\n| ~~**Multiple range selection** (Ctrl-drag additional cell ranges)~~ | **shipped** - Ctrl/Cmd+drag adds ranges; all highlight + copy together; `api.selectCells([...])` takes many; demo `118` | ✓ |\n| ~~**Cell data-type inference** (`cellDataType`)~~ | **shipped** - `cellDataType` on `ColumnDef` + grid-level `inferColumnTypes` | ✓ |\n| ~~**Merged-cell export to xlsx**~~ | **shipped** - `merges` option on `exportData` (single-sheet), lines up with `MergeSpec` | ✓ |\n| ~~**Filters tool panel tab**~~ | **shipped** - Columns \\| Filters tabs in the tool panel (`146`), in sync with the column menu | ✓ |\n| ~~**Copy with headers**~~ | **shipped** - `copyHeadersToClipboard` + `processCellForClipboard` hook | ✓ |\n| ~~**Aligned grids**~~ | **shipped** - `alignedGridGroup` syncs horizontal scroll + column-resize widths; demo `182` | ✓ |\n| ~~**Collapsible column groups**~~ | **shipped** - `columnGroupShow: 'open' \\| 'closed'` + `openByDefault`; demo `183` | ✓ |\n| ~~**Column menu tabs** (General / Filter / Columns)~~ | **shipped** - tabbed column menu; demo any filterable grid | ✓ |\n| ~~**External row-drag drop zones**~~ | **shipped** - `rowDropZone` action (drop rows onto any element); demo `184` | ✓ |\n| ~~**Nested master/detail grids**~~ | **shipped** - `isDetailRow` + `renderDetailRow` hosting a child grid; demo `181` | ✓ |\n\n### Still open (medium / large)\n\n| Gap | Note | Effort |\n| --- | ---- | ------ |\n| ~~**Multi Filter** (two conditions on one column)~~ | **shipped** - a column filter takes a second condition joined by AND / OR, in the menu and via `api.setFilter` | ✓ |\n| ~~**Custom filter / floating-filter component** slot~~ | **shipped** - a column's `customFilter` takes a `predicate(model)` plus `menu` / `floating` snippets; the model is in `api.setCustomFilter`, `getState().customFilters` and `onFiltersChange`'s `custom`. See [Custom column filters](./filtering/custom-column-filters.md) | ✓ |\n| **Custom tool panels** | panel is fixed Columns + Filters (`toolPanelDefaultTab` is a `\"columns\" \\| \"filters\"` union, with no registry) | M |\n| ~~**UI-string localisation**~~ | **shipped** - the `localization` prop takes `{ locale, text }`, where `text` is a partial `GridMessages`; every menu/panel/chrome string is overridable. Note there is no `localeText` prop, and no translation catalogues ship in the box - you supply the strings. See [i18n and RTL](./i18n-rtl.md) | ✓ |\n| ~~**Row-grouping display modes** + group-level footers~~ | **shipped** - `groupDisplayMode: 'groupRows' \\| 'singleColumn' \\| 'multipleColumns'` plus `groupFooters` and `grandTotalRow`; demo `427-group-footers` | ✓ |\n| ~~**In-grid pivot mode** (toggle on the main grid)~~ | **shipped** - `enablePivot()` registers the engine and the main grid pivots in place | ✓ |\n| ~~**Integrated-chart depth** (chart toolbar, cross-filtering)~~ | **shipped** - `crossFilter` config plus `applyChartCrossFilter` / `clearChartCrossFilter`, wired from chart selection in `SvGridChartPanel`, with a Clear filter button; the panel toolbar has chart-type switching, export, AI, add-chart, tabs, maximize, dock and pop out. The picker now reaches all 15 types (it offered 4), gated on whether the current columns can feed each one, and `--sg-chart-*` tokens let a theme recolour the series | ✓ |\n| ~~**Chart depth program** (axis model, 13 more series types, interaction, financial toolkit, builder)~~ | **shipped** 12 Sep 2026, all free in `@svgrid/grid` - per-axis config with a numeric x axis and reference bands; histogram, range, lollipop, dumbbell, pareto, stream, sunburst, radial, nightingale, chord and bullet; wheel / pinch / pan zoom, range presets, `syncGroup`, a context menu, animation, drilldown, point selection, LTTB decimation; eight indicators with `SvChartPanes`, `rowsToOhlcSpec` + resampling, a last-price line, flags, drawing tools; PDF and print; the in-panel builder with a type gallery, Format tab and link / unlink; `<sv-chart>` with React / Vue / Angular wrappers. Demos 434 to 437 | ✓ |\n| ~~**Server-side pivot**~~ | **shipped** (Enterprise) - `createServerRowModel` sends `pivotBy` + `pivotMode`, builds the columns from `pivotResultFields`, and `SvPivotDesigner` drives it in server mode; see [Server pivot](./server/server-pivot.md) | ✓ |\n| **Viewport row model** (the server pushes the visible window over a socket) | not built; [real-time](./real-time.md) merges deltas into loaded rows instead, and a socket can push a whole level through `applyRowData` | M |\n| **3D charts** | not built; the 29 chart types are all 2D | L |\n| **More render modes, starting with Canvas** | not built; the grid renders to the DOM, with row and column virtualization | L |\n| **Spreadsheet gaps** (track changes, page layout, chart formatting, pivot field list and slicers, Data > Subtotal, server-backed collaboration) | listed with what ships today under [What it does not do](./cells/spreadsheet-shell.md#what-it-does-not-do) | M to L |\n\n## What's already there\n\nThe stable, built-in feature surface is large. Highlights: sorting (single +\nmulti), per-column filtering (menu + row + global) with a `between` range\noperator and set/tree/async filters, pagination, grouping + aggregation, tree\ndata, master/detail + full-width detail rows, row + column virtualization\n(100k+ and a 1M-row demo), cell-range selection + copy/paste + Excel-style fill\nhandle + drag-the-border move/copy, inline editing with 14 editor types plus a custom `cellEditor` slot,\nundo/redo, staged editing, find-in-grid, notes + cell comments, tooltips,\nconditional formatting, sparklines, cell merging, column pinning/reorder/resize,\nrow pinning, a formula engine (+ HyperFormula adapter), a server-side row model (flat paging and infinite scroll free; grouping, tree, pivot, transactions and selection across unloaded rows in Enterprise),\nExcel/PDF/CSV/HTML export + print (Enterprise), pivot + charts + AI (Enterprise),\nWAI-ARIA + keyboard nav, RTL, i18n, theming via `--sg-*` tokens, SSR, and a\nCSP-clean runtime.\n\n## How to contribute\n\n1. Pick a gap from **The real remaining gaps** above.\n2. Open an issue describing the API you'd want - names, types, the minimal change.\n3. If you can write the patch, do so, and keep tests with the change.\n"
|
|
5830
5830
|
},
|
|
5831
5831
|
{
|
|
5832
5832
|
"slug": "help/mobile-card-view",
|
|
@@ -8346,8 +8346,8 @@ export const apiReference = {
|
|
|
8346
8346
|
]
|
|
8347
8347
|
};
|
|
8348
8348
|
export const apiSurface = {
|
|
8349
|
-
"gridVersion": "3.0.
|
|
8350
|
-
"enterpriseVersion": "3.1.
|
|
8349
|
+
"gridVersion": "3.0.18",
|
|
8350
|
+
"enterpriseVersion": "3.1.16",
|
|
8351
8351
|
"grid": {
|
|
8352
8352
|
"values": [
|
|
8353
8353
|
"ALL_FILTER_OPERATORS",
|
|
@@ -9165,6 +9165,8 @@ export const apiSurface = {
|
|
|
9165
9165
|
"CountryInput",
|
|
9166
9166
|
"CountryInputConfig",
|
|
9167
9167
|
"CsvOptions",
|
|
9168
|
+
"CustomColumnFilter",
|
|
9169
|
+
"CustomFilterContext",
|
|
9168
9170
|
"DataBarFormat",
|
|
9169
9171
|
"DateLike",
|
|
9170
9172
|
"DateRangeValue",
|
|
@@ -11614,6 +11616,11 @@ export const apiSurface = {
|
|
|
11614
11616
|
"optional": true,
|
|
11615
11617
|
"type": "boolean"
|
|
11616
11618
|
},
|
|
11619
|
+
{
|
|
11620
|
+
"name": "customFilter",
|
|
11621
|
+
"optional": true,
|
|
11622
|
+
"type": "CustomColumnFilter<TData>"
|
|
11623
|
+
},
|
|
11617
11624
|
{
|
|
11618
11625
|
"name": "editorOptions",
|
|
11619
11626
|
"optional": true,
|
|
@@ -11883,6 +11890,7 @@ export const apiSurface = {
|
|
|
11883
11890
|
"getColumnWidths",
|
|
11884
11891
|
"getColumns",
|
|
11885
11892
|
"getCommandContext",
|
|
11893
|
+
"getCustomFilter",
|
|
11886
11894
|
"getData",
|
|
11887
11895
|
"getDisplayedRows",
|
|
11888
11896
|
"getElement",
|
|
@@ -11936,6 +11944,7 @@ export const apiSurface = {
|
|
|
11936
11944
|
"setColumnPinning",
|
|
11937
11945
|
"setColumnVisible",
|
|
11938
11946
|
"setColumnWidth",
|
|
11947
|
+
"setCustomFilter",
|
|
11939
11948
|
"setFacetFilter",
|
|
11940
11949
|
"setFilter",
|
|
11941
11950
|
"setFindQuery",
|
package/dist/preview.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const UI_RESOURCE_URI = "ui://svgrid/preview.html";
|
|
|
8
8
|
* start failing in a way nobody is watching for. `tools/mcp-tools.test.ts` checks these
|
|
9
9
|
* against the workspace so they cannot quietly rot either.
|
|
10
10
|
*/
|
|
11
|
-
export declare const PREVIEW_GRID_WC_VERSION = "3.0.
|
|
11
|
+
export declare const PREVIEW_GRID_WC_VERSION = "3.0.22";
|
|
12
12
|
export declare function previewResource(): {
|
|
13
13
|
uri: string;
|
|
14
14
|
name: string;
|
package/dist/preview.js
CHANGED
|
@@ -27,7 +27,7 @@ const UI_RESOURCE_META_KEY = 'ui/resourceUri';
|
|
|
27
27
|
* start failing in a way nobody is watching for. `tools/mcp-tools.test.ts` checks these
|
|
28
28
|
* against the workspace so they cannot quietly rot either.
|
|
29
29
|
*/
|
|
30
|
-
export const PREVIEW_GRID_WC_VERSION = '3.0.
|
|
30
|
+
export const PREVIEW_GRID_WC_VERSION = '3.0.22';
|
|
31
31
|
const EXT_APPS_VERSION = '1.7.5';
|
|
32
32
|
const CDN = 'https://cdn.jsdelivr.net/npm';
|
|
33
33
|
/**
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "commercial",
|
|
6
6
|
"url": "https://svgrid.com/pricing"
|
|
7
7
|
},
|
|
8
|
-
"version": "3.0.
|
|
8
|
+
"version": "3.0.22",
|
|
9
9
|
"description": "Model Context Protocol server for SvGrid, the Svelte 5 data grid: checks the code your AI writes against the real API surface, plus version-pinned docs, API reference and 373 demo sources.",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"author": "jQWidgets <sales@jqwidgets.com>",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
34
34
|
"svelte": "^5.55.5",
|
|
35
35
|
"zod": "^3.23.8",
|
|
36
|
-
"@svgrid/enterprise": "^3.1.
|
|
36
|
+
"@svgrid/enterprise": "^3.1.16"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22.10.7",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "com.svgrid/svgrid",
|
|
4
4
|
"title": "SvGrid",
|
|
5
5
|
"description": "Checks AI-written SvGrid code against the real API, plus version-pinned Svelte 5 grid docs.",
|
|
6
|
-
"version": "3.0.
|
|
6
|
+
"version": "3.0.22",
|
|
7
7
|
"websiteUrl": "https://svgrid.com/docs/help/mcp-server/",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/sv-grid/sv-grid",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
17
17
|
"identifier": "@svgrid/mcp",
|
|
18
|
-
"version": "3.0.
|
|
18
|
+
"version": "3.0.22",
|
|
19
19
|
"runtimeHint": "npx",
|
|
20
20
|
"transport": {
|
|
21
21
|
"type": "stdio"
|