Features and runtime behavior
Doctrine uses one route model and React shell for development SSR, production prerendering, and browser hydration. The commands differ in lifecycle, not in the page model they render.
MDX firstDevelopment server
doctrine dev creates a custom Vite server. For each document request it loads the current server
entry, finds the normalized route, imports the MDX module, renders HTML, and lets Vite transform the
result before responding.
The watcher handles two related sources:
- MDX add, change, and unlink events update
@amamo/mdxrecords. meta.tsandmeta.<locale>.tschanges invalidate navigation and icon virtual modules and trigger a full reload.
Serve mode tolerates transient navigation mismatches while related files are saved. A newly added page does not enter the route list until its nearest navigation file references it; production is strict.
Static site generation
doctrine build runs the complete pipeline:
- Validate output safety, config, locales, and navigation.
- Build browser and SSR environments with Vite.
- Import the SSR entry and enumerate document routes.
- Prerender each route plus a shared
404.html. - Emit route-level authored MDX sources when page actions are enabled.
- Remove Vite's private manifest.
- Build a Pagefind index from the final output directory.
Every document page includes its <html lang>, title, description, canonical URL, matching locale
alternates, and shared assets. When a default-locale translation exists, Doctrine also emits an
x-default alternate.
Routes and navigation
The content path becomes the route slug:
docs/index.mdx -> /
docs/guide/page.mdx -> /guide/
docs/reference/config.mdx -> /reference/config/
docs/reference/config.zh-CN.mdx -> /zh-CN/reference/config/Both index.mdx and page.mdx mean the containing directory, so do not use both for the same locale
in one directory. Other basenames become a trailing-slash route.
Navigation comes only from colocated metadata. Each item names either a direct page or a direct child directory; arrays define exact order, page titles, group titles, and optional icons. A production build rejects missing, duplicate, and nonexistent references.
A page entry may resolve to a same-directory .tsx file instead of MDX. Doctrine imports only TSX
files named by the nearest locale navigation file, so unlisted files remain ordinary components and
are not parsed as pages. A TSX page uses its navigation title and route, but renders only inside the
shared header and footer without the documentation sidebar, prose styles, or table of contents. An
MDX file and TSX file cannot share the same page basename and locale.
export default {
items: [{ page: 'landing', title: 'Landing page' }],
}export default function LandingPage() {
return <main>Build any React layout here.</main>
}Doctrine's default MDX link component adds the deployment base to root-relative links. External
https://... links open in a new tab. Registering a custom a component replaces this behavior.
Page actions and authored Markdown
By default, every MDX document page has a Copy Page action. Its menu also provides View as
Markdown. Open in GitHub appears when githubUrl is configured and the repository-relative
source root can be inferred or is set with githubSourceRoot. The GitHub action targets the actual
source file for the current locale through blob/HEAD, which lets GitHub resolve the repository's
default branch. A translated route therefore opens its suffixed source file rather than the
default-locale document.
Doctrine serves the same authored source at the route-level .md URL during development and writes
it during production builds:
/ -> /index.md
/index/ -> /index/index.md
/guide/install/ -> /guide/install.md
/zh-CN/guide/install/ -> /zh-CN/guide/install.mdThe extra index segment keeps the root and an actual /index/ document from claiming the same
file.
This is the original MDX source, not Markdown reconstructed from the rendered HTML. It can include
frontmatter, ESM imports, JavaScript expressions, and JSX. Copy Page copies that same source,
while View as Markdown opens its .md URL.
Standalone TSX pages do not show these actions and do not get .md source routes. Set
pageActions: false to disable the actions and MDX source output for the whole site. This setting
does not remove the separate GitHub link from the header.
Search
Production search indexes rendered HTML, including text emitted by custom MDX components. The browser loads Pagefind after the user enters a query, waits briefly for more input, and shows at most eight results. ⌘K and Ctrl+K open the dialog.
Development does not write a permanent index. The first search request builds Pagefind files in memory from current SSR output. Content or navigation changes invalidate that cache; the next search request rebuilds it.
Localization
The default locale has no route prefix. Other configured locales use an encoded prefix, and the language control appears only when another document has the same slug.
Navigation and page metadata never fall back between locale files. Locale switch labels fall back to the locale code.
Doctrine's built-in interface labels are Chinese for locale names beginning with zh and English
for every other locale. Content, navigation, site metadata, route prefixes, and <html lang> still
use the exact configured locale.
Themes and customization
The theme control stores light or dark in localStorage. An inline head script applies the saved
choice—or the operating-system preference on first visit—before the document body renders.
Doctrine ships precompiled interface CSS and built-in Badge, Callout, Card, CardGrid,
CodeBlock, FileTree, FileTreeFolder, FileTreeFile, InstallTabs, LivePreview, Step,
Steps, Tab, and Tabs components. Semantic details, definitions, figures, keyboard input,
highlights, quotes, and tables are styled without wrappers. A configured CSS file loads after the
defaults. A custom component map is merged after built-ins and is shared by SSR and browser
hydration.
See MDX components for the built-in reference and examples.
Tailwind is optional. Doctrine enables its Vite plugin only when the documentation project's root
package.json declares tailwindcss in dependencies, dev dependencies, or optional dependencies.
Deployment subpaths
The pathname of siteUrl becomes Vite's public base. Doctrine applies it to assets, default MDX
links, locale routes, Pagefind results, canonical URLs, and rendered 404 assets without nesting the
output directory.
Trust boundary
Static output does not make source code inert. MDX may contain ESM and JavaScript expressions;
referenced TSX pages, doctrine.config.*, navigation modules, and custom components are executable
modules. They can run during config loading, bundling, SSR, or hydration. Build only sources you
trust with the privileges of the build machine and deployed site.
The built-in frontmatter schema validates only an optional string description; it is not a sandbox.
See the @amamo/mdx security model for the compiler's
detailed boundaries.