zfb

Type to search...

to open search from anywhere

Engine vs Framework

CreatedJun 1, 2026Takeshi Takatsudo

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.

  1. Frontmatter — unified contract across .md, .mdx, and .tsx sources. One JSON shape downstream.
  2. Content collections — typed directories of markdown content, walked at build time.
  3. Build-time content query bridge — a deterministic Rust→JS snapshot that powers synchronous getCollection / getEntry calls from your pages. The contract is stable and documented in Content Collections.
  4. Dynamic routespaths() enumerates the concrete URLs to build for [slug].tsx / [...slug].tsx pages.
  5. Custom directives — register container, leaf, and text MDX directives that map to your JSX components without writing any Rust.
  6. Non-HTML pagespages/sitemap.xml.tsx, pages/llms.txt.tsx, pages/feed.rss.tsx, etc. First-class outputs with their own extension and 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 — /ja/... mirroring /..., 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 extra carries the rest.

Revision History