bext.dev
DocsGetting Started
Getting Started5 min read
On this page

Quickstart

This guide takes you from zero to a running bext site in under 5 minutes using PRISM, bext's native framework. PRISM is the fastest path to a working bext site: zero config, no build step required during development, and the build-time compile pass kicks in automatically.

If you have an existing Next.js / Hono / Laravel app you want to serve via bext instead, jump to Frameworks → Next.js or pick the matching adapter from the framework overview.

Install bext#

The quickest path is the prebuilt release binary:

bash
curl -fsSL https://bext.dev/install | sh

That installs the matched engine, compiler, and framework under ~/.local/bin. Set BEXT_INSTALL_DIR to choose another directory. See the installation guide for other installation options.

Verify:

bash
bext --version
# bext 0.x.y

Create a PRISM site by hand#

PRISM is intentionally tiny — four files get you a running site.

bash
mkdir my-site && cd my-site
mkdir -p src/app

src/app/page.tsx:

tsx
export default function HomePage(props: {
  searchParams?: { name?: string };
}) {
  const name = props.searchParams?.name ?? "world";
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <title>my-site</title>
      </head>
      <body>
        <h1>Hello, {name}!</h1>
        <p>Try /?name=you to change the greeting.</p>
      </body>
    </html>
  );
}

bext.config.toml:

toml
[server]
listen = "127.0.0.1:3088"
app_dir = "."

[framework]
type = "prism"

[compile]
on_demand = true

[build]
watch_dirs = ["src"]

tsconfig.json:

json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@bext-stack/framework",
    "target": "es2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true
  }
}

package.json:

json
{
  "name": "my-site",
  "type": "module",
  "dependencies": {
    "@bext-stack/framework": "0.2.0"
  }
}

Install and run:

bash
bun install
bext dev .

Open http://localhost:3088 — you should see "Hello, world!"

What just happened#

When you ran bext dev ., bext-server:

  1. Read bext.config.toml and saw [framework] type = "prism". That tells the request dispatcher to route through the PRISM pipeline instead of the generic / Next.js / proxy paths.
  2. Walked src/app/ and discovered routes — page.tsx is /, about/page.tsx would be /about, etc.
  3. First request triggered an on-demand compile of the matched route via bext-turbopack. The compile pass folded the static parts of your JSX into HTML strings (see the compile pass).
  4. Subsequent requests hit a warm V8 isolate with a cached page context. In the current rendering-only shootout, PRISM's compile pass averages 15.7 µs on a byte-validated 38.3 KB catalog page; the automatic-JSX runtime path averages 75.3 µs. End-to-end request latency also includes routing, cache, V8 dispatch, and HTTP overhead.

No bun build, no vite dev, no separate dev/prod toolchain. Use bext dev while editing and bext run for production.

Add a route#

Create src/app/about/page.tsx:

tsx
export default function AboutPage() {
  return (
    <main>
      <h1>About</h1>
      <p>Built with bext + PRISM.</p>
      <p><a href="/">← home</a></p>
    </main>
  );
}

With Bext 0.2.11, open http://localhost:3088/about while bext dev . is running. Added, edited, and removed routes reload automatically, as do changes to imported TypeScript modules. No restart is needed.

Add a layout#

Create src/app/layout.tsx:

tsx
export default function RootLayout(props: { children: any }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <title>my-site</title>
      </head>
      <body>
        <nav>
          <a href="/">home</a> · <a href="/about">about</a>
        </nav>
        {props.children}
      </body>
    </html>
  );
}

Then strip the <html> / <head> / <body> from page.tsx and about/page.tsx — the layout wraps them automatically.

The dispatcher composes layouts inside-out: RootLayout(NestedLayout(Page(props))). Each layout.tsx in the directory chain wraps the deeper content via its children prop.

Add an island for interactivity#

PRISM pages are pure server-rendered HTML by default. For interactive bits, add a "use client" component under src/islands/:

Tip

If you only need a small reactive counter or toggle, the signals island ("use signals") avoids shipping React entirely — the hydration bundle is ~3 KB instead of ~40 KB. See the signals example below.

src/islands/Counter.tsx:

tsx
"use client";

import { useState } from "preact/hooks";

export default function Counter({ start = 0 }: { start?: number }) {
  const [n, setN] = useState(start);
  return <button onClick={() => setN(n + 1)}>Count: {n}</button>;
}

Use it from a server page:

tsx
import Counter from "../islands/Counter";

export default function Page() {
  return (
    <main>
      <h1>Click below</h1>
      <Counter start={42} />
    </main>
  );
}

The server renders a <bext-island> placeholder; the client loader fetches /islands/Counter.js, parses props from the data-props attribute, and mounts. See Islands for the full story.

Or with bext signals (no React, ~3 KB hydrate)#

Same component, different runtime — no React in the bundle, no re-render on update. The build pipeline auto-detects "use signals" and emits a separate per-island bundle that uses bext's signals runtime instead of React:

tsx
"use signals";
/** @jsxImportSource @bext-stack/framework/signals */

import { signal } from "@bext-stack/framework/signals";

export default function Counter({ start = 0 }: { start?: number }) {
  const n = signal(start);
  return <button onClick={() => { n.value++; }}>Count: {n.value}</button>;
}

Mount via signalsIsland("Counter", Counter, { start: 42 }). See Signals for primitives, the auto-wrap compile pass, and <List> for reactive arrays.

Deploy#

bext is a single binary. Most production deploys are:

bash
# On the server
bext run /var/www/my-site

…behind a TLS terminator (or use bext's built-in TLS — see Guides → TLS & HTTPS).

For multi-site hosts, switch to the masquerade mode and let bext route by hostname:

bash
bext --nginx-masquerade

See Deploying for systemd units, Docker, and Cloudflare Tunnel patterns.

Next steps#

Topic Doc
The full PRISM framework reference PRISM
The build-time compile pass Compile pass
h() runtime details + escape rules JSX runtime
Server-side data with loader + action PRISM data
Islands (interactive components) Islands
Tailwind v4 integration Tailwind
Routing details Routing
Deploy patterns Deploying
Edit this page ↗Need a hand? ↗
FIND YOUR NEXT STEP

Start with a topic, a command, or a question.