<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bmarkov</title>
    <description>The latest articles on DEV Community by bmarkov (@bmarkov).</description>
    <link>https://dev.to/bmarkov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F137371%2Fe101bc37-e23b-4ab5-aeb3-cac077021867.png</url>
      <title>DEV Community: bmarkov</title>
      <link>https://dev.to/bmarkov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bmarkov"/>
    <language>en</language>
    <item>
      <title>The Svelte Data Grid That Ships Its Own MCP Server</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Fri, 28 Aug 2026 07:44:42 +0000</pubDate>
      <link>https://dev.to/bmarkov/the-svelte-data-grid-that-ships-its-own-mcp-server-1676</link>
      <guid>https://dev.to/bmarkov/the-svelte-data-grid-that-ships-its-own-mcp-server-1676</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fedhb5r2q3bxh1bh89y39.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fedhb5r2q3bxh1bh89y39.gif" alt="Svelte Data Grid MCP" width="720" height="710"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ask a coding model to wire up a Svelte 5 data grid and you usually get code that looks right and does not run. The prop names drift in from a React grid, the reactivity is Svelte 4 stores, and the version it learned from shipped a year and a half ago. You paste the error back, it apologises, it invents a slightly different API.&lt;/p&gt;

&lt;p&gt;That failure mode is the thing &lt;a href="https://svgrid.com" rel="noopener noreferrer"&gt;SvGrid&lt;/a&gt; is built around. It is a Svelte 5 native data grid: a headless core plus a &lt;code&gt;&amp;lt;SvGrid&amp;gt;&lt;/code&gt; renderer, virtualization, grouping, editing, server-side data, keyboard and screen-reader support. The core package &lt;code&gt;@svgrid/grid&lt;/code&gt; is MIT.&lt;/p&gt;

&lt;p&gt;The part worth writing about is what sits on top of that. AI shows up twice in this product: once at build time, so your editor writes correct grid code, and once at runtime, so your users can talk to their data. Here are the five pieces that matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. An MCP server, so the model reads the API instead of remembering it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; is the open standard for exposing tools to LLM clients. SvGrid ships one: &lt;code&gt;@svgrid/mcp&lt;/code&gt;, MIT licensed, listed in the official MCP registry as &lt;code&gt;com.svgrid/svgrid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One command in Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add svgrid &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @svgrid/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the same server in Cursor, Zed, Claude Desktop, or anything else that speaks MCP over stdio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"svgrid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@svgrid/mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eight of the tools cover docs and examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it returns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_examples&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every demo: id, title, one-line blurb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_example_source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The full &lt;code&gt;.svelte&lt;/code&gt; source of one demo, verbatim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;list_docs&lt;/code&gt; / &lt;code&gt;get_doc&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Doc slugs, then the markdown of a page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_docs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Substring search across the docs with excerpts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_api_reference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The curated public API surface, grouped by category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;introspect_source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A draft entity schema from a Drizzle file or sample rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scaffold_entity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runnable SvelteKit files for one entity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two details do most of the work here. First, the corpus is &lt;strong&gt;inlined into the package at build time&lt;/strong&gt;, so answers are pinned to the version you installed rather than to whatever the model absorbed during training. Second, &lt;code&gt;get_example_source&lt;/code&gt; returns real working files, all 360+ of them, so "build me a grid that groups by department and shows a sparkline per row" starts from code that already compiles.&lt;/p&gt;

&lt;p&gt;It runs locally over stdio. No API key, no telemetry, no outbound call. stdout is JSON-RPC, logs go to stderr.&lt;/p&gt;

&lt;p&gt;If you do not want to run a server, the same grounding ships as static files you can paste into a custom GPT, a Claude project, or your own agent's system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topicMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://svgrid.com/llms.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schemas&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://svgrid.com/schemas/index.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is the ~10 kB topic map, &lt;code&gt;llms-full.txt&lt;/code&gt; is every doc page concatenated, and &lt;code&gt;schemas/index.json&lt;/code&gt; gives you JSON Schema for &lt;code&gt;ColumnDef&lt;/code&gt; and the &lt;code&gt;&amp;lt;SvGrid&amp;gt;&lt;/code&gt; props so a generated config can be machine-validated before you ever run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Natural-language filtering that cannot invent a column
&lt;/h2&gt;

&lt;p&gt;Now the runtime side. The AI helpers live in &lt;code&gt;@svgrid/grid&lt;/code&gt; itself, MIT, no license key and no separate package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aiFilter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@svgrid/grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;aiFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accounts losing momentum in EMEA, by NPS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   filters: [&lt;/span&gt;
&lt;span class="c1"&gt;//     { field: 'region', operator: 'equals',   value: 'EMEA' },&lt;/span&gt;
&lt;span class="c1"&gt;//     { field: 'nps',    operator: 'lessThan', value: '30' },&lt;/span&gt;
&lt;span class="c1"&gt;//   ],&lt;/span&gt;
&lt;span class="c1"&gt;//   sort: [{ field: 'nps', desc: false }],&lt;/span&gt;
&lt;span class="c1"&gt;//   rationale: 'EMEA region, low NPS, sorted ascending.',&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things keep this from becoming a party trick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The prompt is grounded in your columns.&lt;/strong&gt; Before the call, the grid embeds the live column schema (names, types, sampled values) so the model picks real field names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A hallucination guard on the way back.&lt;/strong&gt; If the model still returns a column that does not exist, that clause is dropped rather than handed to &lt;code&gt;setFilter&lt;/code&gt;, which would throw. You would rather lose a clause than crash the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It returns a plan and does not apply it.&lt;/strong&gt; That gives you the "here is what I would do, accept?" preview for free. Pass &lt;code&gt;{ apply: true }&lt;/code&gt; when you want it committed directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an analyst-facing table this replaces a dozen per-column filter operators with one text box.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Smart fill, and paste from wherever the data actually came from
&lt;/h2&gt;

&lt;p&gt;Smart fill is the spreadsheet feature. The user types one or two worked examples in a column, and the model proposes the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;aiSmartFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;company&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Northwind&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enterprise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;company&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Helios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;growth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// result.predictions: [{ rowIndex, value, confidence }, ...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You decide what happens next: accept all, accept per cell, or write the prediction to a shadow field and render a confidence pill in the cell.&lt;/p&gt;

&lt;p&gt;Smart paste is the one that RevOps users react to. Drop in CSV, TSV, JSON, a vCard, a Markdown table copied out of Slack, an email signature block, or a paragraph of prose, and the parser detects the format and maps it to typed rows. Every parsed row then goes through three normalization passes before the preview renders: email typo correction (&lt;code&gt;gmial.com&lt;/code&gt;), phone rewriting to a consistent &lt;code&gt;+CC AAA BBB CCCC&lt;/code&gt; shape, and name cleaning that strips &lt;code&gt;Dr.&lt;/code&gt; / &lt;code&gt;Jr.&lt;/code&gt; and rewrites &lt;code&gt;"Last, First"&lt;/code&gt;. Duplicate emails collapse into one row with missing fields merged in.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. One adapter, no bundled model
&lt;/h2&gt;

&lt;p&gt;Every AI call in the grid routes through a single async function you register once at boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setAIProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AIProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@svgrid/grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AIProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;responseFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;responseFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`AI provider returned &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;setAIProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The grid ships no model client at all. Model choice, routing, key handling, and what leaves the browser stay yours. &lt;code&gt;signal&lt;/code&gt; is forwarded so you can cancel in flight, and &lt;code&gt;task&lt;/code&gt; is tagged (&lt;code&gt;'filter' | 'smart-fill' | 'summarize' | 'classify'&lt;/code&gt;) so you can route cheap tasks to a small model and summaries to a stronger one.&lt;/p&gt;

&lt;p&gt;That one provider powers the whole set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;aiFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;        &lt;span class="c1"&gt;// NL query -&amp;gt; filter + sort plan&lt;/span&gt;
&lt;span class="nf"&gt;aiSmartFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;// examples -&amp;gt; proposed column values&lt;/span&gt;
&lt;span class="nf"&gt;aiSummarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;// row / selection / group / all -&amp;gt; text + bullets&lt;/span&gt;
&lt;span class="nf"&gt;aiClassify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="c1"&gt;// free-text cells -&amp;gt; bucketed labels&lt;/span&gt;
&lt;span class="nf"&gt;aiExport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;        &lt;span class="c1"&gt;// NL query -&amp;gt; filter + group + format, then export&lt;/span&gt;
&lt;span class="nf"&gt;aiFindAnomalies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;        &lt;span class="c1"&gt;// scan a slice for outliers and bad values&lt;/span&gt;
&lt;span class="nf"&gt;enableAiCharting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="c1"&gt;// adds an AI button to the chart panel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;enableAiCharting&lt;/code&gt;, "revenue by country, stacked by product" becomes a chart config (type, group-by, split-by, measure, aggregate) applied live.&lt;/p&gt;

&lt;p&gt;Helpers request &lt;code&gt;responseFormat: 'json'&lt;/code&gt; and the grid parses the reply, stripping a single markdown fence first so models that wrap output in a code block still parse. Malformed output gives you a typed error rather than a silent wrong answer.&lt;/p&gt;

&lt;p&gt;Nothing is imported until you import it, so if you do not want natural language anywhere, these tree-shake away and cost nothing. A bundled &lt;code&gt;mockAIProvider&lt;/code&gt; returns deterministic canned shapes per task, which is how every AI demo on the site runs with no key.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The grid is a tool surface
&lt;/h2&gt;

&lt;p&gt;The imperative &lt;code&gt;SvGridApi&lt;/code&gt; maps cleanly onto function calling, so an agent can drive the UI rather than just describe it. Each method becomes one tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setFilter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="cm"&gt;/* columnId, operator, value */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setSort&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="cm"&gt;/* columnId, direction */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setGroupBy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* columnIds */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clearAllFilters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point &lt;code&gt;getDisplayedRows()&lt;/code&gt; at a read-only summariser and the model sees exactly what the user sees, filters and sorting and grouping already applied, instead of the raw dataset.&lt;/p&gt;

&lt;p&gt;The same idea runs one level up. Alongside the docs tools, the MCP server exposes 27 &lt;code&gt;studio_*&lt;/code&gt; tools that drive the same validated project model the visual designer uses: add entities, screens, blocks and components, bind a data source, set auth, RBAC, theme, the typed data layer, and the deploy target, then emit a full runnable SvelteKit app. Every edit goes through the model's own functions and &lt;code&gt;validateProject&lt;/code&gt;, so an agent that guesses wrong gets a validation error instead of a broken app. Those generators are part of the commercial Studio tier; the docs and example tools are free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth a look if
&lt;/h2&gt;

&lt;p&gt;You are building tables in Svelte 5 and you are tired of correcting your assistant's grid code, or you want natural-language filtering in an app without shipping a model client and a vendor lock-in with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create @svgrid@latest
claude mcp add svgrid &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @svgrid/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://svgrid.com/docs/help/mcp-server/" rel="noopener noreferrer"&gt;MCP server docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://svgrid.com/docs/help/ai/" rel="noopener noreferrer"&gt;AI assistant docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://svgrid.com/docs/help/agents/" rel="noopener noreferrer"&gt;Building an agent that drives the grid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://svgrid.com/demos/" rel="noopener noreferrer"&gt;Live demos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>svelte</category>
      <category>ai</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A visual data-app builder that generates SvelteKit source</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Fri, 17 Jul 2026 07:45:00 +0000</pubDate>
      <link>https://dev.to/bmarkov/a-visual-data-app-builder-that-generates-sveltekit-source-4lci</link>
      <guid>https://dev.to/bmarkov/a-visual-data-app-builder-that-generates-sveltekit-source-4lci</guid>
      <description>&lt;p&gt;Every few months I end up building the same thing: an admin screen. A table of&lt;br&gt;
rows, a form to edit one, some validation, a chart, wire it to an API, add&lt;br&gt;
paging. It is never hard, exactly. It is just hours of boilerplate I have typed&lt;br&gt;
a hundred times.&lt;/p&gt;

&lt;p&gt;The usual fix is a hosted builder (Retool, Appsmith, and friends). They are&lt;br&gt;
great until the day you want to leave, and then you find out the app only runs&lt;br&gt;
inside their platform. There is no "eject to code" button that gives you&lt;br&gt;
something real.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;SvGrid Studio&lt;/strong&gt;: a visual builder that composes those screens for&lt;br&gt;
you and then hands you a plain &lt;strong&gt;SvelteKit project you own and host yourself&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it actually does
&lt;/h2&gt;

&lt;p&gt;You start from a schema, add screens made of blocks (grid, edit form, chart,&lt;br&gt;
dashboard, KPI, master-detail), point them at a data source, and click&lt;br&gt;
&lt;strong&gt;Generate app&lt;/strong&gt;. Out comes real &lt;code&gt;.svelte&lt;/code&gt; files, &lt;code&gt;load&lt;/code&gt; functions, and form&lt;br&gt;
actions. No runtime to phone home to, nothing interpreting a JSON config. If you&lt;br&gt;
uninstall Studio tomorrow, the generated app keeps running.&lt;/p&gt;

&lt;p&gt;The core idea is that &lt;strong&gt;one schema drives everything&lt;/strong&gt; - the grid columns, the&lt;br&gt;
edit form, the validation, and the codegen - so they can never drift apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;idField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reference&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customerId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;relation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;labelField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Amount ($)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;draft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Draft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refunded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Refunded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That schema alone gives you a typed, sortable, filterable grid and a matching&lt;br&gt;
edit form with the relation rendered as a searchable lookup. Studio just lets&lt;br&gt;
you arrange those pieces on a canvas visually instead of wiring them by hand.&lt;/p&gt;
&lt;h2&gt;
  
  
  You own the output
&lt;/h2&gt;

&lt;p&gt;This is the part I care about most. The generated project is ordinary&lt;br&gt;
SvelteKit, the kind of code you would have written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/routes/orders/+page.svelte (generated) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SvGrid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@svgrid/grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SvGridEditPanel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@svgrid/enterprise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$lib/schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$props&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;editing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;SvGrid&lt;/span&gt; &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;columns=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;schemaToColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onRowClick=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;#if&lt;/span&gt; &lt;span class="nx"&gt;editing&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;SvGridEditPanel&lt;/span&gt; &lt;span class="na"&gt;schema=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;row=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;editing&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                   &lt;span class="na"&gt;onSubmit=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;saveOrder&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onCancel=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;/if&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It imports the &lt;code&gt;@svgrid&lt;/code&gt; packages the way any app imports a library, but there&lt;br&gt;
is no hosted platform in the loop. You commit it, you deploy it to Vercel or a&lt;br&gt;
Node box or wherever, you change it freely.&lt;/p&gt;

&lt;h2&gt;
  
  
  One data-source contract, four backends
&lt;/h2&gt;

&lt;p&gt;Every source implements the same &lt;code&gt;ServerDataSource&lt;/code&gt; contract, so sort, filter,&lt;br&gt;
paging and CRUD map uniformly onto whatever you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;REST&lt;/strong&gt; (any JSON API)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; (PostgREST, with realtime and FK lookups auto-detected)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;in-memory&lt;/strong&gt; (for prototyping)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is even a demo that runs &lt;strong&gt;real Postgres in the browser&lt;/strong&gt; via PGlite&lt;br&gt;
(WASM), so you can try the SQL path with no backend at all and watch the actual&lt;br&gt;
query it generates under the toolbar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Driving it with AI
&lt;/h2&gt;

&lt;p&gt;There is an MCP server too. You can describe the app to an AI assistant&lt;br&gt;
("customers, orders, a revenue dashboard"), get back the schema plus screens,&lt;br&gt;
and then refine them in the visual designer. The AI produces the same schema&lt;br&gt;
objects you would author by hand, so nothing is a black box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Svelte 5
&lt;/h2&gt;

&lt;p&gt;It is a real Svelte grid built on runes, not a React grid ported over, so the&lt;br&gt;
output is small and, more importantly, &lt;strong&gt;readable&lt;/strong&gt;. If a builder is going to&lt;br&gt;
generate code you have to maintain, the code has to look like code a person&lt;br&gt;
wrote. That is a lot easier to promise when the framework has no vdom and the&lt;br&gt;
components compile away.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest bits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It is &lt;strong&gt;Svelte-only&lt;/strong&gt; on purpose. I would rather go deep on SvelteKit than be
a shallow cross-framework tool.&lt;/li&gt;
&lt;li&gt;The free grid is MIT-spirited and standalone. &lt;strong&gt;Studio and the enterprise
features&lt;/strong&gt; (Excel/PDF export, pivot, the AI stuff) are paid with a license
key - the AG-Grid model. I would rather say that up front than bury it.&lt;/li&gt;
&lt;li&gt;The designer is still button-driven; drag-drop is only partly there, and the
whole Studio layer is early.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The visual designer runs in the browser, no signup:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://svgrid.com/studio" rel="noopener noreferrer"&gt;https://svgrid.com/studio&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I would love feedback from two camps: Svelte folks on whether the generated code&lt;br&gt;
is something you would actually want to own, and anyone who has been burned by a&lt;br&gt;
hosted internal-tool builder on whether "generate code you keep" is the right&lt;br&gt;
trade or just relocates the pain.&lt;/p&gt;

&lt;p&gt;If you want to poke at the grid itself first, the examples gallery is at&lt;br&gt;
&lt;a href="https://svgrid.com/demos" rel="noopener noreferrer"&gt;https://svgrid.com/demos&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sveltekit</category>
      <category>svelte5</category>
      <category>svgrid</category>
      <category>svgridstudio</category>
    </item>
    <item>
      <title>SvGrid: a Svelte 5 native data grid (MIT core, headless + render component, MCP-ready)</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Sat, 20 Jun 2026 08:17:11 +0000</pubDate>
      <link>https://dev.to/bmarkov/svgrid-a-svelte-5-native-data-grid-mit-core-headless-render-component-mcp-ready-1j8l</link>
      <guid>https://dev.to/bmarkov/svgrid-a-svelte-5-native-data-grid-mit-core-headless-render-component-mcp-ready-1j8l</guid>
      <description>&lt;p&gt;The Svelte ecosystem has matured fast in the last year, but one thing I kept hitting was the data grid problem. If you wanted a real one, your options were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TanStack Table&lt;/strong&gt; (Svelte adapter). Headless only, multi-framework. You build the entire DOM layer yourself. Beautiful primitives, but you still ship a thousand lines before you have a sortable table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AG Grid via a Svelte wrapper.&lt;/strong&gt; Production-grade, but the React shape underneath fights Svelte 5 runes the moment you try anything reactive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;svelte-headless-table.&lt;/strong&gt; Lovely for small tables, not built for 100k rows with virtualization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of them felt right for the Svelte 5 era. So we spent the last year building one: &lt;strong&gt;SvGrid&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is MIT-licensed, on npm as &lt;code&gt;@svgrid/grid&lt;/code&gt;, and has a sibling package &lt;code&gt;@svgrid/enterprise&lt;/code&gt; for the heavier features (export, pivot, AI helpers). Site, docs, 150+ live demos: &lt;a href="https://svgrid.com" rel="noopener noreferrer"&gt;svgrid.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in 30 seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create sv-grid@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That scaffolds a working Vite + Svelte 5 app with a sortable, filterable, virtualized grid wired up.&lt;/p&gt;

&lt;p&gt;Or drop the package straight into an existing app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @svgrid/grid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The hello-world that actually does something
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SvGrid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ColumnDef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@svgrid/grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ada&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Linus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Grace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inactive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firstName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;First name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;SvGrid&lt;/span&gt; &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;columns=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a real, working, accessible grid. Sorting, filtering, cell-range selection, inline editing, virtualization all light up the moment you turn on the matching prop. No provider wrapping, no DOM you have to author yourself, no &lt;code&gt;.subscribe&lt;/code&gt; ceremony.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "Svelte 5 native" matters
&lt;/h2&gt;

&lt;p&gt;A data grid is, at its core, a reactivity problem. A hundred thousand cells, any of which can change, and you want to repaint only what actually moved. Svelte 5 runes (&lt;code&gt;$state&lt;/code&gt;, &lt;code&gt;$derived&lt;/code&gt;, &lt;code&gt;$effect&lt;/code&gt;) give you fine-grained reactivity at exactly that scope.&lt;/p&gt;

&lt;p&gt;Three patterns the engine uses that turned out to matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;$state.raw&lt;/code&gt; for large arrays.&lt;/strong&gt; Svelte's deep proxy is wonderful for forms and brutal for 100k-row tables. The row backing is &lt;code&gt;$state.raw&lt;/code&gt;; the array reference is reactive, the contents are not. Sort-and-filter on the dataset I was profiling went from ~80ms to ~6ms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;$effect.pre&lt;/code&gt; for measure-before-paint.&lt;/strong&gt; Virtualization needs the viewport height before the paint that uses it. With plain &lt;code&gt;$effect&lt;/code&gt; you get one frame of "rows render at 0 height, then snap to real height." &lt;code&gt;$effect.pre&lt;/code&gt; kills the flicker without a &lt;code&gt;requestAnimationFrame&lt;/code&gt; hack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Snippets as cell renderers.&lt;/strong&gt; Svelte 5 snippets are first-class values. You attach one to a &lt;code&gt;ColumnDef.cell&lt;/code&gt; and &lt;code&gt;{@render cell.cell(...)}&lt;/code&gt; from the body component, and the args (&lt;code&gt;value&lt;/code&gt;, &lt;code&gt;row&lt;/code&gt;, &lt;code&gt;getValue&lt;/code&gt;) typecheck the whole way through. No more named slots, no &lt;code&gt;&amp;lt;svelte:fragment&amp;gt;&lt;/code&gt; dance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the free MIT core (&lt;code&gt;@svgrid/grid&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Row + column virtualization.&lt;/strong&gt; 100k rows by 100 cols stays smooth. There's a 1M-row demo if you want to see it break later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtering.&lt;/strong&gt; Excel-style filter menu, inline filter row, locale-aware text matching, set / value-list filter, between operator on numbers and dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editing.&lt;/strong&gt; 14 built-in &lt;code&gt;editorType&lt;/code&gt;s (text, number, date, datetime, time, select, rich-select with typeahead, textarea, color, checkbox, list, chips, rating, password) and a &lt;code&gt;cellEditor&lt;/code&gt; snippet slot for anything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selection.&lt;/strong&gt; Cell-range click+drag and Shift+arrows, copy/paste as TSV, Excel-style fill handle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hierarchy.&lt;/strong&gt; Row grouping with aggregation, tree data, master/detail, full-width detail rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout.&lt;/strong&gt; Row + column pinning, sticky header + first column, header drag-to-reorder, column sizing API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations.&lt;/strong&gt; Find in grid (Ctrl+F), undo/redo (Ctrl+Z), transaction API, optimistic updates, server-side row model with sort / filter / group pushdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A11y.&lt;/strong&gt; WAI-ARIA + full keyboard nav, RTL, high-contrast theme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle.&lt;/strong&gt; ~7.5 KB gzipped for the headless core, ~42 KB gzipped for the full render component. Svelte stays a peer dependency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The community core has zero feature gating, no license key, no watermark, no row-count cap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI-native bit
&lt;/h2&gt;

&lt;p&gt;This is the part I am most excited about, and the part I think is underappreciated in the component-library space.&lt;/p&gt;

&lt;p&gt;SvGrid ships an &lt;strong&gt;MCP (Model Context Protocol) server&lt;/strong&gt;. Add it to your Claude Desktop, Cursor, or Zed config and the assistant gets accurate, version-pinned answers about every prop, method, and event in the library, plus the source of all 150+ demos as context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"svgrid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@svgrid/mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you ask Cursor "give me a grid with row grouping and server-side data," it grounds the answer in actual SvGrid types and emits code that compiles, instead of hallucinating ag-grid props.&lt;/p&gt;

&lt;p&gt;There is also a published &lt;code&gt;llms.txt&lt;/code&gt; / &lt;code&gt;llms-full.txt&lt;/code&gt; for retrieval-augmented setups, and the gallery has live AI demos (a natural-language filter bar that turns "EMEA over 50k active" into a typed filter expression, plus a Smart Paste that turns free-form text into typed rows).&lt;/p&gt;

&lt;h2&gt;
  
  
  Open core, upfront
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@svgrid/grid&lt;/code&gt; is MIT-licensed and free for commercial use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@svgrid/enterprise&lt;/code&gt; is a separately-licensed companion that adds export to Excel / PDF / CSV / TSV / HTML with branded headers, footers, and password protection; a paginated printable view; pivot tables with a drag-and-drop Designer + drill-through; and AI helpers. Per-developer pricing, perpetual license with an optional yearly renewal for updates, OSS projects get it for free.&lt;/p&gt;

&lt;p&gt;If everything you need is in the core, you never have to touch the paid package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest about what is not there yet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Column spanning (&lt;code&gt;colSpan&lt;/code&gt; on cell context). On the roadmap, large effort.&lt;/li&gt;
&lt;li&gt;Built-in row dragging across grids. Demos cover the in-grid case, but the cross-grid version is not in the engine yet.&lt;/li&gt;
&lt;li&gt;Variable row height on the &lt;code&gt;&amp;lt;SvGrid&amp;gt;&lt;/code&gt; render component. The headless virtualizer does it today.&lt;/li&gt;
&lt;li&gt;Engine-level formula language. There is a working in-grid formula engine in a demo, but it has not graduated into the package.&lt;/li&gt;
&lt;li&gt;Custom calendar systems (Hijri, Buddhist, fiscal year) for the date editor. Gregorian dates / times / datetimes are all built in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full public roadmap with effort tags and a "recently shipped" track record: &lt;a href="https://svgrid.com/roadmap" rel="noopener noreferrer"&gt;svgrid.com/roadmap&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Site + 150+ demos: &lt;a href="https://svgrid.com" rel="noopener noreferrer"&gt;svgrid.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sv-grid/sv-grid" rel="noopener noreferrer"&gt;github.com/sv-grid/sv-grid&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@svgrid/grid" rel="noopener noreferrer"&gt;@svgrid/grid&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Scaffold: &lt;code&gt;npm create sv-grid@latest&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would love feedback. What is the first thing that breaks when you try it, and what is the one missing feature that would stop you adopting it? I am reading every reply.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>showdev</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>New Smart UI for React with 60+ components</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Mon, 08 Jun 2020 11:53:06 +0000</pubDate>
      <link>https://dev.to/bmarkov/new-smart-ui-for-react-with-60-components-2903</link>
      <guid>https://dev.to/bmarkov/new-smart-ui-for-react-with-60-components-2903</guid>
      <description>&lt;p&gt;Demos: &lt;a href="https://www.htmlelements.com/react/" rel="noopener noreferrer"&gt;https://www.htmlelements.com/react/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>datagrid</category>
      <category>kanban</category>
    </item>
    <item>
      <title>First Gantt Chart Web Component</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Tue, 30 Jul 2019 18:26:31 +0000</pubDate>
      <link>https://dev.to/bmarkov/first-gantt-chart-web-component-42kf</link>
      <guid>https://dev.to/bmarkov/first-gantt-chart-web-component-42kf</guid>
      <description>&lt;p&gt;Ideal for project management:&lt;br&gt;
&lt;a href="https://www.htmlelements.com/demos/gantt/overview/index.htm"&gt;https://www.htmlelements.com/demos/gantt/overview/index.htm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gantt</category>
      <category>webcomponents</category>
      <category>ganttchart</category>
      <category>ganttcustomelement</category>
    </item>
    <item>
      <title>SMART HTML ELEMENTS Colors Element</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Thu, 23 May 2019 10:26:21 +0000</pubDate>
      <link>https://dev.to/bmarkov/smart-html-elements-colors-element-211c</link>
      <guid>https://dev.to/bmarkov/smart-html-elements-colors-element-211c</guid>
      <description>&lt;p&gt;Creates a Custom Element by using Smart Custom Elements library. Smart provides API for Web Components &amp;amp; Custom Elements development. You can use it to create your own HTML Tags.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/anon/embed/JqpWeb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>codepen</category>
      <category>colorpicker</category>
    </item>
    <item>
      <title>Feature Complete Chart Web Component </title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Wed, 08 May 2019 04:17:56 +0000</pubDate>
      <link>https://dev.to/bmarkov/feature-complete-chart-web-component-4ia3</link>
      <guid>https://dev.to/bmarkov/feature-complete-chart-web-component-4ia3</guid>
      <description>&lt;p&gt;Feature Complete Chart Web Component with SVG and HTML5 rendering. Pie, Donut, Bar, Column, Financial, Pyramid, Bubble, Scatter and 30 more Chart types.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.htmlelements.com/demos/chart/overview/"&gt;https://www.htmlelements.com/demos/chart/overview/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chart</category>
      <category>charting</category>
      <category>chartwebcomponent</category>
    </item>
    <item>
      <title>Awesome Web Components and HTML Elements</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Tue, 19 Feb 2019 08:46:26 +0000</pubDate>
      <link>https://dev.to/bmarkov/awesome-web-components-and-html-elements-573j</link>
      <guid>https://dev.to/bmarkov/awesome-web-components-and-html-elements-573j</guid>
      <description>

&lt;p&gt;Website: &lt;a href="https://www.htmlelements.com/demos/"&gt;https://www.htmlelements.com/demos/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JCUDOcp_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.htmlelements.com/demos/images/checkout-web-page-template.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JCUDOcp_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.htmlelements.com/demos/images/checkout-web-page-template.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0FJiJIXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.htmlelements.com/demos/images/dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0FJiJIXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.htmlelements.com/demos/images/dashboard.png"&gt;&lt;/a&gt;&lt;/p&gt;


</description>
      <category>webcomponents</category>
      <category>customelements</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Cute - CSS in JS: https://bmarkov.github.io/cute-css/</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Mon, 18 Feb 2019 05:57:25 +0000</pubDate>
      <link>https://dev.to/bmarkov/cute---css-in-js-3dmc</link>
      <guid>https://dev.to/bmarkov/cute---css-in-js-3dmc</guid>
      <description>

&lt;p&gt;Utilising tagged template literals and the power of CSS.&lt;br&gt;
Automatic CSS - adds css to a web page on demand, fully automatically. Your users load the least amount of code necessary.&lt;br&gt;
No class name bugs - unique class names for your styles. You never have to worry about duplication or misspellings.&lt;br&gt;
Removes the mapping between components and styles&lt;br&gt;
FREE&lt;/p&gt;


</description>
      <category>css</category>
      <category>javascript</category>
      <category>cssinjs</category>
    </item>
    <item>
      <title>Custom Elements: https://www.htmlelements.com/</title>
      <dc:creator>bmarkov</dc:creator>
      <pubDate>Mon, 18 Feb 2019 05:18:33 +0000</pubDate>
      <link>https://dev.to/bmarkov/custom-elements-httpswwwhtmlelementscom-2f10</link>
      <guid>https://dev.to/bmarkov/custom-elements-httpswwwhtmlelementscom-2f10</guid>
      <description>

&lt;p&gt;Vanilla JS and ES6 library and next-generation front-end framework: &lt;a href="https://www.htmlelements.com/"&gt;https://www.htmlelements.com/&lt;/a&gt;&lt;br&gt;
Ideal for the new Web Apps. Automatically adapts to different device types.&lt;br&gt;
0 dependencies. 0 learning curve.&lt;br&gt;
-Javascript library that helps you to create Custom UI Elements.&lt;br&gt;
-Integrates with Angular, ReactJS, Vue.js and any other framework.&lt;br&gt;
Arbitrary-precision arithmetic and Math functions.&lt;br&gt;
DateTime precision up to a yoctosecond (10⁻²⁴ second).&lt;/p&gt;


</description>
      <category>customelemements</category>
      <category>webcomponents</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
