# Elements vs Astro
Astro is an open-source web framework for content-driven websites such as blogs, marketing sites and documentation ([why Astro](https://docs.astro.build/en/concepts/why-astro/)). It is free and MIT-licensed, and it is made by The Astro Technology Company, which [joined Cloudflare](https://blog.cloudflare.com/astro-joins-cloudflare/) in January 2026. Cloudflare sells hosting, including Workers, metered by requests and CPU time ([Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/)). Pages are `.astro` files: HTML with a component script at the top that runs on the server. By default, Astro prerenders each page to static HTML at build time and sends no JavaScript to the browser. Interactive parts are islands: components written in React, Vue, Svelte or another UI framework, each loaded in the browser with a `client:` directive. Astro routes by the files in `src/pages/`, loads Markdown and MDX through content collections, and with a server adapter can render pages on demand.
Elements also starts from HTML. It is an integrated app environment, built for people and their agents. At its center is a project server that runs while you work, and around it the same system includes a package installer, a test runner, a bundled Postgres database and a deploy command for any Ubuntu server you reach over SSH. It ships with a default framework that just works, whose pages are Elements HTML, standard HTML with reactive expressions, rendered on the server for every request. Typed server functions, sessions, realtime data, background jobs and email are part of it. Elements apps are written in TypeScript and run on Node.js.
Astro is a set of parts you assemble. Sign-in, a database, realtime updates, background jobs, email and tests are not in Astro, so each one is a library or a service you pick and connect, and every seam between them is a place the app breaks, for a person or an agent. Even rendering is a choice: static by default, and on demand only once you add an adapter for your host. Elements is ready out of the box: sessions, Postgres, jobs, email and tests are in place when `elements create` makes the app. It stays close to the metal, with HTML, CSS, TypeScript, SQL and function calls, and the framework vocabulary is the bare minimum: `app.route()`, `session.login()`, `sql()`, `@rpc` and tests. A blog, a marketing site and a product with accounts, payments and realtime are the same kind of app, built, tested and deployed by the same system.
## At a Glance
| | Elements | Astro |
|---|---|---|
| What it is | A project server, a default framework, Postgres, tests and deploy, shipped as one coherent system | A framework for content-driven websites, with adapters for each host |
| Business model | Sells the system you build with, per machine, with nothing metered ([pricing](/pricing)) | Owned by Cloudflare, which sells metered hosting ([Cloudflare](https://blog.cloudflare.com/astro-joins-cloudflare/), [Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/)) |
| Rendering | [On the server](/learn/man/html/server), with the page's data in the first response | Prerendered to static HTML by default; on demand only with an adapter |
| Interactive parts | Any template, written in [Elements HTML](/learn/man/html) like the rest of the page | Islands, each in a UI framework of its own, sharing state through a separate library |
| Content | Rows in Postgres, live on the next request | Markdown and MDX files through content collections; a prerendered page changes after a rebuild and redeploy |
| Routing | [Declared in code](/learn/man/router) with web standard `URLPattern` | Files in `src/pages/` |
| Server functions | [`@rpc`](/learn/man/rpc): import the function and call it, typed end to end | Actions, with input validated by Zod schemas |
| Sign-in and sessions | [Built in](/learn/man/session), the same over HTTP and WebSockets | No official sign-in; sessions need on-demand rendering and a storage driver |
| Database | [Postgres, bundled](/learn/man/database), with `sql()` and [migrations](/learn/man/migrations) | Not included; the docs recommend a third-party library |
| Payments | [Stripe Checkout](/learn/man/payments), set up with one secret key | Not included |
| Realtime | [LiveTable](/learn/man/livetable) and [channels](/learn/man/channel) | Not included |
| Background jobs and cron | [Built in](/learn/man/jobs) | Not included |
| Email | [Built in](/learn/man/email), written in Elements HTML | Not included |
| Tests | [Part of the build](/learn/man/tests) | Not included; the docs cover four outside tools |
| Build errors | [One build](/learn/man/build) reports type checking, tests, migrations and program analysis, as you work | `astro check`, a separate command the docs aim at CI; tests are a tool you add |
| Deploy | [`elements deploy`](/learn/man/deploy) to any Ubuntu server over SSH, often under a second | Static files to a host, or an adapter per host, each configured on its own |
## One Language for Every Part of a Page
Each interactive part of an Astro page is an island, and each island can be written in a different UI framework: a React search box, a Vue cart and a Svelte carousel can ship on the same page, and each framework loads its own runtime in the browser. That is one more decision per island, and one more seam: islands cannot share state through a framework's context providers, so Astro's docs send you to a separate library, Nano Stores, to pass it between them ([sharing state](https://docs.astro.build/en/recipes/sharing-state-islands/)).
In Elements, the static parts and the interactive parts of a page are written in the same language. [Elements HTML](/learn/man/html) is standard HTML plus TypeScript expressions in `{}` and a few `e:` attributes for conditions and loops, and any template can bind a form field, call the server or show live data. A search box that filters a list as you type is one template, with no island:
```ehtml
interface Post {
slug: string;
title: string;
}
function matches(post: Post, text: string): boolean {
return post.title.toLowerCase().includes(text.toLowerCase());
}
```
The input binds both ways by default, so typing updates `search.text` and the list updates with it. The page still renders on the server, with the full list in the first response.
## Agents Need Correction, Not Endless Abstractions
For an AI coding agent such as Claude Code or Codex, the value is not in endless framework abstractions or in a directory of integrations. It is in correction: finding out what it got wrong the moment it gets it wrong. Astro's pitch leans the other way, on its [integrations directory](https://astro.build/integrations/) and on islands in any UI framework. An integration helps an agent only when it does something the agent cannot write quickly from standards, and that set is small. To draw a search box, an agent would sooner write HTML and CSS than pick between React, Vue and Svelte and learn the island directives that load them, and a few lines of TypeScript beat adding a package. Every integration past that point is one more config entry, one more API to look up, and one more seam between parts that were not built together.
Some framework is worth having. No agent writes machine code to build a site, and none wants a package to subtract two numbers; between those ends is the plumbing every app needs, and the Elements app framework ships it: routing, typed server calls with `@rpc`, sessions, `sql()` and the database driver. Each abstraction past that costs an agent more to learn and get right than it saves, so past the plumbing Elements stays with standards: a page is HTML and CSS, an interactive part of it is the same HTML with TypeScript in braces, a server call is a TypeScript function call, and data is SQL through `sql()`.
The engineering goes into correction. The framework and its build were designed together, so one coherent system checks every page, server function, `sql()` call, migration and test the agent writes, and `elements build -json` reports each mistake in microseconds, because the project server already holds the build state. Your terminal, your editor and each agent read that same build. Server code is written without `async` or `await` ([async](/learn/man/async)), so no agent can forget an `await`.
## Content Goes Live Without a Rebuild
A prerendered Astro page changes only when the site is rebuilt and redeployed. Content that has to change between builds needs pages rendered on demand, which takes a [server adapter](https://docs.astro.build/en/guides/on-demand-rendering/) for your host, or content loaded at request time.
In Elements, content is data in Postgres, and every page [renders on the server](/learn/man/html/server) with its data already in the first response. Search engines and first-time visitors get complete HTML, and a page with no event handlers works with JavaScript turned off ([marketing page](/learn/man/recipes/marketing-page)). The [markdown blog recipe](/learn/man/recipes/markdown-blog) stores posts as rows, renders their Markdown to HTML on the server with an npm package, and gives admins an editor page in the same app. A draft is a row with no publish date. Publishing sets one, and the post is live on the next request, with no rebuild and no deploy. The same tables feed search, feeds and anything else the app builds on them.
## The Site Grows Into the Product
A site rarely stays a site. A marketing page gains a signup form, then paid plans, then a dashboard, notifications and a nightly report. In Astro, each of those is a decision to make and a part to connect before it works:
- **Sign-in:** "There is no official authentication solution for Astro," say its [docs](https://docs.astro.build/en/guides/authentication/), which send you to the integrations directory and walk through four options: Better Auth, Clerk, Lucia and Scalekit.
- **Sessions:** only for pages rendered on demand, and they need a storage driver. The Node, Cloudflare and Netlify adapters set a default one, and every other adapter needs one configured ([sessions](https://docs.astro.build/en/guides/sessions/)).
- **Database:** none of its own. The [Astro DB](https://docs.astro.build/en/guides/astro-db/) page now recommends "migrating to a third-party library", which is one more client, one more schema tool and one more service to provision.
- **Payments, realtime, background jobs and email:** not part of Astro.
- **Tests:** not part of Astro. The [testing guide](https://docs.astro.build/en/guides/testing/) covers setting up Vitest, Playwright, Cypress or Nightwatch.
In Elements, each of those is part of the framework. [Sessions](/learn/man/session) work in route handlers, server functions and templates, and password sign-in is a short server function that checks a bcrypt hash in Postgres ([authentication](/learn/man/recipes/authentication)). Card payments go through Stripe Checkout, and your part of the setup is one Stripe secret key ([payments](/learn/man/payments)). A [LiveTable](/learn/man/livetable) keeps a set of database rows in sync in every browser showing it. [Jobs and cron](/learn/man/jobs) run from a queue in the same Postgres database, and [email](/learn/man/email) is written in the same html language as pages and sent through any SMTP provider you configure.
The marketing page and the dashboard share one layout, one stylesheet, one set of types and one deploy.
## One Answer for Every Check
Astro's `astro dev --background` runs the dev server in the background with JSON logs, and starts it that way automatically when it detects an AI agent on macOS or Linux. The dev server does no type checking. `astro check` does that separately, and the [CLI reference](https://docs.astro.build/en/reference/cli-reference/) says it "is intended to be used in CI workflows." Tests are a separate tool you choose, configure and run yourself. A build can also pass when content is broken: "[`glob()` loader seems to swallow Markdown rendering errors and lets astro build exit successfully](https://github.com/withastro/astro/issues/18054)" is open, marked "P4: important."
Elements gives you every check in one answer, as you work. A project server runs while you work and holds the build graph and build state in memory. On every save, the build state is updated almost instantly, and it builds in milliseconds and answers agents and humans in microseconds ([build](/learn/man/build)). Ask `elements build -json` and the reply covers every build error at once, type checking, tests, migrations and program analysis, for code and templates alike, whether the asker is your editor, your terminal or one of several agents.
## Deploys Often Finish in Under a Second
A static Astro site is rebuilt with `astro build` and its output uploaded to a host. A site with pages rendered on demand needs an adapter for that host, its database is another service you provision, and tests are a step you add to your own CI. The adapter is a seam of its own. "[@astrojs/vercel: the /_astro/* immutable cache rule sits after { "handle": "filesystem" }, so hashed assets are served with max-age=0](https://github.com/withastro/astro/issues/18160)" is open: on a Vercel deployment with server output, the adapter's cache rule never runs, and content-hashed files go out with `max-age=0`.
With Elements, there is no static upload versus adapter decision, and no adapter to match to a host. You rent [an Ubuntu server from any provider and point `elements deploy` at it over SSH](/learn/man/deploy). Your local project server and the one on that machine work out which files differ and move only those, so a new server takes a few seconds and a routine deploy often less than one. Tests run on each machine before the switch and migrations run once; if either fails, visitors keep getting the release already up. Elements installs Postgres on the server, with the load balancer and TLS certificates. That suits a prototype or a site on one machine. Past that, or for managed backups and scaling, set `DB_HOST` to a hosted Postgres such as DigitalOcean Managed PostgreSQL or Amazon RDS ([setup](/learn/man/deploy/setup)).
## Who Pays for Astro
Astro is an on-ramp to the business of the company that pays for it, and both companies that have paid for it steered the framework toward what they sell. The Astro Technology Company was venture-backed from its start in 2022 ([announcement](https://astro.build/blog/the-astro-technology-company/)). In March 2024 it launched Astro DB with Astro Studio, a hosted database platform ([Astro DB](https://astro.build/blog/astro-db/)). Six months later it shut Studio down, writing that its goal "to build this into a profitable business to support the continued development and growth of Astro" had failed. Studio databases became inaccessible on March 1, 2025, and were deleted after that ([Goodbye Studio](https://astro.build/blog/goodbye-astro-studio/)). The Astro DB integration that remained was then deprecated and removed from the framework.
In January 2026, the company joined Cloudflare, and its full-time employees became Cloudflare employees ([Cloudflare](https://blog.cloudflare.com/astro-joins-cloudflare/)). Cloudflare sells hosting, metered by requests and CPU time ([Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/)). Cloudflare says Astro will deploy anywhere. Astro 6 rebuilt the Cloudflare adapter so development, prerendering and production all run on Cloudflare's runtime, with the stated goal of making "Cloudflare a first-class Astro runtime" ([Astro 6.0](https://astro.build/blog/astro-6/)).
Elements earns from one thing, its tooling, sold per machine with nothing metered ([pricing](/pricing)), and you deploy to any server you choose. The framework is not on the invoice: `@elements/app` and its sibling runtime packages carry the MIT license, and every site and app built on them belongs to you ([licenses](/learn/man/licenses)).
## Moving From Astro
An agent can port an Astro site easily, because its pages, components and server code map onto the Elements app framework's [routes](/learn/man/router), [HTML templates](/learn/man/html) and [`@rpc`](/learn/man/rpc) functions. You keep TypeScript, your Markdown and your npm packages, and you gain sign-in, a database, payments, tests and deploy in the same app, so the site grows into a product.
## Questions
### What is the difference between Elements and Astro?
Astro is an open-source framework for content-driven websites: pages are prerendered to static HTML by default, interactive islands are written in other UI frameworks, and sign-in, a database, tests and hosting are parts you choose and connect. Elements is an integrated app environment that is ready out of the box: a project server, a default framework with sessions, payments, realtime and jobs, a bundled Postgres database, a test runner and a deploy command, so a site grows into a product in one app.
### Is Elements an Astro alternative?
Yes. Elements renders every page on the server, and the same app has sign-in, a database, payments, realtime and background jobs, so a site grows into a product without a second framework, build or deploy.
### Can I use React, Vue or Svelte components in Elements?
No. Every part of an Elements page, static or interactive, is written in Elements HTML, standard HTML with typed reactive expressions. There is one runtime and one state model, with no islands to coordinate and no state library to add between them.
### Who owns Astro?
Cloudflare. The Astro Technology Company, which makes Astro, joined Cloudflare in January 2026, and its full-time employees now work for Cloudflare ([Cloudflare](https://blog.cloudflare.com/astro-joins-cloudflare/)). Cloudflare earns its revenue from hosting and network services. Elements is sold directly, per machine, so its revenue comes from the tooling itself ([pricing](/pricing)).
### Can Elements build a blog or a marketing site?
Yes. Every page renders on the server with its data in the first response, and a page with no event handlers works with JavaScript turned off. The manual includes marketing page and markdown blog recipes, and a post saved to Postgres is live on the next request, with no rebuild.
### How do I move an Astro site to Elements?
An agent can port it easily, because its pages, components and server code map onto the Elements app framework's routes, HTML templates and @rpc functions. You keep TypeScript, your Markdown and your npm packages, and you gain sign-in, a database, payments, tests and deploy in the same app.