Pages
Brix pages pair a browser-safe contract with a Preact component and a server route, giving SSR and URL construction one typed source of truth.
File layout
src/features/catalog/pages/productDetails/
contract.ts
Page.tsx
route.ts
route.test.ts
The conventional route.ts is discovered automatically. It must export one runtime value named *PageRoute.
Define the contract
Contracts define paths and optional query validation:
import { createPageContract } from "#brix";
import { z } from "brix/zod";
export const productDetailsPageContract = createPageContract({
path: "/products/:productId",
query: z.object({ preview: z.string().optional() }),
});
Path parameters are inferred from :productId. Invalid query input receives a 400 response before the loader runs.
Portable route paths
Page and API routes use the same portable Hono path grammar so origin and edge matching agree. Named parameters can use constraints such as /products/:productId{[0-9]+}. Optional parameters must be terminal. Constraints cannot contain anchors, alternation, lookarounds, or patterns that match an empty string; a constraint that can span slashes must be terminal. Unsupported patterns are rejected during route registration rather than interpreted differently by different routers.
Use query or body validation for data rules that do not belong in the URL structure. Use getPath for URL construction rather than assembling constrained paths manually.
Write the component
The page module exports exactly one named runtime component ending in Page:
type ProductDetailsPageProps = {
product: { id: string; name: string };
};
export function ProductDetailsPage({ product }: ProductDetailsPageProps) {
return (
<main>
<h1>{product.name}</h1>
<p>Product ID: {product.id}</p>
</main>
);
}
Type-only exports are allowed. Do not use a default export or add unrelated runtime exports.
Create the route
The loader receives parsed params and query and returns component props or an early Response:
import { createPageRoute } from "#brix/server";
import { ProductDetailsPage } from "./Page.tsx";
import { productDetailsPageContract } from "./contract.ts";
const products = new Map([["sku-123", { id: "sku-123", name: "Everyday backpack" }]]);
export const productDetailsPageRoute = createPageRoute(productDetailsPageContract, {
component: ProductDetailsPage,
load: (c, { params }) => {
const product = products.get(params.productId);
if (!product) return c.notFound();
return { product };
},
});
Middleware order is authentication, route middleware, loader, then render. Set auth: true only after configuring authentication in defineBrixServer.
Build links safely
Use getPath; do not concatenate path parameters manually:
import { getPath } from "#brix";
import { productDetailsPageContract } from "./contract.ts";
const href = getPath(productDetailsPageContract, {
params: { productId: "sku 123" },
query: { preview: "true" },
});
Brix encodes path values and serializes query values; incoming requests are validated before the loader runs. Localized contracts take an exact locale key before the options object.
Metadata and alternate representations
Render Head from brix/islands/server in the server component to contribute document
metadata without maintaining a second HTML template:
import { Head } from "brix/islands/server";
export function ProductDetailsPage({ product }: ProductDetailsPageProps) {
return (
<>
<Head>
<title>{product.name}</title>
<meta name="description" content={`Details for ${product.name}`} />
</Head>
<main>
<h1>{product.name}</h1>
</main>
</>
);
}
The example replaces the earlier component and uses its props type. Keep metadata on the server; do not hydrate a component just to set its title.
For locale-keyed paths and dictionary loading, continue with translations. For an explicit semantic projection, add the contract's Markdown representation and the route's matching callback as shown in agent-readable Markdown. Route cache policies are edge hints, not a response store; see fragments.
Test and run
brix add page productDetails --feature catalog
brix test src/features/catalog/pages/productDetails/route.test.ts
brix dev
See testing for route-harness examples and islands for adding narrowly scoped interaction to SSR pages.