Build and deploy apps with AI, on your machines.

Elements is the integrated app environment for building and deploying web apps. It's a project server, app framework and build tooling, engineered from the ground up to work together. The project server is always running, so your agent can check its work instead of guessing. Prototype quickly. Ship the same code to production.

Try it: a working app in seconds.

Install elements: curl -fsSL 'https://install.elements.dev?os=darwin' | sh && export PATH=~/elements/bin:$PATH Create an Elements app: cd ~/elements/projects elements create myapp cd myapp Start the app.

Your agent builds on Elements.

You bring the agent. Elements brings the project server, build tooling, app framework and deployment. The result is correct code the first time, and still correct a hundred agent sessions later. Keep building on your own machine, then deploy to commodity hardware in seconds. Nothing meters how much you build.

An Elements app, running on your machine.

An agent can build a complex app extremely quickly, because everything it needs is already here. Features like realtime pubsub, database migrations, sessions and authentication all ship with Elements.

Claude Code building an Elements app

The build state is ready before your agent asks.

The build has already run by the time your agent asks: type check, program analysis, migrations and tests. Your agent reads the diagnostics and corrects the code before reporting the work finished.

An agent reading two build errors and proposing the fixes

An always-on project server, to make your agent smarter.

One server per project. The first command starts it and everything after connects to the same one: elements build, elements test, elements install, and your editor over LSP. It holds the build state in memory, so checking costs microseconds instead of seconds, and your agent can check after every change rather than at the end.

The project server answers in microseconds.

Save a file and the rebuild and the full type check are already done, because the build state never left memory. Your agent asks the same question with -json and reads the answer without parsing a screen.

elements build
State: Ok
Elapsed: 0.000006s
j/k: down/up G/g: bottom/top y: copy q: quit
elements build -json
{
"path": "/Users/cmather/elements/projects/myapp",
"ok": true,
"errorCount": 0,
"warningCount": 0,
"message": "The build is ok.",
"buildGen": 15,
"buildRanAt": "2026-09-21T12:08:15.74752-07:00",
"started": "2026-09-21T12:09:41.121451-07:00",
"finished": "2026-09-21T12:09:41.121457-07:00",
"elapsedSeconds": 0.000006
}

A build is everything required to release the app.

Read the config, install packages, crawl, type check, run program analysis, emit, migrate the test database, run the tests, collect diagnostics, migrate the app database, release atomically, hot reload connected browsers.

One of the fastest package installers ever built.

Packages install as part of the build, automatically. It is npm compatible. Install one at version local and it resolves from your package search path, gets watched like the rest of the project, and ships on deploy. No monorepo, no file links.

elements install
State: Ok
Elapsed: 0.000857s
PACKAGES:
@elements/app @0.7.0
@elements/style @0.7.0
@types/node @22.20.3
tslib @2.8.1
5 root, 29 total packages
j/k: down/up G/g: bottom/top y: copy q: quit

Your database stays in sync automatically.

A migration is a .sql file. No ORM, no migration DSL, nothing to learn on top of sql you already know, which is also why an agent writes and edits them correctly, and why you can always run one directly against a database if you need to. Write one and it is applied on save, to the test database before the tests run and to the app database after. Nothing to remember, nothing to run.

elements db migrate
State: Ok
Elapsed: 0.035618s
MIGRATIONS:
node_modules/@elements/app/migrations/2026-07-11-000000-app-baseline.migration.sql [ok]
The application framework baseline migrations.
app/migrations/20260915155829-add-chat.migration.sql [ok]
add chat
j/k: down/up G/g: bottom/top y: copy q: quit

Tests are fast enough to be useful.

The test server runs several processes at once, so tests execute in parallel against the freshly migrated test database. Those processes take part in hot module reload, and a test is rerun when its dependencies change, so a typical test takes a few milliseconds. Fast enough that they stop being the thing you write to be compliant and start being how you find out what you broke.

elements test
State: Ok
Elapsed: 0.000014s
TESTS:
app/pages/home/test.ts [pass]
└─ home [pass]
├─ a fresh install has a channel to open on [pass]
├─ an anonymous visitor cannot create a channel [pass]
├─ a guest creates a channel too [pass]
├─ a signed in user creates a channel under a slug [pass]
├─ a channel name that slugifies to nothing is refused [pass]
└─ two channels cannot share a name [pass]
j/k: down/up G/g: bottom/top y: copy q: quit

Program analysis stops unsafe code.

A database call reachable from the browser does not compile, so it never ships. The report carries the fix and the path that got there.

elements build
State: Error
Elapsed: 0.000010s
ERRORS:
You have 1 error:
1. app/pages/home/template.ehtml:6:10
Security error: cannot call server code from the browser without going through an rpc
function.
Try:
Put the server code inside a function with an @rpc build tag. For example:
/**
* @rpc
*/
export function fn() {}
Then call fn().
5: function onSignIn(email: string) {
6: return sql(`select id from users where email = ${email}`).first();
7: }
Call stack:
2. app/pages/home/template.ehtml:6:10 sql
1. app/pages/home/template.ehtml:32:49 onSignIn
j/k: down/up G/g: bottom/top y: copy q: quit

Deploy to your own servers, with one command.

Any Ubuntu box you can ssh into. Elements builds the release, ships it, runs the migrations and hot reloads the app. No server restart, no dropped connections. A load balancer and TLS certificates come with every deploy machine.

elements deploy
State: Ok
Elapsed: 0.720184s

Peer to peer, and incremental.

A deploy is your project server talking directly to the one on each machine. Yours sends the source graph it already holds, theirs diffs it against the graph it already holds, and only what changed crosses the wire. Both ends are already hot. No queue behind a million other people's builds, no container image to assemble and push, and nothing rebuilt from scratch on the far side.

Load balancing on every machine.

Every machine runs its own load balancer and they find each other automatically. To grow the cluster, add the IP address to config.jsoc and deploy. Whichever machine takes a request balances it across the cluster, with failover to the ones still answering.

Automatic SSL on every machine.

Set a domain, point DNS at the machine, open 80 and 443. Elements issues the certificate through Let's Encrypt on the first request and renews it before it expires. Adding a domain or rotating a certificate takes effect without dropping connections.

Integrated Postgres.

Elements installs and manages Postgres on every machine, so a single-machine app has a database with nothing to set up. For a bigger app, or a cluster of several machines, point Elements at a hosted Postgres instead.

A completely integrated app framework that just works.

Everything a real application needs. No external packages required.

html
A reactive HTML language with typed template constructors. The first response is the rendered page. The browser attaches to it and patches only what changes.
rpc
Call a server function from the browser like any other function. The compiler turns the call into a request and keeps the body out of the browser bundle.
routes
Declared in TypeScript with a typed request and response. Return a page, or return data.
auth
Login, logout and authorization primitives are built in. Works in routes, rpc functions, and reactively in html templates.
sql
Write sql in your code. It is parameterized at build time, against the Postgres the install already gave you.
migrations
Applied by the build: the test database before the tests run, the app database after.
live tables
Real time publish and subscribe for your data, with optimistic mutations. Declare the set of rows a page needs and every browser subscribed to it stays current: insert, update or delete anywhere and the change arrives everywhere, painting locally before the server has answered.
channels
Broadcast from the server to every browser listening. Built on Postgres listen and notify.
jobs and cron
Queue work off the request path, or schedule it. A job server starts with the project, on your machine and on every machine you deploy to.
email
Write mail in the same html language as your pages. The compiler inlines the stylesheets so it renders in a mail client.
tests
Run across child node processes with hot reload on, against a freshly migrated test database, as a phase of every build.
app/pages/waitlist/template.ehtml
import { sql, session } from "@elements/app";
/** @rpc */
function joinWaitlist(email: string) {
let user = sql<{ id: string }>(
`insert into users (email, name) values (${email}, 'waitlist') returning id`,
).firstOrThrow("insert failed");
session.login({ userId: user.id, userName: email });
}
<html (private email: string = "", private joined: boolean = false)>
<body>
<form onsubmit={() => { joinWaitlist(email); joined = true; }}>
<input type="email" value={email} required>
<button type="submit">Join</button>
</form>
<p e:if={joined}>Thanks, {session.get("userName")}.</p>
</body>
</html>

The difference between an app that works the first time and an afternoon of telling your agent to try again.

It matters more the larger the app gets. Save any file in the project and it is checked against every page, every route and every test, so your agent can change a schema in a codebase nobody remembers and find out immediately that it broke something three files away. Nothing regresses quietly, and an app you shipped six months ago is still one you can change with confidence.

I've been building Elements for more than a decade. When I started, the plan was to create better tooling for people, to make building web apps dramatically simpler. It turns out the same tools also make agents insanely more productive. It's finally here. I'd love to hear from you: hello@elements.dev.

Chris Mather

The integrated app environment.

A project server, build tools, app framework and deployment, as one carefully engineered system. Built from the ground up to work together, on your own machines.

Install Elements