Engine vs Framework
zfb is the engine — frameworks like a future zudo-doc-v2 sit on top of its primitives.
ℹ️ What this page covers
How zfb’s surface area is split between engine primitives (the six things zfb commits to) and framework concerns (sidebars, search, blog conventions, theming) that live outside the engine.
zfb is a static site engine. It scans pages/, walks content
collections, runs an MDX pipeline, evaluates TSX, threads page meta
through layouts, and writes files to disk. That’s the engine.
A framework — like a future zudo-doc-v2, or a blog kit, or whatever someone publishes downstream — is what turns a pile of engine primitives into a finished site experience. Sidebar generation, search, versioning UI, theme controls, blog routing, i18n routing: all framework concerns.
This page exists because the line between the two is load-bearing. Knowing which side a feature lives on tells you where to look for it, where to extend it, and what your project’s responsibility is when zfb itself doesn’t ship the answer.
The six engine primitives
zfb v1 commits to exactly six primitives. Frameworks build on top of them; nothing else in zfb is part of the public surface contract.
- Frontmatter — unified contract across
.md,.mdx, and.tsxsources. One JSON shape downstream. - Content collections — typed directories of markdown content, walked at build time.
- Build-time content query bridge — a deterministic Rust→JS
snapshot that powers synchronous
getCollection/getEntrycalls from your pages. The contract is stable and documented in Content Collections. - Dynamic routes —
paths()enumerates the concrete URLs to build for[slug].tsx/[...slug].tsxpages. - Custom directives — register container, leaf, and text MDX directives that map to your JSX components without writing any Rust.
- Non-HTML pages —
pages/,sitemap. xml. tsx pages/,llms. txt. tsx pages/, etc. First-class outputs with their own extension andfeed. rss. tsx Content-Type.
That’s the engine. Stable, named, documented.
What zfb does NOT do
These are framework concerns. zfb gives you the substrate; you (or a framework you depend on) wire it up.
- Sidebar / navigation tree generation.
- Search index building (Pagefind, Lunr, etc.).
- Blog conventions (tag pages, archives, RSS feed wiring, pagination).
- i18n routing —
/mirroringja/ . . . /, locale negotiation.. . . - Theme controls (light / dark / dim, design tokens, color palette UI).
- Versioning UI — multi-version docs, version dropdown.
- Layout components — header, footer, table of contents, card grids.
- Site-level chrome — skip links, focus management, scroll-to-top.
The principle: if the answer to “would two frameworks built on zfb do this differently?” is yes, it belongs in a framework, not in zfb.
A small example: a layout consumes the engine’s meta
The engine threads page metadata through PageMeta. Layouts — which
are your code, not the engine’s — read meta and project it into
the <head>. Here’s a tiny default layout:
// layouts/default.tsx
export default function DefaultLayout({ meta, children }) {
const og = meta?.openGraph ?? {};
return (
<html lang={meta?.lang ?? "en"}>
<head>
<title>{meta?.title ?? "Untitled"}</title>
{meta?.description && (
<meta name="description" content={meta.description} />
)}
{og.image && <meta property="og:image" content={og.image} />}
</head>
<body>{children}</body>
</html>
);
}
The page itself just declares meta:
// pages/about.tsx
export const meta = {
title: "About",
description: "Who we are.",
openGraph: { image: "/og/about.png" },
};
export default function AboutPage() {
return <h2>About us</h2>;
}
Notice what the engine does and doesn’t do here. The engine parses
meta, resolves the layout, and wraps your page output in it. Whether
to render og:image, what fallback to use, how to format the title —
all framework-level decisions. zfb stops at “give the layout the
typed PageMeta.”
Opt-in Markdown extras
Not every Markdown capability is a primitive. The zfb-md-extras crate
ships a catalog of opt-in features — Mermaid diagrams, GitHub-style
alerts, reading-time injection, transclusion, and more. These are
framework-flavored: a blog framework might enable reading time and
ruby annotation, while a technical-reference framework might want link
validation and code-block enrichment instead. Neither set is universal.
By keeping them opt-in (enabled per-project in zfb.config.ts) rather
than Core, zfb avoids baking one framework’s opinion into the engine.
The six engine primitives remain the full public contract; the extras
catalog is additive and independently togglable.
See Markdown Features for the full list.
Where to look next
- For each engine primitive, the linked concept page above.
- For the Markdown extras catalog, Markdown Features.
- For the head/meta surface specifically, <code>meta</code> export
documents which fields are first-class and how
extracarries the rest.