▶ Watch the demo
Latest release: 0.3.5Multi-app · security-first

One project. Several apps. One backend and one login.

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@latest

Early-stage and pre-1.0. Testers welcome: join the Discord.

How the pieces fit

Three apps, one shared backend, one login — and a database that is yours to choose.

One project, several appsThree apps (marketing, dashboard, admin) on the left each call one shared backend in packages/backend in the middle. One shared login session connects the apps. The backend talks to your own database on the right, which is your choice. Each app deploys separately.marketingapps/marketingdeploys separatelydashboardapps/dashboarddeploys separatelyadminapps/admindeploys separatelyone loginpackages/backendone set of functions,called by every appyour databaseyour choice —none is built inOne project, several appsThree apps (marketing, dashboard, admin) side by side, joined by one shared login, all calling one shared backend in packages/backend, which talks to your own database. Each app deploys separately.marketingown deploydashboardown deployadminown deployone login across the appspackages/backendone set of functions, called by every appyour databaseyour choice — none is built in
Each app builds and deploys separately. The backend and the login are shared.

Why it exists

Caching and data flow you can read in the route file, not infer.

The implicit way (Next.js App Router defaults)
  • Caching is layered and inferred: fetch, route segment and router caches interact by default
  • The server/client boundary is wherever a "use client" directive lands, and it pulls in the whole subtree
  • Revalidation timing comes from defaults you have to look up
The Devora.js way
  • renderMode written per route: ssr/ssg/csr/isr/streaming
  • loader and action exported explicitly for data and mutations
  • revalidate: { seconds } written in the route file itself

Security 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.

How it compares

Fair trade-offs, not a scoreboard — these are different tools solving overlapping problems.

Next.jsTurborepo / NxPlain ReactDevora.js
What it isFrameworkBuild/task toolUI libraryFramework
Multiple apps, one projectMulti-Zones — separate deployments, one domainWire it yourself, any frameworkSeparate project per appNative — one config, shared core; separate deployments
Navigating between appsFull page load—Full page loadFull page load — same as Multi-Zones
Shared backendPer-app API routes / Route HandlersNot its jobYou build itOne shared packages/backend
Client JS on a static page132 kB gzip of JavaScript on the static page of our benchmark app—The whole client bundleNone in the same benchmark; island() opts in one component
Security headers (CSP/HSTS)Manual — middleware/config—ManualOn by default, every app
Adding a second appNew zone + rewrites/assetPrefixNew app + wire the tooling yourselfNew Vite config + routerdevora 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.

See the full comparison →

What you get

Six things the framework handles so your app code doesn't have to.

Multi-app, first-class

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.

Explicit over implicit

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.

Security by default

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.

Bring your own backend

One shared backend for server functions and your DB client of choice — no built-in ORM or auth provider forcing your hand.

Five render modes

ssr, ssg, csr, isr, and streaming, chosen per route — this page itself is ssg, pre-rendered once at build time.

Islands, not full hydration

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.

One shared backend

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";

Built with Devora.js

Real products running on it today.

YallaDo

Personal and team task tracking: a private board plus shared team workspaces.

  • SSR pages
  • Islands
  • Sessions + CSRF
  • JSON API routes
  • Security headers (custom CSP)
  • Vercel

Khepra

Landing page and early-access waitlist for a daily habits and workouts app.

  • Islands
  • JSON API routes
  • Default security headers
  • Vercel

These docs

This site: every page pre-rendered, with Pagefind search. The source is on GitHub.

  • ssg pages
  • Head API
  • Sitemap
  • Custom CSP
  • Vercel

What each one uses is read from the deployed site (headers, cookies, markup, API responses), checked October 14, 2026.

Benchmarks

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

Requests/second, server-rendered page, higher is better
FrameworkResult
Devora.js 0.3.511,736 req/s
Astro 7.3.89,456 req/s
SvelteKit 3.0.17,122 req/s
React Router 8.4.03,944 req/s
Next.js 16.4.11,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

Requests/second, pre-rendered page, higher is better
FrameworkResult
Devora.js 0.3.5101,891 req/s
Astro 7.3.838,096 req/s
React Router 8.4.036,138 req/s
SvelteKit 3.0.136,036 req/s
Next.js 16.4.113,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

Requests/second, form POST, higher is better
FrameworkResult
Devora.js 0.3.579,136 req/s
SvelteKit 3.0.136,976 req/s
React Router 8.4.023,567 req/s
Astro 7.3.818,791 req/s
Next.js 16.4.18,585 req/s

A valid form submission answered with a 303 redirect.

Cold production buildLower is better

Cold production build, lower is better
FrameworkResult
React Router 8.4.0409 ms
Devora.js 0.3.5434 ms
Astro 7.3.8563 ms
SvelteKit 3.0.1971 ms
Next.js 16.4.12.17 s

npm run build with build output and caches deleted.

Idle server memoryLower is better

Idle server memory, lower is better
FrameworkResult
SvelteKit 3.0.1141 MB
React Router 8.4.0149 MB
Devora.js 0.3.5157 MB
Astro 7.3.8161 MB
Next.js 16.4.1175 MB

Resident memory 2 s after start.

JavaScript sent to the browser, SSR pageLower is better

JavaScript sent to the browser, SSR page, lower is better
FrameworkResult
SvelteKit 3.0.133.4 kB
Devora.js 0.3.545.6 kB
Astro 7.3.867.4 kB
React Router 8.4.0108.6 kB
Next.js 16.4.1132.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).

Where to go next