Routing
Understand how PreactPress maps files to URLs
Routing#
PreactPress uses file-based routing. Every .md and .mdx file under srcDir becomes a route, and production builds write each route as static HTML.
File-based routing#
Given this structure:
.
├── index.md
├── about.md
├── guide/
│ ├── index.md
│ └── getting-started.md
└── interactive.mdxPreactPress creates these routes:
index.md -> /
about.md -> /about
guide/index.md -> /guide
guide/getting-started.md -> /guide/getting-started
interactive.mdx -> /interactiveDuring production builds, routes are written as directory indexes:
/ -> dist/index.html
/about -> dist/about/index.html
/guide/getting-started -> dist/guide/getting-started/index.htmlBy default (cleanUrls: true), builds emit directory indexes so hosts can serve extensionless URLs.
Project root and source directory#
The project root is where PreactPress looks for .preactpress/config.ts.
By default, source files also live in the project root:
.
├── .preactpress/
│ └── config.ts
├── index.md
└── guide/
└── getting-started.mdYou can move content into a nested source directory with srcDir:
export default {
srcDir: "docs",
};That produces this shape:
.
├── .preactpress/
│ └── config.ts
└── docs/
├── index.md
└── guide/
└── getting-started.mdThe resulting routes stay the same:
docs/index.md -> /
docs/guide/getting-started.md -> /guide/getting-startedLinking between pages#
Use absolute or relative links. The recommended style is to omit file extensions:
[Getting Started](/guide/getting-started)
[Routing](./routing)
[Home](../)Avoid linking directly to generated HTML or source Markdown:
<!-- Avoid -->
[Getting Started](/guide/getting-started.md)
[Getting Started](/guide/getting-started.html)preactpress check validates local Markdown links that use .md, .mdx, or .html extensions and reports missing pages.
Locale routes#
PreactPress supports VitePress-style locale folders. Keep the default language at the root and put translations in locale folders:
index.md -> /
guide/getting-started.md -> /guide/getting-started
de/index.md -> /de
de/guide/getting-started.md -> /de/guide/getting-startedConfigure locale labels, language codes, and locale-specific nav in .preactpress/config.ts:
export default {
locales: {
root: {
label: "English",
lang: "en",
},
de: {
label: "Deutsch",
lang: "de",
link: "/de/",
},
},
};The default theme shows a language switcher when multiple locales are configured.
Tag routes#
Pages can define tags in frontmatter:
---
title: Release notes
tags: [release, changelog]
---Each tag receives an index page:
release -> /tags/release
changelog -> /tags/changelogFor localized content, tag pages are scoped to the locale:
de/guide/intro.md with tag "Markdown" -> /de/tags/markdownIf a real Markdown or MDX page exists at the same route as a generated tag index, the real page wins.
Base path#
Use site.base when the site is served from a subpath, for example GitHub Pages project sites:
export default {
site: {
base: "/my-repo/",
},
};You can also override it for one build:
pnpm exec preactpress build --base /my-repo/Route rewrites#
Map a public URL to existing content without duplicating files:
export default {
rewrites: {
"/docs": "/guide",
"/getting-started": "/guide/intro",
},
};Keys are the routes visitors use; values must point at routes that already exist from your Markdown files. preactpress check validates rewrite sources and collisions.
Clean URLs and hosting#
cleanUrls |
Output for /about |
Typical host |
|---|---|---|
true (default) |
dist/about/index.html |
Netlify, Vercel, Cloudflare Pages, GitHub Pages |
false |
dist/about.html |
Static buckets without directory index support |
export default {
cleanUrls: false,
};Most modern static hosts work with the default. Set cleanUrls: false only when your host cannot resolve /about to about/index.html.
Dynamic routes#
Generate routes at build time with a bracket template and a colocated .paths.ts file:
## <!-- packages/[pkg].md -->
## title: "{{ params.pkg }}"
# {{ params.pkg }}// packages/[pkg].paths.ts
export default {
paths() {
return [
{ params: { pkg: "preact" } },
{ params: { pkg: "vite" }, props: { note: "Build tool" } },
];
},
};This emits /packages/preact and /packages/vite. Use {{ params.key }} and {{ props.key }} in frontmatter and body.
Data loaders#
Colocate *.data.ts with a page to load build-time data via createContentLoader from @kamod-ch/preactpress/config. The result is exposed on page.meta.contentData (and in themes through the page prop).
// blog.data.ts
import { createContentLoader } from "@kamod-ch/preactpress/config";
export default createContentLoader("posts/*.md", {
transform(items) {
return items.map((item) => ({
title: item.title,
route: item.route,
}));
},
});Pair with blog.md or blog/index.md for route /blog.
Current limitations#
| Feature | Status |
|---|---|
Dynamic MDX templates ([id].mdx) |
Not supported (use .md templates) |
| Pattern-based rewrites with params | Not supported |