# Elements vs SvelteKit SvelteKit is an open-source web app framework for Svelte, made by the Svelte team. Svelte is a UI framework with its own compiler: you write components in `.svelte` files that combine a script, HTML-like markup and styles, and mark reactive state with [runes](https://svelte.dev/docs/svelte/what-are-runes) such as `$state` and `$derived`. SvelteKit adds [routing](https://svelte.dev/docs/kit/routing) based on folders and `+page` and `+layout` files, `load` functions that fetch a page's data, [form actions](https://svelte.dev/docs/kit/form-actions) that handle submissions, and rendering on the server, in the browser, or ahead of time as static files. It runs on Vite, and [adapters](https://svelte.dev/docs/kit/adapters) package the built app for a Node.js server, static hosting, Vercel, Netlify or Cloudflare. Elements builds the same kind of app. It is an integrated app environment, made for people and their agents: a project server that runs while you work, installing packages, running tests and migrations, and reporting every build error, with Postgres bundled and a deploy command for any Ubuntu server over SSH. It ships with a default framework that just works, with routing, server rendering and pages that update when their data changes, and with typed server functions, sessions, realtime data, background jobs and email. Pages are written in Elements HTML, plain HTML with reactive expressions that the build compiles and checks. The apps are TypeScript and run on Node.js. A SvelteKit app comes together from add-ons. Past routing and rendering, `sv add` brings in Drizzle and drizzle-kit for the database and migrations, Better Auth for sign-in, Vitest and Playwright for tests, and an adapter for the host, and background jobs and email are yours to find. Each is its own project with its own config file and its own API, and each seam is a place to break: a Better Auth setup whose tables have to match the Drizzle schema, or a type error that the dev server never reports and that surfaces only when `svelte-check` runs. Elements is ready out of the box. Postgres, migrations, sessions, tests, jobs, email, realtime and deploy are engineered as one coherent system, so you build productively from the first minute. Between you and the platform there is only a thin layer: pages are HTML and CSS, queries are SQL, server calls are TypeScript function calls, and the framework words to learn are `app.route()`, `session.login()`, `sql()`, `@rpc` and tests. ## At a Glance | | Elements | SvelteKit | |---|---|---| | What it is | A project server and a default framework, with Postgres, tests and deploy | A Svelte framework with routing and rendering, run on Vite | | Templates | [Elements HTML](/learn/man/html): standard HTML with typed reactive expressions | Svelte components, with runes marking reactive state | | Routing | [Declared in `index.ts`](/learn/man/router) with web standard `URLPattern` | Folders and `+page`, `+layout` and `+server` files | | Server functions | [`@rpc`](/learn/man/rpc): typed calls for reads and writes, built in | Remote functions, turned on by two config flags; form actions for POST | | Sign-in and sessions | [Built in](/learn/man/session), the same over HTTP and WebSockets | Not included; the CLI can set up Better Auth | | Database | [Postgres, bundled](/learn/man/database), with `sql()` built in | Not included; the CLI can add Drizzle | | Migrations | [Applied when you save them](/learn/man/migrations) | Not included | | Build errors | [One build](/learn/man/build) reports type errors, failing tests, migration errors and server code reachable from the browser | `svelte-check`, Vitest and Playwright, each run on its own | | Tests | [Part of the build](/learn/man/tests) | Not included; the CLI can add Vitest and Playwright | | Realtime | [LiveTable](/learn/man/livetable) and [channels](/learn/man/channel) | `query.live` streams values to one page; reaching other users is yours to build | | Background jobs and cron | [Built in](/learn/man/jobs) | Not included | | Email | [Built in](/learn/man/email); messages are Elements HTML templates | Not included | | Deploy | [`elements deploy`](/learn/man/deploy) to any Ubuntu server over SSH, usually under a second | An adapter packages the build; hosting and the pipeline are yours | ## One Environment, Not a Stack of Add-Ons Beyond routing and rendering, SvelteKit leaves each part of an app to an add-on. Its CLI, `sv`, [adds them to a project](https://svelte.dev/docs/cli/sv-add): Drizzle for the database, Better Auth for sign-in, Vitest and Playwright for tests, Tailwind for styles, and an adapter for the host. Each is a separate project with its own configuration, its own docs and its own API. For background jobs, email and realtime, there is no add-on at all. In Elements the database, sessions, the test runner, jobs, email and realtime ship together, and one build checks all the code that uses them. Styles are covered too: [`@elements/style`](/learn/man/style) is a design system of tokens, components and utilities, and `elements create` [sets it up in every new app](/learn/man/style/install). An add-on saves an agent time only when it does something the agent cannot write quickly from standards. Tailwind is a case in point: an agent writes plain CSS fluently, so a utility vocabulary is one more thing to look up. Every add-on past that line is another config file and another seam. Leaving the `sv add` catalog does not mean leaving npm: Stripe, the AWS SDK and other packages install unchanged. A framework still earns its keep. On a line from machine code to a package that subtracts two numbers, an agent wants the point in between where routing, typed server calls, sessions and a database driver are handled as plumbing, so the Elements app framework ships them as `app.route()`, `@rpc`, `session.login()` and `sql()`. Beyond that plumbing returns diminish, and each extra rune, block syntax or add-on API costs an agent more to learn and get right than it saves. There, the payoff is correction, knowing what it got wrong as soon as it gets it wrong, and in a stack of add-ons that answer is spread across `svelte-check`, Vitest and the dev server. Elements keeps the rest to HTML and CSS pages and TypeScript function calls, and spends its engineering on correction. One coherent system checks every template, function, query, test and migration, so every build error, from type checking, tests, migrations and program analysis, turns up in milliseconds. Its project server runs while you work and holds the build graph and build state in memory, so on every save the build state is updated almost instantly, and `elements build -json` answers agents and humans in microseconds ([build](/learn/man/build)). The editor, the terminal and every agent share that build, and server code has no `async` or `await` for an agent to forget. ## Templates Are Standard HTML, Not Svelte Components Svelte writes reactive pages as components in its own file format. Markup uses its own template syntax, `{#if}`, `{:else}`, `{#each}` and `{@render}`, and reactive values are declared with runes, keywords that only work in `.svelte`, `.svelte.js` and `.svelte.ts` files ([runes](https://svelte.dev/docs/svelte/what-are-runes)). Elements uses [Elements HTML](/learn/man/html): standard HTML tags, TypeScript expressions in `{}`, and a few `e:` attributes for conditions and loops. A page declares its data as typed attributes on its opening tag, and when that data changes, the page updates. Nothing marks a value as reactive. ```ehtml interface Task { title: string; } function addTask(tasks: Task[], form: { title: string }) { tasks.push({ title: form.title }); form.title = ""; }

Nothing to do.

``` The handler is a plain TypeScript function. Pushing onto the array adds the row, and clearing the field empties the input, because form inputs bind both ways by default. Where Svelte needs `$state` to know what to track, the Elements runtime works it out from the expressions and [patches only the markup](/learn/man/html/reactivity) that reads the changed value, after a server render that sends finished HTML. The build checks templates with the rest of the code, so a mistake in a template shows up in the same build report as any other, with no separate checker to run. ## Server Functions Are Built In, for Reads and Writes SvelteKit has several ways to reach the server: `load` functions for reading a page's data, form actions that take a POST from a `
`, and `+server` endpoints you call with `fetch`. Each has its own conventions and its own way of passing data back. Its typed server calls, [remote functions](https://svelte.dev/docs/kit/remote-functions), are off by default: an app turns them on with two flags in its config, and they add a fourth convention, `query`, `form`, `command` and `prerender` functions in `.remote.ts` files. In Elements it is a regular feature, with nothing to opt into. Mark a function [`@rpc`](/learn/man/rpc) and a page can call it for a read or a write, passing typed arguments and getting a typed result, live data included. The compiler swaps the call for a network request and leaves the body on the server. Where a remote function relies on file naming to stay server side, Elements checks each call: reaching `sql`, `tx` or `session.login` from browser code fails the build with "Security error: cannot call server code from the browser without going through an rpc function." ## The Database and Sign-In Ship With Elements A web app needs a database, and most need users who sign in. SvelteKit includes neither. For the database, the CLI adds Drizzle, an ORM with its own migration tool. For sign-in, the [authentication docs](https://svelte.dev/docs/kit/auth) point to the CLI's Better Auth setup, or to the Lucia guide for building session auth yourself. In place of Drizzle, Elements ships Postgres itself, configured on your laptop and on each server it deploys to. Queries are plain SQL in the [`sql()` function](/learn/man/database/sql); the compiler makes each `${value}` a bound parameter, which closes off injection. In place of drizzle-kit, a [migration](/learn/man/migrations) is a SQL file that takes effect as you save it, and at deploy time the pending ones apply together in one transaction or the release does not go out. In place of Better Auth, [sessions](/learn/man/session) come with the framework: route handlers, server functions and templates read the same session whether the request arrived over HTTP or a WebSocket, and a page showing the user's name redraws when they sign in. Sign-in is a short server function that checks a password hash inside Postgres and calls `session.login()`, as the [authentication recipe](/learn/man/recipes/authentication) shows. ## Realtime, Jobs and Email Are Built In Chat, live dashboards and shared lists need the page to update for every user watching it. SvelteKit's remote functions include `query.live`, an async generator that streams values to one page, and the docs' notification example re-queries the database every second in a loop ([remote functions](https://svelte.dev/docs/kit/remote-functions#query.live)). Getting one user's change to everyone else is left to the app, and SvelteKit has no WebSocket support ("[Native support for web sockets](https://github.com/sveltejs/kit/issues/1491)" is an open issue with 305 thumbs-up), so an app adds a third-party service or runs its own WebSocket server. Background jobs, scheduled tasks and sending email are not included either. Elements includes all three. A [LiveTable](/learn/man/livetable) keeps a query's rows current in each open tab: the person editing sees the change immediately while the server confirms it, and other viewers get that one row patched. For events that are not rows, a [channel](/learn/man/channel) pushes messages from the server to subscribed browsers. [Jobs](/learn/man/jobs) and cron schedules run on a worker process, with the queue in the same Postgres database, so a job enqueued inside a transaction commits with the rest of the write. [Email](/learn/man/email) templates are written in Elements HTML and sent through the SMTP provider you configure. ## Every Build Error Comes From One Build An agent needs to know what its change broke: a failing test, a type error, a migration that no longer applies, server code the browser can reach. In SvelteKit, those answers come from separate tools: Vitest and Playwright for tests, added with the CLI, and [`svelte-check`](https://svelte.dev/docs/cli/sv-check) for types, a command you run on its own or in CI. Vite's dev server does not type check ([Vite](https://vite.dev/guide/features#transpile-only)). Elements folds [tests](/learn/man/tests), [migrations](/learn/man/migrations), [type checking](/learn/man/typescript) and program analysis, such as server code reachable from the browser, into one build, built in milliseconds, so any red result is a red build. Only the tests whose code changed run again, and each one runs in a Postgres transaction that is rolled back afterward, leaving nothing to clean up. Where you would run `svelte-check`, Vitest and Playwright in turn, `elements build -json` reports every build error, from templates and code to migrations and tests, as one JSON result in microseconds, and the editor, the terminal and any agents running at once all read that same build. ## Adapters Package the App; Elements Deploys It SvelteKit [adapters](https://svelte.dev/docs/kit/adapters) are "small plugins that take the built app as input and generate output for deployment." The official ones target Vercel, Netlify, Cloudflare, static files and Node.js. With the [Node adapter](https://svelte.dev/docs/kit/adapter-node), you copy the build output, `package.json` and production dependencies to your server, start it with `node build`, and set the adapter's environment variables for the reverse proxy you put in front of it. Tests, migrations and rollback are steps you add to your own pipeline. Elements has no adapter step, and no build folder to copy. `elements deploy` targets [an Ubuntu machine you can SSH into](/learn/man/deploy), rented anywhere, and the project server on your laptop syncs with its twin on that machine, sending only what differs. Expect a few seconds on a fresh server and usually under a second after that. The pipeline you would script around `node build` is already in it: tests on each machine, migrations once on the first machine, and a halt that leaves the current release serving if anything fails. The reverse proxy goes away too, since Elements configures the load balancer, TLS certificates and Postgres itself. That local Postgres is meant for a prototype or one server; past that, or when you want managed backups and scaling, point `DB_HOST` at a hosted Postgres such as DigitalOcean Managed PostgreSQL or Amazon RDS ([setup](/learn/man/deploy/setup)). ## Who Pays for SvelteKit SvelteKit is an on-ramp to Vercel's metered hosting business. Vercel has funded full-time work on Svelte since 2021, writing at the time, "With Vercel's backing, Svelte can get even more ambitious" ([Vercel](https://vercel.com/blog/vercel-welcomes-rich-harris-creator-of-svelte)). Svelte also takes donations through [Open Collective](https://opencollective.com/svelte). What Vercel sells is metered hosting ([Vercel pricing](https://vercel.com/pricing), [Elements vs Vercel](/vs/vercel)), and Vercel is one of the five targets of SvelteKit's official adapters ([adapters](https://svelte.dev/docs/kit/adapters)). Elements is a company that sells its tooling directly, per machine, with nothing metered ([pricing](/pricing)), so what it earns from is the tooling itself. It charges for the tooling, not for the framework: the runtime packages your app depends on, `@elements/app` among them, are MIT licensed, and everything you build is yours ([licenses](/learn/man/licenses)). ## Moving From SvelteKit An agent can port a SvelteKit app easily. Svelte markup is already HTML with expressions in braces, so its components become [Elements HTML templates](/learn/man/html), and its routes and server code map onto `app.route()` lines and [`@rpc`](/learn/man/rpc) functions. You keep TypeScript and your npm packages, and in place of add-ons and an adapter you get sign-in, a database, realtime, jobs, email, tests and deploy, ready in one system. ## Questions ### What is the difference between Elements and SvelteKit? SvelteKit is an open-source framework for Svelte that handles routing, rendering and form handling, and leaves the database, sign-in, tests, migrations, jobs and email to add-ons, other libraries and services. Elements is an integrated app environment with a project server and a default framework. The Elements app framework handles routing, rendering and forms with sign-in, realtime, jobs and email ready in it, and the system around it comes with Postgres, migrations, tests and deploy already connected. A Svelte project reports problems through svelte-check, Vitest and the dev server; Elements reports them through one build. ### Is Elements a SvelteKit alternative? Yes. It replaces SvelteKit and the add-ons `sv add` would install, with nothing to wire together, and jobs, email and realtime, which have no add-on, come ready too. ### Can I use Svelte components in Elements? No. Pages are Elements HTML, which has typed expressions but no runes and no `{#if}` blocks. Both are HTML with expressions in braces, so an agent ports a Svelte component to an Elements HTML template directly. ### Does Elements do server-side rendering? Yes, on every request. Each page arrives as complete HTML for crawlers and new visitors, and it is also fully reactive in the browser, with no compromise. ### Can I use npm packages in Elements? Yes. Packages land in an ordinary node_modules folder, unmodified, and the build runs the install for you. ### How do I move a SvelteKit app to Elements? An agent can port a SvelteKit app easily. Svelte markup is already HTML with expressions in braces, so its components become Elements HTML templates, and its routes and server code map onto app.route() lines and @rpc functions. You keep TypeScript and your npm packages, and in place of add-ons and an adapter you get sign-in, a database, realtime, jobs, email, tests and deploy.