<!-- https://svgrid.com/docs/enterprise/studio/code-generation/ - SvGrid documentation as markdown. Index of every page: https://svgrid.com/llms.txt -->

# Code generation

Studio generates plain SvelteKit code you own - there is no runtime and no
lock-in. The same `scaffold()` core powers the [CLI](https://svgrid.com/docs/enterprise/studio/cli/), the
[AI generator](https://svgrid.com/docs/enterprise/studio/ai-generation/), and the [visual designer](https://svgrid.com/docs/enterprise/studio/app-designer/), so
all three emit identical files.

![The generated files: schema module, +server.ts API route, and +page.svelte screen, with svgrid:managed markers.](https://svgrid.com/docs-media/studio-generated-code.png)

## What gets generated

From one `EntitySchema`, three files:

| File | Contents |
| --- | --- |
| `src/lib/<name>.schema.ts` | The `EntitySchema` literal + a typed row type. |
| `src/routes/api/<name>/+server.ts` | The API route - `createKitHandlers` over a data source. |
| `src/routes/<name>/+page.svelte` | The screen - grid + edit panel, wired to the route. |

The generated page is a full data screen: server-side sort, filter, and global
search; a native pagination footer; a validated create / edit modal;
multi-select optimistic delete; and loading / error / empty states. Any
`relation` (foreign-key) field also gets a searchable **lookup** wired to the
related entity's API route ([Relations](https://svgrid.com/docs/enterprise/studio/relations/)).

## The three files, up close

**1. The schema** is the single source of truth - a plain literal you can edit by
hand or regenerate:

```ts
// src/lib/customers.schema.ts
import type { EntitySchema } from '@svgrid/enterprise'

export type CustomersRow = {
  id: string; name: string; email: string; mrr: number; active: boolean
}

export const customersSchema: EntitySchema<CustomersRow> = {
  name: 'customers',
  idField: 'id',
  fields: [
    { field: 'id', type: 'text', primaryKey: true, readonly: true },
    { field: 'name', type: 'text', required: true },
    { field: 'email', type: 'text', format: 'email', required: true },
    { field: 'mrr', type: 'number', min: 0 },
    { field: 'active', type: 'boolean' },
  ],
}
```

**2. The API route** is a single `POST` endpoint. `createKitHandlers` speaks one
JSON wire protocol for read + create + update + delete, so the client needs just
the one handler:

```ts
// src/routes/api/customers/+server.ts
import { createInMemoryDataSource, createKitHandlers } from '@svgrid/enterprise'
import { customersSchema, type CustomersRow } from '$lib/customers.schema'

const source = createInMemoryDataSource<CustomersRow>([], customersSchema)

export const { POST } = createKitHandlers({ schema: customersSchema, source })
```

Swap `createInMemoryDataSource` for a SQL, Supabase, or REST source and the page
does not change - that is the [ServerDataSource](https://svgrid.com/docs/enterprise/studio/data-binding/) contract at
work.

**3. The screen** wires the grid and the edit panel to that route through the
controller - server-side sort / filter / page, the create-edit modal, and
optimistic delete. It is a normal `+page.svelte` you own; see the
[Studio live SQL demo](https://svgrid.com/demos/193-studio-live-sql/) for the
whole thing running.

## Managed regions & safe regeneration

Every generated file wraps its body in markers:

```ts
// svgrid:managed:start
// Regenerated by SvGrid Studio. Edits inside these markers are overwritten.
...generated code...
// svgrid:managed:end
```

Re-running generation replaces **only** the managed region and preserves
everything outside it. So you can:

- Add imports, helpers, and layout **outside** the markers - they survive
  regeneration.
- Re-run `add` after a schema or database change to pull in new columns without
  losing your customizations.

This idempotent regeneration is what makes the generator safe to keep using,
rather than a one-shot scaffold you fork away from.

## Choosing the data source

The generated `+server.ts` backend depends on how you scaffold:

| Scaffold | Backend |
| --- | --- |
| `--db <dialect>` | Connected to that driver via `process.env.DATABASE_URL`. |
| `--sql` | `createSqlDataSource` with an `execute()` stub to fill in. |
| `--from` (no `--db`) | In-memory, so the screen runs immediately. |

See [Databases](https://svgrid.com/docs/enterprise/studio/databases/) and [Data binding](https://svgrid.com/docs/enterprise/studio/data-binding/).

## Render mode: SPA or SSR per screen

An **`ssr`** screen emits idiomatic server-rendered SvelteKit: a
`+page.server.ts` with a `load` function and form `actions`, URL-driven sort /
filter / page state, and progressive enhancement. A **`spa`** screen emits a
client page where the browser talks to the API route through the data-source
controller.

**New apps get `ssr` wherever a screen qualifies.** When you generate an app -
`svgrid-studio init`, the designer's **New app** wizard, or `crudAppFromSchemas` -
every qualifying screen on a SQL, REST, or in-memory entity starts in `ssr`. You
can still switch any screen either way in the [app designer](https://svgrid.com/docs/enterprise/studio/app-designer/),
and a screen you set to `spa` stays `spa`.

In-memory data is held once, on the server. The seeded rows live in
`src/lib/server/store.ts`, and every screen reaches them through the entity's
`/api/<entity>` route, the same way a SQL entity does. So a server-rendered screen
and a client screen read and write the same rows, and the route's validation,
access rules, and triggers apply to both. See
[In-memory](https://svgrid.com/docs/enterprise/studio/in-memory/#in-a-studio-app) for what that means in a deployed app.

PGlite apps stay `spa`: PGlite keeps its rows in the browser's IndexedDB, which
the server cannot read.

### How the app is wired

Once an app has at least one server-rendered screen, the root `src/routes/+layout.ts`
leaves SvelteKit's own default in place - server rendering on - and each
client-only screen opts out in its own `+page.ts`:

```ts
// src/routes/<screen>/+page.ts
export const ssr = false
```

So the nav shell, the home page, and the sign-in pages all render on the server,
and only the screens that fetch in the browser skip it. An app with nothing to
server-render keeps the single `export const ssr = false` in the root layout, as
before.

Not every screen shape can emit as SSR. The rules:

- The screen must be entity-bound, without a [code-behind](https://svgrid.com/docs/enterprise/studio/code-behind/)
  companion.
- Its data source must be `memory` or `sql` (both reuse the entity's `/api` route
  via `event.fetch`), or `rest` **on an absolute URL** (the server
  calls the remote API directly; a relative URL has no origin to resolve against
  there, so it stays SPA). `supabase` and `pglite` screens stay SPA - PGlite only
  exists in the browser, and a Supabase read carries the signed-in user's token,
  which a server-side call with the anon key would silently drop.
- **A grid, optionally with a facet panel**, emits as load + form actions (no
  tree data, no scheduler view). The facet panel becomes a plain `GET` form
  whose controls are named for the URL params the `load` reads, so filtering
  works with JavaScript off and every filtered view has a shareable URL.
- **Read-only block screens** - any mix of chart, pivot, dashboard, KPI, gauge,
  tree, detail, and master-detail - emit as a load-only page.
- Anything else (boards, calendars, UI component blocks, containers,
  grids with extras) stays SPA.

The designer only offers the toggle when the screen qualifies; a screen set to
`ssr` that stops qualifying falls back to the SPA emit.

## Verification

The AI path compiles the generated page (via the Svelte compiler) before handing
it back, and the recommended final step everywhere is your project's own check:

```bash
npx svelte-check
```

## See also

- [The Studio CLI](https://svgrid.com/docs/enterprise/studio/cli/) · [AI generation](https://svgrid.com/docs/enterprise/studio/ai-generation/) · [Visual app designer](https://svgrid.com/docs/enterprise/studio/app-designer/)

---

SvGrid is the Svelte 5 data grid (`npm install @svgrid/grid`, MIT). This page: https://svgrid.com/docs/enterprise/studio/code-generation/ . All docs: https://svgrid.com/llms.txt
