Elements vs Nuxt
Nuxt is an open-source full-stack web framework for Vue.js, developed largely by a team that NuxtLabs funded and employed, until NuxtLabs joined Vercel in July 2025. You write pages as Vue single-file components, and Nuxt renders them on the server and makes them interactive in the browser. It adds routing based on the files in app/pages/, automatic imports, data fetching with useFetch and useAsyncData, and API routes served by its own server engine, Nitro (introduction). Features beyond the core come from a directory of more than 444 modules. Nuxt is free and MIT-licensed, and much of its core team now works at Vercel, which earns its revenue from hosting: a monthly fee per seat on Pro, plus metered function invocations, CPU time, data transfer and requests (Vercel pricing).
Elements plays in the same category and replaces Nuxt. Elements is an integrated app environment for building web apps, 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: routing, server rendering, typed server functions, sessions, realtime data, background jobs and email, with pages written in Elements HTML, standard HTML with typed reactive expressions. Elements apps are written in TypeScript and run on Node.js.
Nuxt is a Vue framework at the center of parts you assemble yourself. Its core has no sign-in, no test runner and no migrations, and its server engine's database layer, WebSockets and scheduled tasks each stay off until you set an experimental flag (database, WebSockets, tasks). Every missing piece is a module or package to pick, configure and connect, and every seam between them is a place the app breaks, for you or your agent. The vocabulary piles up on top: ref() and .value, useFetch and useAsyncData, defineEventHandler and readBody, definePageMeta, route rules and auto-imports. Elements is ready out of the box and well engineered, so you build with it productively from the first minute. It stays close to the metal: HTML, CSS, TypeScript, SQL and function calls. The framework vocabulary is the bare minimum, app.route(), session.login(), sql(), @rpc and tests, and a wrong field name in a template fails the same build as a broken migration, a moment after you save.
At a Glance
| Elements | Nuxt | |
|---|---|---|
| What it is | A project server and a default framework, with Postgres, tests and deploy, shipped as one coherent system | A Vue framework with a server engine, assembled with modules |
| Business model | Sells the tooling directly, per machine, with nothing metered (pricing) | Free; much of the core team works at Vercel, which sells metered hosting (Vercel) |
| Templates | Elements HTML: standard HTML with typed reactive expressions | Vue single-file components, with ref() and .value for state |
| Routing | Declared in code with web standard URLPattern |
Files in app/pages/ for pages, and in server/ for API routes |
| Server functions | @rpc: import the function and call it, typed end to end |
API routes, called by URL with useFetch or $fetch |
| Sign-in and sessions | Built in, the same over HTTP and WebSockets | A module such as nuxt-auth-utils, plus a database you add for users |
| Database | Postgres, bundled, with sql() built in |
Nitro's database layer, behind a flag and on SQLite by default, or bring your own |
| Migrations | Applied when you save them | Not included |
| Tests | Part of the build | Five packages to install, starting with @nuxt/test-utils and Vitest |
| Realtime | LiveTable and channels | WebSockets in Nitro, behind a flag |
| Background jobs and cron | Built in | Tasks in Nitro, behind a flag |
| Built in, written in Elements HTML | A module or library | |
| Build errors | One build reports type checking, tests, migrations and program analysis, as you work | Types off in nuxt dev and nuxt build; nuxt typecheck and tests run separately |
| Deploy | elements deploy to any Ubuntu server over SSH, usually under a second |
A Nitro preset for a host, or a Node.js server and PM2 you run yourself |
For an Agent, the Value Is in Correction, Not Modules
More app code is now written by coding agents such as Claude Code and Codex. For an agent, the value is not in endless framework abstractions or in a directory of modules. It is in correction: finding out what it got wrong the moment it gets it wrong.
Nuxt puts its weight on the other side. Features beyond the core arrive as modules, for auth, content, images, UI kits and hosting, and the directory lists more than 444 of them (modules). Each one is a decision before your app works, a block of options in nuxt.config, another API for the agent to look up, and one more package in the app. When two of them disagree, you or your agent debug the seam. Libraries can be real shortcuts, and Elements is one, but few modules do something an agent cannot write quickly from standards. An agent would sooner write HTML and CSS by hand than learn the props of a Nuxt UI kit, and a few lines of TypeScript beat a dependency that wraps them. The libraries that are real shortcuts, such as Stripe or the AWS SDK, install into Elements from npm as they are.
Picture a line with machine code at one end, which no agent writes, and a package for subtraction at the other. The right amount of framework sits between them, at the plumbing every app needs, and Elements ships that plumbing: routing, typed server calls with @rpc, sessions, sql() and the database driver. Beyond it, every added abstraction costs an agent more to learn and get right than it saves, so Elements stops at the plumbing and puts its engineering into correction. In the Elements app framework, a page is HTML and CSS, a server call is a TypeScript function call, and data is SQL through sql(), with app.route(), session.login() and @rpc as the vocabulary on top. Server code needs no async or await, because the compiler adds them (async). Because the framework and its build were designed together as one coherent system, that system checks every page, server function, test and migration the agent writes, and elements build -json reports each mistake in microseconds. The terminal, the editor and every agent read that one build.
Templates Hold Plain TypeScript Data
Vue and Elements both write templates as HTML with embedded expressions, so a Vue developer reads Elements HTML on sight. The difference is the data behind the template.
In Vue, state that should update the page has to be wrapped in ref() or reactive(), and a ref is read and written through .value in script code. Derived values need computed(), props and events are declared with defineProps and defineEmits, and a form field binds both ways only when you add v-model.
In Elements HTML, the data is ordinary TypeScript. Attributes are declared on the template's root tag with their types, event handlers are plain functions in the same file, and changing an object's fields in a handler updates the page, with nothing to wrap. Form inputs bind both ways by default. The build type checks templates with the rest of the code, so a misspelled field in a template is a build error.
Server Functions Are Imported, Not Fetched by URL
In Nuxt, server code lives in API routes: a file such as server/api/tasks.post.ts exports a handler made with defineEventHandler, reads its input with readBody, and the page calls it by URL with $fetch. The file name sets the path and can set the HTTP method, and the URL string is a seam between the page and the server that a rename breaks.
In Elements, there is no URL. Mark a function @rpc, import it into the page and call it. The parameters and return type carry across, and the body never reaches the browser.
app/shared/services/tasks.tsElementsimport { sql } from "@elements/app"; export interface Task { id: string; title: string; } /** @rpc */ export function addTask(title: string): Task { return sql<Task>(` insert into tasks (title) values (${title}) returning id, title `).firstOrThrow(); }
In Elements, the page imports it and calls it like a local function:
app/pages/tasks/template.ehtmlElementsimport { addTask, Task } from "#app/shared/services/tasks"; function onSubmit(form: { title: string }, tasks: Task[]) { tasks.push(addTask(form.title)); form.title = ""; } <html (tasks: Task[], private form: { title: string } = { title: "" })> <form onsubmit={() => onSubmit(form, tasks)}> <input value={form.title} required> <button type="submit">Add</button> </form> <ul> <li e:for={task of tasks}>{task.title}</li> </ul> </html>
There is no URL to keep in sync, no body to parse and no method to pick. If browser code calls sql, tx, session.login or email directly, the build fails with "Security error: cannot call server code from the browser without going through an rpc function."
Routes Are Code, Not Folders
Routing decides which code answers which URL. In Nuxt, two folder trees are the router: app/pages/ for pages, with dynamic segments written as file names like [id].vue, and server/api/ and server/routes/ for everything else, with an optional HTTP method in the file name.
In Elements, there is one router. Each route is an app.route() call in index.ts, matched by a URLPattern with named segments, wildcards, optional groups and inline regex. Pages and JSON endpoints go through the same router, which parses bodies and multipart uploads and handles sessions and ETags. Reorganizing folders never moves a URL.
Caching Does the Right Thing by Default
Caching a page that shows one user's data risks serving it to another user. Nuxt caches pages through route rules, cache, swr and isr, which you set per route, and the HTML and the data payload behind it are cached as separate responses. A July 2026 high-severity advisory shows the seam: with those rules on authenticated pages, "The cached _payload.json could be served to a different user or an unauthenticated visitor, even though the HTML was correctly varied." Nuxt told users to "purge any CDN or edge cache that may already hold a leaked _payload.json" (Nuxt security patch releases, GHSA-wm8w-6qjm-cv43).
In Elements, caching works out of the box on the HTTP standard, with nothing to configure. Every page has an ETag computed from its source, its data and the session, so an unchanged page answers 304 (server rendering). Every asset has a hash in its file name and is cached by the browser and any CDN in between (assets). Signed-in pages stay private, kept out of shared caches, so one user's page is never served to another.
The Database, Realtime and Jobs Ship in Elements
Most apps need a database, live updates and work that runs in the background. Nitro has a feature for each, and each stays off until you turn it on with a flag in the config: the database layer, which defaults to SQLite, WebSockets, whose support on each host is tracked in an open issue, "WebSocket Support Tracker", and tasks. Nitro's database layer has no migrations.
In Elements, these are part of the framework:
- Database: Postgres, installed for you locally and on each server. Queries are SQL in the
sql()function, and a value you put in a query can never turn into SQL injection. - Migrations: each is a SQL file that takes effect when you save it (migrations), and at deploy the new ones apply together in one transaction, or the release is stopped.
- Realtime: a LiveTable keeps a set of rows the same in each browser displaying them, and channels carry other server-to-browser messages. Postgres carries both, so nothing else needs deploying.
- Jobs and cron: a queue in the same Postgres database, so a job enqueued inside a transaction commits with your other writes (jobs).
Sign-In and Sessions Are Built In
Nuxt has no sign-in of its own. Its sessions and authentication recipe installs the nuxt-auth-utils module, has you set a NUXT_SESSION_PASSWORD of at least 32 characters, and leaves storing users to a database you add next.
In Elements, sessions and Postgres both ship with the system. Templates, route handlers and server functions read the session the same way over HTTP or WebSockets, and a template that shows who is signed in redraws the moment someone signs in. Sign-in is a short server function that checks a bcrypt hash in Postgres and calls session.login() (authentication).
Every Build Error in One Answer
Nuxt leaves each check to a tool of its own. Its testing guide starts by installing five packages: @nuxt/test-utils, vitest, @vue/test-utils, happy-dom and playwright-core. Its TypeScript guide says "By default, Nuxt doesn't check types when you run nuxt dev or nuxt build, for performance reasons"; turning it on means installing vue-tsc and typescript and running nuxt typecheck yourself. Builds take memory, too. An open issue, "Memory usage during nuxt build", filed in April 2026 with 24 reactions, reports a peak of 3.5 to 4 GB on a minimal project with only @nuxt/ui and more than 12 GB on a larger one with sourcemaps and thousands of prerendered routes.
In Elements, one build reports every error, type checking, tests, migrations and program analysis, for code and templates alike, and it stays on. A failing test fails the build, and each test runs in a Postgres transaction that is discarded afterward, so no test leaves data behind. The 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. It builds in milliseconds and answers agents and humans in microseconds (build). elements build -json returns every one of those errors as one answer, and the terminal, the editor and parallel agents all read that one build.
Who Pays for Nuxt
Nuxt is now an on-ramp to Vercel's metered hosting business. Before July 2025, NuxtLabs funded Nuxt itself and sold paid add-ons such as Nuxt UI Pro. Then it joined Vercel, and its announcement says "With Vercel's support, we no longer have to split our focus between maintaining Nuxt and funding its future." The same post commits to wiring NuxtHub into Vercel's Marketplace offerings "like Postgres and Redis" (NuxtLabs). Vercel's announcement names Nuxt's creator and three core maintainers among the team NuxtLabs funded and employed (Vercel).
Vercel is a venture-backed company (Vercel), and its revenue is the hosting: a fee per seat plus metered compute, requests and data transfer (Vercel pricing). Vercel also funds Next.js, the React framework it sells hosting for; Elements vs Next.js covers that model, and Elements vs Vercel covers the bill.
Elements makes its money another way: it sells its tooling, per machine, and meters nothing (pricing), and your app runs on a server you rent from the provider you choose. Elements charges for the tooling, not the framework: @elements/app and the other runtime packages an app runs on are MIT licensed, and the code you write stays yours (licenses).
Deploys Usually Finish in Under a Second
A Nuxt build produces an .output directory. On your own server, you run it with node .output/server/index.mjs, and the deployment guide recommends PM2 to manage the process. The reverse proxy, TLS certificates and database are yours to set up, and tests and migrations are steps you add to your own pipeline.
In Elements, elements deploy connects over SSH to an Ubuntu server from any provider and sends only the files that changed: a few seconds for a new machine, usually under a second from then on. Tests run on each machine, migrations run once, and a failure stops the deploy while the current release keeps serving. Elements configures the load balancer, TLS certificates and Postgres for you. The bundled Postgres suits a prototype or an app on one server. Once you run several servers, or want backups and scaling handled by a provider, point DB_HOST at a hosted Postgres such as DigitalOcean Managed PostgreSQL or Amazon RDS (setup).
Moving From Nuxt
An agent can port a Nuxt app easily, because its pages, Vue components and server code map onto the Elements app framework's routes, HTML templates and @rpc functions. You keep TypeScript and your npm packages, and what Nuxt leaves to modules comes ready: sign-in, a database, realtime, jobs, tests and deploy.
Questions
What is the difference between Elements and Nuxt?
Nuxt is an open-source Vue framework at the center of parts you assemble: sign-in, tests and migrations come from modules and other packages, and its server engine's database layer, WebSockets and tasks stay off until you set an experimental flag. Elements is an integrated app environment, ready out of the box: a project server, a default app framework with sessions, realtime and jobs, a bundled Postgres database, a test runner and a deploy command, with one build that reports every build error, from type checking, tests, migrations and program analysis, to you and your agent.
Is Elements a Nuxt alternative?
Yes. Elements takes the place of Nuxt and the modules and services around it: the database, sign-in and sessions, a test runner, background jobs, realtime, email and deploy all ship with Elements.
Can I use Vue components in Elements?
No. Elements ships Elements HTML, standard HTML with typed reactive expressions, which a Vue developer reads on sight. State is plain TypeScript data with no ref() or .value, and form inputs bind both ways by default.
Who owns Nuxt?
NuxtLabs, the company behind Nuxt and Nitro, joined Vercel in July 2025, and Nuxt's creator and many of its core maintainers now work there (Vercel). Vercel is a venture-backed hosting company whose revenue comes from metered hosting (Vercel pricing). Elements is sold directly, per machine, so its revenue comes from the tooling itself (pricing).
Does Elements do server-side rendering?
Yes, every page, automatically, with no mode to choose. Each page arrives as complete HTML, so crawlers and new visitors see its content on the first paint, and the same page is also fully reactive in the browser, with no compromise.
How do I move a Nuxt app to Elements?
An agent can port a Nuxt app easily, because its pages, Vue components and server code map onto the Elements app framework's routes, HTML templates and @rpc functions. You keep TypeScript and your npm packages, and what Nuxt leaves to modules comes ready: sign-in, a database, realtime, jobs, tests and deploy.