Abret is a modern, lightweight, and type-safe web framework built specifically for Bun. It extends Bun.serve with a powerful routing system, composable middleware, and a built-in JSX rendering engine, all while maintaining minimal overhead.
- π Native Bun Integration: Built directly on top of
Bun.servefor maximum performance. - π‘οΈ Type-Safe Routing: Strict TypeScript inference for route parameters and handlers.
- π Composable Middleware: Robust middleware system with
createMiddlewareandcomposeMiddlewares. - βοΈ JSX/TSX Support: Server-side rendering with familiar JSX syntax (no Virtual DOM).
- π§© Unified Context: Component and Request context unified into a single API using
AsyncLocalStorage. - π§ Smart HTML Generation: Automatic
<head>management (titles, meta tags) and DOCTYPE handling.
Abret's JSX engine is optimized for high-performance server-side rendering, avoiding the overhead of Virtual DOM.
- Simple Render: ~800,000 ops/sec
- Complex Component Tree: ~82,000 ops/sec
- VNode Creation: ~2,200,000 ops/sec
Preliminary results on typical hardware. Significantly faster than standard React/Preact renderToString.
bun add abretCreate a simple server with routes and HTML rendering:
import { createRoute, mergeRoutes } from "abret";
import { html } from "abret/html";
const home = createRoute("/", () => {
return html`
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>Welcome to Abret!</h1>
</body>
</html>
`;
});
const api = createRoute("/api/hello", () =>
Response.json({ message: "Hello World" }),
);
const routes = mergeRoutes(home, api);
Bun.serve({
routes,
});Organize your routes using createRoute and createRouteGroup. Types for parameters are automatically inferred.
import { createRouteGroup, mergeRoutes } from "abret";
// Create a group with a prefix
const api = createRouteGroup("/api/v1");
const routes = mergeRoutes(
api("/users", { GET: () => Response.json([]) }),
api("/users/:id", (req) => {
// strict type safety: req.params.id is string
return Response.json({ id: req.params.id });
}),
);
Bun.serve({ routes });Create reusable middleware and share state across the request lifecycle using the unified Context API. Context is implicit and doesn't require passing req objects.
```ts
import { createRoute, createMiddleware, createContext, setContext, useContext } from "abret";
// Define a type-safe context key
const UserContext = createContext<{ name: string }>("user");
const authMiddleware = createMiddleware((req, server, next) => {
const token = req.headers.get("Authorization");
if (!token) return new Response("Unauthorized", { status: 401 });
// Store user in context (implicit scope)
setContext(UserContext, { name: "Alice" });
return next();
});
// Apply middleware to a route
const dashboard = createRoute(
"/dashboard",
() => {
// Safely retrieve context (no req needed)
const user = useContext(UserContext, { required: true });
return new Response(`Welcome ${user.name}`);
},
authMiddleware,
);Build UI with components and share state using Context, just like in React (but on the server).
To use JSX, configure your tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "abret"
}
}Example component:
import { createRoute, createContext, useContext } from "abret";
import { html } from "abret/html";
const ThemeContext = createContext("light"); // Default value
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button class={`btn-${theme}`}>Click me</button>;
}
const page = createRoute("/component", () => {
return html(
<ThemeContext.Provider value="dark">
<ThemeButton />
</ThemeContext.Provider>,
);
});import {
createRoute,
createRouteGroup,
mergeRoutes,
createMiddleware,
composeMiddlewares,
createContext,
useContext,
setContext,
} from "abret";createRoute(path, handler, ...middlewares)- Creates a route. Uses exact path matching.mergeRoutes(...routes)- Combines multiple route objects.createRouteGroup(prefix, middlewares)- Creates a route group.createMiddleware(fn)- Helper for typed middleware.composeMiddlewares(...middlewares)- Composes multiple middlewares.- Context utilities:
createContext,useContext,setContext,runWithContext,runWithContextValue.
import {
createContext,
useContext,
setContext,
hasContext,
clearContext,
} from "abret/store";createContext<T>(name, defaultValue?)- Creates a context key (and Provider if default given).setContext(key, value)- Sets value in current scope.useContext(key, options?)- Gets value (or default).hasContext(key)- Checks if set.clearContext(key)- Clears value.
import { html, HTMLResponse } from "abret/html";html(string | jsx)- Creates anHTMLResponse.HTMLResponse- Extended Response with.doctype()and metadata handling.raw(string)- Creates safe unescaped HTML content.
MIT
