page()
page() defines a server-rendered route for mini(). A page render function returns HTML and receives request context: the original request, parsed URL, matched route parameters, current route pattern, and HTMX state.
Basic Page
import { page } from "@calvinbonner/minifw/core";
import { html } from "@calvinbonner/minifw/helpers";
const product = page(({ params }) => html`<h1>${params["id"]}</h1>`, {
head: { title: "Product" },
});
Register it under a route pattern:
mini({
routes: {
"/products/:id": product,
},
});
The route pattern becomes context.route, and Bun's matched :id parameter is available as context.params["id"].
Render Context
| Field | Description |
|---|---|
request | Original incoming Bun.BunRequest, including cookies |
url | Parsed request URL, including search parameters |
route | Matched route pattern, when the page is served by mini() |
params | Route parameters as a string record |
isHtmx | Whether the request includes HX-Request: true |
Use url.searchParams for query values and params for values captured by a route pattern. For native Bun handlers outside a page, use isHtmx() to detect the same header.
Head Metadata
The optional head field provides document metadata for the initial response and applicable <title>/<meta> updates for HTMX responses.
const profile = page(renderProfile, {
head: {
title: "Profile",
description: "Manage your account profile.",
canonical: "https://example.com/profile",
robots: "noindex",
},
});
mini() creates the initial document and renders this metadata in its head. Do not include <html>, <head>, or <body> in page markup.
Scoped Styles
Use the style-function overload to attach CSS to a page. MiniFW scopes element selectors to that page's markup, preventing collisions with other pages and partials.
import { css, html } from "@calvinbonner/minifw/helpers";
const profile = page(
({ params }) => html`<article class="profile">${params["id"]}</article>`,
() => css`
.profile {
max-width: 60ch;
}
`,
{ head: { title: "Profile" } },
);
For an initial response, scoped styles move into the document head. For a later HTMX response, the markup includes its styles and MiniFW's runtime promotes any new style block into the existing head. See Style Encapsulation for details.
Caching
Set cache: true to cache a page indefinitely, or use a millisecond ttl.
const catalog = page(renderCatalog, { cache: { ttl: 60_000 } });
Page cache keys include the route, pathname, search string, parameters, and HTMX state. Omit cache, or set it to false, when each request must render fresh content. See Cache Management.
Normal And HTMX Responses
For a normal request, mini() renders the page inside all matching layouts, returning a full document. For a standard HTMX request, MiniFW returns page markup and targets the innermost layout's pageTarget. For boosted navigation that shares an outer layout prefix, it swaps only the changed inner fragment; unrelated layout chains use HX-Redirect for a full navigation.
Redirects And Errors
Call redirectTo() when render-time logic should stop and redirect the client:
const account = page(({ params }) => {
if (!params["userId"]) redirectTo("/login", 303);
return "<h1>Account</h1>";
});
Call error() for an expected HTTP failure such as a 404. It reaches Bun's error handler, where isMiniError() narrows the error and exposes its status.
Use fragment() to compose reusable context-free markup inside a page. Use a partial() when that markup needs its own request endpoint.