Frameworks on bext

bext is two things at once:

  1. A TLS-terminating Rust HTTP frontend that masquerades as nginx for compatibility, then routes per-host through whatever framework the site declares. 2. A native framework called PRISM — TSX → string-builder server rendering, no React on the server, with a build-time compile pass that lands SSR at ~14 µs per page on a release V8 isolate. See PRISM and the compile pass.

You can run a site on PRISM, or you can run a site on Next.js / Hono / Express / Laravel / Symfony / a fetch-handler app and use bext's adapters to call its platform services. The choice is per-site, in each bext.config.toml.

When to pick what

If you're building... Pick
Public marketing / docs / blog / status page PRISM — zero-JS pages, microsecond renders, default-on compile pass
CMS-driven content site with islands of interactivity PRISM + islands
Data-driven app with form submissions PRISM + server actions / loaders
Existing Next.js app Next.js — bext serves it via [framework] type = "nextjs"
Existing Laravel/Symfony/PHP app PHP adapter — worker mode for ~15K req/s
New app with team familiar with React Next.js if you need RSC, PRISM if you don't

PRISM is the recommended choice for new bext-only projects. It has the lowest config overhead (zero-config default for any directory with [framework] type = "prism" and src/app/page.tsx) and the best per-render performance (the compile pass beats Marko 5 by ~5× and Solid 1.9 by ~7× on the same fixture).

PRISM (native)

bext's own framework. JSX compiles to direct HTML strings via the h() runtime; a build-time compile pass folds whole component trees into single string concatenations. No React on the server, no virtual DOM, no diffing. Optional islands ("use client") for interactivity.

Topic Doc
Framework overview, file conventions, server actions PRISM
h() runtime, attributes, escaping rules JSX runtime
Build-time compile pass — what folds, what doesn't, perf data Compile pass
Remix-style loader / action exports PRISM data
`` + multipart + @bext:max-body directive Server actions
React-hydrated interactive components Islands
Fine-grained reactivity, no React, ~3 KB hydrate Signals
Tailwind v4 integration Tailwind

PRISM ships two hydration models: "use client" (React-based islands) and "use signals" (bext's signals runtime — fine-grained reactivity, no virtual DOM, ~3 KB hydrate runtime). Both can coexist on the same page; the loader script picks the right bundle by data-runtime attribute.

Two production PRISM sites today: sites/prism-demo and sites/status.

Framework adapters (Next.js, Hono, Laravel, etc.)

For sites built on a different framework, bext provides SDK adapters that expose its platform services — cache invalidation, real-time pub/sub, KV store, durable queues, scheduled tasks — through an idiomatic API.

Framework Package Language Pattern
Next.js @bext-stack/nextjs TypeScript Direct imports
Hono @bext-stack/hono TypeScript Middleware + standalone client
Express @bext-stack/express TypeScript Middleware + standalone client
Laravel bext/laravel PHP Service provider + helpers
Symfony bext/symfony PHP Bundle + autowiring
PHP Micro bext/framework PHP File-based routing

Auto-Detection

bext automatically detects your framework with zero configuration:

1. Next.jsnext.config.{js,ts,mjs} present 2. Honohono in package.json dependencies 3. Express/Fastify.listen() pattern in entry point 4. FetchHandlerexport default { fetch } pattern (Bun/Deno/Workers) 5. bext Nativebext.config.toml present 6. Static Site — only public/ or static/ directories

The detected framework determines which source transforms are applied:

- Next.js gets 14 transforms (import stripping, barrel optimization, font optimization, server actions, CSS modules, etc.)

- Hono gets 3 transforms (env inline, import strip, alias rewrite)

- Generic frameworks get 3 transforms (env inline, alias rewrite, process polyfill)

- Static sites get no transforms

Architecture

All SDK adapters communicate with bext via HTTP calls to a local sidecar:

┌──────────────┐     HTTP (localhost:3061)     ┌──────────────┐
│  Your App    │  ──────────────────────────→  │  bext Server │
│  (Next.js,   │  ← cache, realtime, kv,      │              │
│   Laravel,   │    queue, tasks               │  ISR Cache   │
│   etc.)      │                               │  PubSub Hub  │
└──────────────┘                               │  KV Store    │
                                               │  Queue       │
                                               │  Scheduler   │
                                               └──────────────┘

Environment variables:

- BEXT_SDK_URL — sidecar base URL (default: http://127.0.0.1:3061)

- BEXT_APP_ID — application identifier for multi-app isolation (default: default)

Common Services

Every adapter provides access to the same five services:

Cache Invalidation

Invalidate ISR-cached pages by tag or path:

// TypeScript
await cache.invalidateTag("products");
await cache.invalidatePath("/api/products");
// PHP
bext_invalidate("products");
app('bext.cache')->invalidatePath("/api/products");

Real-Time Pub/Sub

Push events to connected browsers via SSE/WebSocket:

await realtime.publish("orders", { id: 123, status: "shipped" });
bext_publish("orders", ["id" => 123, "status" => "shipped"]);

KV Store

Key-value storage with optional TTL:

await kv.set("session:abc", userData, { ttl: 3600 });
const data = await kv.get("session:abc");
await kv.delete("session:abc");

Durable Queues

Background job processing with retry support:

await queue.push("emails", { to: "user@example.com", template: "welcome" });
const msg = await queue.pull("emails");
await queue.ack("emails", msg.id);

Scheduled Tasks

Cron-style task registration:

await tasks.register("cleanup", { cron: "0 2 * * *", command: "cache::gc" });
const all = await tasks.list();
await tasks.cancel("cleanup");

PHP Worker Mode

The PHP adapters (Laravel, Symfony) support worker mode for dramatically improved performance:

Mode Latency Throughput
Classic (per-request boot) 1–5ms ~1K req/s
Worker (pre-loaded kernel) 34us ~15K req/s

Worker mode keeps the framework kernel loaded in memory and processes requests in a loop, eliminating per-request bootstrap overhead. See the Laravel and Symfony pages for setup details.

Rendering Modes

Each app in your bext.config.toml can use a different rendering strategy:

[[apps]]
hostname = "blog.example.com"
root = "./blog"
rendering = "isr"        # Cached with background revalidation
revalidate = 3600

[[apps]]
hostname = "api.example.com"
root = "./api"
rendering = "ssr"        # Fresh render every request

[[apps]]
hostname = "docs.example.com"
root = "./docs"
rendering = "static"     # Pre-built at deploy time

Available modes: isr (default), ssr, static, swr.