Astro Integration
Astro is an islands-first web framework
built for content-heavy sites. @astrojs/bext is the Astro adapter
that targets bext: you keep Astro's authoring experience, and bext
provides the HTTP layer, cache, WAF, HTTP/2/3, and plugin ecosystem
that every adapter otherwise reinvents.
Preview release. The adapter and the Rust-side framework runtime in
crates/bext-framework-astroship as part of bext's E6 milestone. See the ecosystem roadmap for what's landed and what's still on the follow-up list.
Install
bun add -d @astrojs/bext
Peer dependencies: astro ^4 or astro ^5.
Configure
In astro.config.mjs:
export default defineConfig({
output: "server",
adapter: bext({
runtime: "bun",
}),
});
The adapter registers itself in the astro:config:setup hook and
calls setAdapter() in astro:config:done. Astro then builds with
server output, producing dist/server/entry.mjs plus a dist/client
directory of hashed assets.
Options
| Option | Type | Default | Description |
|---|---|---|---|
runtime |
"node" | "bun" |
"bun" |
Preferred runtime for the SSR worker. Informational; bext decides. |
Build
bun run build
The build emits:
dist/
client/ hashed JS, CSS, fonts
server/
entry.mjs SSR entry exporting { handler }
bext-adapter.json metadata for the bext runtime
bext-adapter.json is the contract between @astrojs/bext and
crates/bext-framework-astro. It looks like:
{
"adapter": "@astrojs/bext",
"version": "0.1.0-preview",
"runtime": "bun",
"serverEntry": "server/entry.mjs",
"clientDir": "client",
"generatedAt": "2026-04-11T12:00:00.000Z"
}
Serve
Via bext.config.toml
[framework]
type = "astro"
build_dir = "dist"
[build]
script = "dist/server/entry.mjs"
watch_dirs = ["src"]
live_reload = true
Then:
bext serve
bext's vhost detects the project as Astro (on astro.config.* +
astro in package.json), reads the build output, and dispatches
requests to the Astro handler through the framework runtime
adapter.
Via bext new
bext new my-astro-app --template @bext/starter-astro
cd my-astro-app
bun install
bun run build
bext serve
The starter ships with astro.config.mjs, a Base.astro layout,
two pages (/ and /about), and the bext.config.toml plumbing
pre-wired.
How it fits together
Three moving parts collaborate on every Astro request through bext:
@astrojs/bext(this package) — Astro integration and adapter. Runs at build time. Producesdist/andbext-adapter.json. 2.crates/bext-framework-astro— RustFrameworkAdapterimplementation. Runs at server-config time. Detects the project, invokesbunx astro build(ornpx astro build), normalises the output, and vends a runtime adapter. 3.bext-servervhost — per-request. Routes incoming requests to the Astro runtime adapter, which in turn dispatches to the Node / Bun subprocess runningdist/server/entry.mjs.
How it compares to @astrojs/node
@astrojs/node produces a standalone Node server you run yourself.
@astrojs/bext skips the HTTP layer — bext provides that — and
just emits the compiled SSR entry plus static assets. bext then
adds TLS, HTTP/2/3, cache, WAF, compression, and the full plugin
ecosystem without Astro needing to know about any of them.
If you currently run @astrojs/node behind nginx, @astrojs/bext
replaces both layers in one move.
What works today
- File-system routing (src/pages/*)
- Dynamic routes with [param].astro
- API routes (src/pages/api/*)
- Content collections
- MDX (with @astrojs/mdx)
- Islands via client:load, client:visible, etc.
- Hybrid output (output: "hybrid")
- Prerendered pages (export const prerender = true)
- Static assets served from dist/client/
Current limitations
- Subprocess dispatch is still TODO. For the E6 preview, the
runtime adapter is a descriptor only —
bext-framework-astro'shandle_requestreturns a not-yet-wired error. The follow-up is to teach bext-server's vhost to dispatch to the Node / Bun subprocess that importsdist/server/entry.mjs, reusing the same subprocess plumbing bext already has for PHP / FastCGI. - No streaming responses yet. Astro'sapp.render()can return a streamingResponse. The preview buffers those. Piping them through bext's response writer lands with the streamingFrameworkAdapter::renderhook. - No edge runtime support. This adapter targets Node / Bun. Edge-style Workers-compatible output is out of scope for E6. - Image service isn't bridged yet. Astro's built-in image service works, but it doesn't go through bext's media pipeline for caching or transformation. Integration with bext's image pipeline is a follow-up. - No native Rust build.@astrojs/bextshells out toastro build— Astro's build is a Vite plugin set and rewriting it in Rust is out of scope for E6. A direct turbopack / bun pipeline is on the ecosystem roadmap.
Troubleshooting
astro build produced no dist/ directory— the build ran but didn't emit the expected layout. Double-check thatastro.config.mjshasoutput: "server"and that the adapter is installed as a dev-dependency. -AstroRuntime::handle_request is not wired yet— you're seeing the E6 descriptor-only limitation. Build works, detection works, but per-request dispatch is scheduled for the follow-up. Until then, use@astrojs/nodebehind bext as a reverse proxy if you need production traffic right now. - Dependency mismatch with Astro v4 vs v5 —peerDependenciesis set to"astro": "^4 || ^5". If your lockfile pins an earlier major, the integration hooks won't resolve — upgrade Astro or use an older adapter.
Related docs
- @bext/starter-astro — starter scaffold
- bext framework overview — how adapters
compose with bext's request pipeline
- PRISM — bext's native React engine, another
FrameworkAdapterimplementation