zfb

Type to search...

to open search from anywhere

Project structure

CreatedJun 1, 2026Takeshi Takatsudo

A tour of every file and directory the default template lays down.

When you run zfb new my-site, the basic-blog template lays down a small but complete project. Knowing what each directory is for makes the rest of the docs much easier to skim.

my-site/
├── pages/
│   ├── index.tsx
│   └── blog/
│       └── [slug].tsx
├── layouts/
│   └── default.tsx
├── components/
│   ├── note.tsx
│   └── theme-toggle.tsx
├── content/
│   └── blog/
│       └── hello-zfb.mdx
├── styles/
│   └── global.css
├── public/
├── zfb.config.json
├── package.json
├── tsconfig.json
└── .gitignore

pages/

File-system routing lives here. Every .tsx file under pages/ becomes a route:

  • pages/index.tsx/
  • pages/about.tsx/about
  • pages/blog/[slug].tsx/blog/:slug (dynamic route — one HTML file per resolved slug)
  • pages/docs/[...slug].tsx → catchall, matches any depth

The default template ships an index page and one dynamic blog route. A dynamic route file exports a paths() function; zfb calls it at build time to expand [slug] into one HTML file per resolved slug. See Dynamic routes for the full API.

layouts/

Reusable page wrappers. layouts/default.tsx is the shell every page imports — header, footer, common metadata, and so on. Layouts are plain TSX components; you compose them however you like.

components/

Plain components and islands. The template ships components/note.tsx (a server-rendered callout) and components/theme-toggle.tsx (a "use client" island for toggling dark mode). The "use client" directive at the top of a file is what turns a component into a client-side island — components without it render only on the server and ship zero JavaScript. The full mental model is on the islands page.

content/

Content collections. content/blog/ holds the seed entries of a blog collection: Markdown and MDX files in a named directory, queryable from pages via getCollection("blog"). Collection schemas are declared in zfb.config.ts under collections.

styles/

Global CSS. styles/global.css is the entry point — import it from your root layout. The default template wires up Tailwind here when tailwind.enabled is set in the config.

public/

Static assets served as-is from the site root. Put favicon.ico, robots.txt, SVGs, raster images, fonts, manifest files, or any binary you want to reference by absolute URL here. The directory does NOT appear in the URL — public/logo.svg is reachable at /logo.svg, both in zfb dev and after zfb build.

Reference these files by URL from your TSX, MDX, or CSS:

<img src="/logo.svg" alt="" width={128} height={32} />
<link rel="icon" href="/favicon.ico" />

Do NOT use bundler-style imports (import logo from "./logo.svg") for static assets — zfb does not run an asset pipeline over public/. The directory is a verbatim mirror; files come out at the URL that matches their relative path.

When base is set in zfb.config.ts (e.g. base: "/pj/site/"), files in public/ are served under that prefix too — public/logo.svg/pj/site/logo.svg. The same prepend happens at build time, so the prefix is consistent between dev and prod.

See Static Assets for the full reference, including when to use public/ vs a TSX import for islands.

zfb.config.json

The config file uses a camelCase schema with these keys: outDir (default "dist"), publicDir (default "public"), host (default "localhost"), port (default 3000), framework ("preact" or "react", default "preact"), collections, tailwind, and plugins.

The template scaffolds zfb.config.json. If you rename it to zfb.config.ts, zfb loads it via the bundled esbuild binary and you get full TypeScript types and IDE completion — the schema is identical in both formats.

package.json, tsconfig.json, .gitignore

Standard project plumbing. package.json declares the framework runtime dependency (preact by default) and any libraries your islands import. tsconfig.json is configured so TSX in pages/, layouts/, and components/ type-checks cleanly. The shipped .gitignore excludes dist/ and node_modules/ so build output and dependencies stay out of version control.

Revision History