YallaDo
Personal and team task tracking: a private board plus shared team workspaces.
Build the marketing site, the product app and the admin panel in one repo. They share one backend and one login, and each deploys on its own.
npx create-devora@latestEarly-stage and pre-1.0. Testers welcome: join the Discord.
Three apps, one shared backend, one login — and a database that is yours to choose.
Caching and data flow you can read in the route file, not infer.
"use client" directive lands, and it pulls in the whole subtreerenderMode written per route: ssr/ssg/csr/isr/streamingloader and action exported explicitly for data and mutationsrevalidate: { seconds } written in the route file itselfSecurity follows the same philosophy: a default, not a plugin.
To be fair to Next.js: version 16 can move the same way. With cacheComponents: true caching is opt-in through "use cache" and data fetching is dynamic by default; its docs say both become the default in the next major release. The left card describes the App Router without that flag.
import { defineProject } from "@devorajs/core/config";
export default defineProject({
apps: [
{ name: "marketing", dir: "apps/marketing", domain: "marketing.example.com", auth: "shared" },
{ name: "dashboard", dir: "apps/dashboard", domain: "dashboard.example.com", auth: "shared" },
{ name: "admin", dir: "apps/admin", domain: "admin.example.com", auth: "shared" },
],
shared: {
core: "packages/core",
backend: "packages/backend",
auth: "shared",
},
});That file is what npm create devora@latest writes. devora dev runs every app, devora build builds each one, and a function in packages/backend is imported by whichever app needs it. CSP and HSTS headers are on by default, and sessions are revocable for apps that use auth; render modes are chosen per route in the route file.
Fair trade-offs, not a scoreboard — these are different tools solving overlapping problems.
| Next.js | Turborepo / Nx | Plain React | ||
|---|---|---|---|---|
| What it is | Framework | Build/task tool | UI library | Framework |
| Multiple apps, one project | Multi-Zones — separate deployments, one domain | Wire it yourself, any framework | Separate project per app | Native — one config, shared core; separate deployments |
| Navigating between apps | Full page load | — | Full page load | Full page load — same as Multi-Zones |
| Shared backend | Per-app API routes / Route Handlers | Not its job | You build it | One shared packages/backend |
| Client JS on a static page | 132 kB gzip of JavaScript on the static page of our benchmark app | — | The whole client bundle | None in the same benchmark; island() opts in one component |
| Security headers (CSP/HSTS) | Manual — middleware/config | — | Manual | On by default, every app |
| Adding a second app | New zone + rewrites/assetPrefix | New app + wire the tooling yourself | New Vite config + router | devora add <name> |
About Turborepo and Nx: they're monorepo task runners, not frameworks — a "—" means the dimension isn't their job, not a knock against them. A large or polyglot repo still wants one of them; devora.js doesn't replace what they do, it just doesn't require one for "a few apps sharing one core."
Want the whole picture?
SvelteKit, Astro, React Router, Express, Fastify, and NestJS too — with adoption numbers, and where each one is ahead.
Six things the framework handles so your app code doesn't have to.
Marketing site, product app, and admin panel live side by side in one project, sharing a core — not bolted together with a separate monorepo tool.
Render mode, data loading, and server/client boundaries are declared right in the route file — nothing caches on a schedule you didn't write down.
CSP and HSTS headers on by default for every app, CSRF protection built in (a route that changes state calls verifyCsrf()), plus revocable sessions for apps that use auth.
One shared backend for server functions and your DB client of choice — no built-in ORM or auth provider forcing your hand.
ssr, ssg, csr, isr, and streaming, chosen per route — this page itself is ssg, pre-rendered once at build time.
Hydrate only the components that need interactivity; the rest of the page stays static HTML, so less JavaScript is sent and less hydration work is done.
Every app in the project calls the same functions — no API layer to write between them.
Logic more than one app needs lives once, in packages/backend. A page's loader or action imports it and calls it directly; an API route in another app calls the same function for mobile clients. One login works in both, so the function can check requireAuth() the same way everywhere.
// packages/backend/notes.ts
export const addNote = serverFn(async (input, ctx) => {
ctx.requireAuth();
return db.notes.insert(input);
});
// apps/dashboard/routes/notes.tsx (a form action)
// apps/api/endpoints/notes.ts (JSON for mobile)
import { addNote } from "@devorajs/backend/notes";Real products running on it today.
Personal and team task tracking: a private board plus shared team workspaces.
Landing page and early-access waitlist for a daily habits and workouts app.
This site: every page pre-rendered, with Pagefind search. The source is on GitHub.
What each one uses is read from the deployed site (headers, cookies, markup, API responses), checked October 14, 2026.
In this run Devora.js was ahead of every other framework on server-rendered page, pre-rendered page and form post; level, within the run-to-run spread, on build time (with Astro, SvelteKit and React Router); behind on memory (SvelteKit) and JavaScript (SvelteKit).
Requests/second, server-rendered pageHigher is better
| Framework | Result |
|---|---|
| Devora.js 0.3.5 | 11,736 req/s |
| Astro 7.3.8 | 9,456 req/s |
| SvelteKit 3.0.1 | 7,122 req/s |
| React Router 8.4.0 | 3,944 req/s |
| Next.js 16.4.1 | 1,826 req/s |
A page rendered per request: 100 items from a loader and one interactive counter. The scenario closest to a real application.
Requests/second, pre-rendered pageHigher is better
| Framework | Result |
|---|---|
| Devora.js 0.3.5 | 101,891 req/s |
| Astro 7.3.8 | 38,096 req/s |
| React Router 8.4.0 | 36,138 req/s |
| SvelteKit 3.0.1 | 36,036 req/s |
| Next.js 16.4.1 | 13,989 req/s |
A tiny page with no data, built ahead of time. Each framework sends its own output (sizes under "Response size"), so this mostly measures the HTTP layer.
Requests/second, form POSTHigher is better
| Framework | Result |
|---|---|
| Devora.js 0.3.5 | 79,136 req/s |
| SvelteKit 3.0.1 | 36,976 req/s |
| React Router 8.4.0 | 23,567 req/s |
| Astro 7.3.8 | 18,791 req/s |
| Next.js 16.4.1 | 8,585 req/s |
A valid form submission answered with a 303 redirect.
Cold production buildLower is better
| Framework | Result |
|---|---|
| React Router 8.4.0 | 409 ms |
| Devora.js 0.3.5 | 434 ms |
| Astro 7.3.8 | 563 ms |
| SvelteKit 3.0.1 | 971 ms |
| Next.js 16.4.1 | 2.17 s |
npm run build with build output and caches deleted.
Idle server memoryLower is better
| Framework | Result |
|---|---|
| SvelteKit 3.0.1 | 141 MB |
| React Router 8.4.0 | 149 MB |
| Devora.js 0.3.5 | 157 MB |
| Astro 7.3.8 | 161 MB |
| Next.js 16.4.1 | 175 MB |
Resident memory 2 s after start.
JavaScript sent to the browser, SSR pageLower is better
| Framework | Result |
|---|---|
| SvelteKit 3.0.1 | 33.4 kB |
| Devora.js 0.3.5 | 45.6 kB |
| Astro 7.3.8 | 67.4 kB |
| React Router 8.4.0 | 108.6 kB |
| Next.js 16.4.1 | 132.0 kB |
Every script the page loads, gzip.
Apple M5, 10 cores, 16 GB, Darwin 27.0.0, Node 24.19.0. 50 connections, median of 5 runs (builds: 3), 2026-10-16. Client and server on one machine, tiny pages. Reproduce · Full results and caveats
Live demos. The 3 example apps from the framework repo — marketing, dashboard and admin, one project with three auth modes — deployed by the maintainer on Vercel and Netlify.
See examples and demos →What's next. Desktop and native mobile apps are planned goals, not started, and there are no dates. See the roadmap (also as ROADMAP.md on GitHub).