SvGrid in Lovable

Lovable builds React apps, so SvGrid goes in through @svgrid/grid-wc, the web-component build of the grid with a React wrapper. It is the same grid as the Svelte package, compiled into a custom element, so the React app needs no Svelte toolchain.

One file does the integration: DataGrid.tsx, which loads the grid in the browser only. Lovable's default template since May 2026 is TanStack Start, which renders pages on the server, and a custom element has nothing to render there. With DataGrid in place, everything else is ordinary props.

The Lovable starter in dark mode: summary cards, grouping buttons with By status active, and the grid grouped into New, Refunded, Paid and Shipped rows with row counts, quantity and amount totals, in the app's own neutral shadcn palette.

Start from a prompt

Lovable's Build with URL opens a new project with a prompt already in the chat box. It waits for you to press send. This prompt carries the DataGrid file and the theme block verbatim, so the agent does not have to invent either:

Build an orders admin page with a data grid.

Use SvGrid through the npm package @svgrid/grid-wc; its React wrapper is @svgrid/grid-wc/react. Do not install @svgrid/grid, which is the Svelte version.

1. Create src/components/DataGrid.tsx with exactly this code. It loads the grid in the browser only, because the grid is a custom element and this app renders on the server:

import { forwardRef, lazy, Suspense, useEffect, useState, type CSSProperties } from 'react'
import type { SvGridHandle, SvGridProps } from '@svgrid/grid-wc/react'

const SvGrid = lazy(() => import('@svgrid/grid-wc/react').then((m) => ({ default: m.SvGrid })))

export type DataGridProps = Omit<SvGridProps, 'className' | 'style'> & {
  height?: CSSProperties['height']
  className?: string
  style?: CSSProperties
}

export const DataGrid = forwardRef<SvGridHandle, DataGridProps>(function DataGrid(
  { height = 480, className, style, ...props },
  ref,
) {
  const [mounted, setMounted] = useState(false)
  useEffect(() => setMounted(true), [])
  return (
    <div className={`svgrid-theme ${className ?? ''}`.trim()} style={{ height, ...style }}>
      {mounted && (
        <Suspense fallback={null}>
          <SvGrid ref={ref} {...props} />
        </Suspense>
      )}
    </div>
  )
})

2. Never import @svgrid/grid-wc anywhere except DataGrid.tsx.

3. Add this to the main CSS file, after the shadcn variables, so the grid uses the app's colors and dark mode. If the variables hold bare HSL parts (0 0% 100%), wrap each as hsl(var(--background)):

.svgrid-theme {
  --sg-bg: var(--background);
  --sg-fg: var(--foreground);
  --sg-muted: var(--muted-foreground);
  --sg-border: var(--border);
  --sg-header-bg: var(--muted);
  --sg-header-fg: var(--muted-foreground);
  --sg-bg-subtle: var(--muted);
  --sg-muted-bg: var(--muted);
  --sg-row-alt-bg: var(--background);
  --sg-row-hover-bg: var(--accent);
  --sg-selection-bg: var(--accent);
  --sg-input-bg: var(--background);
  --sg-input-border: var(--input);
  --sg-accent: var(--primary);
  --sg-on-accent: var(--primary-foreground);
  --sg-focus-ring: var(--ring);
  --sg-radius: calc(var(--radius) - 2px);
}

4. Build an Orders page with 200 sample orders (id, customer, region, status: New/Paid/Shipped/Refunded, quantity, amount, orderedOn, priority). Render:
<DataGrid height={560} data={orders} columns={columns} getRowId={(row) => row.id} sortable filterable editable pageable pageSize={25} showRowSelection groupable />
Columns look like { field: 'amount', header: 'Amount', width: 130, align: 'right', editorType: 'number', format: { type: 'currency', currency: 'USD' } }. Region and status use editorType 'select' with editorOptions; priority uses editorType 'checkbox'.

5. The grid never changes the data array. In onCellValueChange(e) update state with e.row, e.columnId and e.newValue. Show the total amount of the selected rows in a card above the grid; onRowSelectionChange(e) gives e.rows.

Docs written for agents: https://svgrid.com/docs/help/lovable.md

Open this prompt in Lovable

What Lovable writes from a prompt varies from run to run. The starter below is the tested reference: the same screen, with every file in place.

Try the starter without Lovable

Open the starter in StackBlitz ยท Source on GitHub

Lovable cannot import a GitHub repository, so the starter is a plain React + Vite app you can run anywhere. It is laid out the way a Lovable project is (src/components, src/lib, shadcn variables in src/index.css); the two pieces to carry into Lovable are src/components/DataGrid.tsx and the .svgrid-theme block.

The React starter in light mode: Orders heading, cards for 240 orders, revenue and three selected rows, grouping buttons, and the grid with three rows ticked, the edited Amount cell focused and a pager reading 1 to 25 of 240.

Why the grid waits for the browser

On the server, TanStack Start renders DataGrid as an empty box of the grid's height and no grid code runs. In the browser React hydrates the same box, an effect marks the component mounted, it lazily imports @svgrid/grid-wc/react, which registers the sv-grid element, and the grid mounts with its props, themed through the svgrid-theme class.

The file, with its comments:

import { forwardRef, lazy, Suspense, useEffect, useState, type CSSProperties } from 'react'
import type { SvGridHandle, SvGridProps } from '@svgrid/grid-wc/react'

// Loaded on first render in the browser, never on the server.
const SvGrid = lazy(() => import('@svgrid/grid-wc/react').then((m) => ({ default: m.SvGrid })))

export type DataGridProps = Omit<SvGridProps, 'className' | 'style'> & {
  /** The grid fills this box, so the box needs a height. Default 480. */
  height?: CSSProperties['height']
  className?: string
  style?: CSSProperties
}

export const DataGrid = forwardRef<SvGridHandle, DataGridProps>(function DataGrid(
  { height = 480, className, style, ...props },
  ref,
) {
  // false on the server and on the first client render, so both emit the
  // same empty box: no hydration mismatch, no layout jump.
  const [mounted, setMounted] = useState(false)
  useEffect(() => setMounted(true), [])

  return (
    <div className={`svgrid-theme ${className ?? ''}`.trim()} style={{ height, ...style }}>
      {mounted && (
        <Suspense fallback={null}>
          <SvGrid ref={ref} {...props} />
        </Suspense>
      )}
    </div>
  )
})

Three things it settles:

Match the app's theme

The grid draws with --sg-* CSS custom properties. Lovable apps keep their palette in shadcn variables (--background, --border, --primary), with dark mode as a .dark class on <html>. The .svgrid-theme block in the prompt points one set at the other, so the grid follows the app's colors and switches with its dark mode; DataGrid puts the class on its box.

The block assumes variables that hold whole colors, such as --background: oklch(1 0 0). Older shadcn projects store bare HSL parts, --background: 0 0% 100%; there, write each value as hsl(var(--background)).

To use one of the grid's own palettes instead, drop the block and set the tokens yourself; Theme and density lists them.

Ground the agent

Project knowledge

Project settings, Knowledge. Project knowledge holds up to 10,000 characters; this is well under:

## Tables and data grids

This project uses SvGrid for tables and data grids, through `@svgrid/grid-wc`
(MIT), the web-component build with a React wrapper. For any table that sorts,
filters, edits, pages, groups or holds many rows, render `<DataGrid>` from
`src/components/DataGrid.tsx` instead of writing a table component.

- Never import `@svgrid/grid-wc` or `@svgrid/grid-wc/react` anywhere except
  `DataGrid.tsx`. The grid is a custom element; `DataGrid` loads it in the
  browser only, which keeps server rendering (TanStack Start, Next.js) working.
- Do not install `@svgrid/grid`. It is the Svelte component; this is a React app.
- Features are boolean props, all off by default:
  `<DataGrid data={rows} columns={columns} sortable filterable editable pageable height={520} />`.
- Columns: `{ field, header, width, align, editorType, editorOptions, format, aggregate }`.
  `editorType` is `'text' | 'number' | 'date' | 'select' | 'checkbox'`;
  `format` is for example `{ type: 'currency', currency: 'USD' }`.
- The grid does not write into `data`. Apply an edit in `onCellValueChange`
  (`e.row`, `e.columnId`, `e.newValue`) and save it there.
- Selected rows arrive in `onRowSelectionChange` as `e.rows` (with `showRowSelection`).
- `pageSize` is the initial page size, read once when the grid mounts.
- A cell cannot render a React component. Use column `format` for numbers,
  currency and dates.
- Colors come from the `--sg-*` custom properties. The `.svgrid-theme` block
  in `src/index.css` points them at the shadcn variables, so the grid follows
  the app's theme and `.dark`. Do not restyle the grid with Tailwind classes.
- Docs written for agents: https://svgrid.com/llms.txt and
  https://svgrid.com/docs/help/web-components/react.md

It is the starter's AGENTS.md from the second heading down. Workspace knowledge takes the same text if every project in the workspace should know it.

The MCP server as a chat connector

Connectors, Add custom, MCP server:

Field Value
Server name svgrid
Server URL https://mcp.svgrid.com/mcp
Authentication No authentication

Lovable offers custom MCP servers on all plans. A chat connector feeds the agent while it builds and is never part of the published app. The server is public and read-only; its search, get_api_reference and get_example_source tools answer the agent's API questions, and check_svgrid_code checks a snippet. The examples it returns are Svelte, so the knowledge block above is what keeps the agent on DataGrid.

Troubleshooting

customElements is not defined during server rendering. Something imports @svgrid/grid-wc outside DataGrid.tsx, usually a route file the agent edited. Move the usage to <DataGrid>.

The grid area stays empty. Give DataGrid a height. Inside a flex column, the box also needs min-height: 0 to shrink.

Columns show [object Object] or the grid is empty with no error. The code renders the raw <sv-grid> element in React 18 or earlier, which sets arrays as attributes. Use DataGrid; the wrapper assigns them as properties.

pageSize is ignored and the grid shows 10 rows a page. A bug in the React wrapper of @svgrid/grid-wc 3.0.35 and earlier, which assigned props after the grid had mounted. Update the package.

The agent installed @svgrid/grid. That is the Svelte component and will not render in React. Remove it and keep @svgrid/grid-wc; the knowledge block prevents a repeat.

Cells need a button or a badge. A cell cannot render a React component. Column format covers numbers, currency and dates; for more, see what crosses the boundary.

Frequently asked questions

Can I use a data grid component in Lovable?

Yes. Install @svgrid/grid-wc and render it through a DataGrid component that loads the grid in the browser only. Sorting, filtering, inline editing, row selection, grouping and paging are boolean props.

Does SvGrid work with TanStack Start server rendering?

Yes, through DataGrid. The server renders an empty box with the grid's height and the browser loads the grid after hydration. Importing @svgrid/grid-wc at the top of a route file throws customElements is not defined on the server in versions 3.0.35 and earlier.

Will the grid match my Lovable app's shadcn theme?

Yes. The .svgrid-theme block points the grid's --sg-* tokens at the shadcn variables, so it follows the app's palette and its .dark mode.

Is it free?

@svgrid/grid-wc is MIT and needs no key. Excel and PDF export, import and pivot are in the paid @svgrid/enterprise package, which also runs from React.

See also

Related articles

  • Using SvGrid with Cursor, Zed, and Windsurf - Wire the SvGrid MCP server into Cursor, Zed, Windsurf, or Claude so AI assistants generate grid code that compiles on the first try - no invented props, no wrong import names.
  • SvGrid vs TanStack Table - A Deep Dive - A concrete architectural comparison of SvGrid and TanStack Table's Svelte adapter - how each handles reactivity, rendering, and feature composition, with code that shows exactly where they diverge.
  • SvGrid vs svelte-headless-table - A practical comparison of SvGrid and svelte-headless-table covering reactivity model, rendering approach, feature scope, and when each is the right choice for a Svelte 5 project.