Writing in Markdown
These docs test Fuzor's Markdown route pipeline. Every page under website/pages/docs/ is an index.md file; Fuzor's built-in docs layout automatically supplies the shared navigation and documentation framework.
Text and links
Use bold, emphasis, and relative documentation links as you would in a normal Markdown file. Fuzor parses Markdown during compilation and sends HTML to the browser.
The Markdown file is the route source. The docs layout is ordinary HTML supplied by the compiler.
Lists
- Create a route folder.
- Add
index.md. - Link to the resulting URL.
Nested documentation is still just folders. For example, pages/docs/guides/building/index.md becomes /docs/guides/building/.
Code blocks
Fenced blocks keep configuration and examples readable:
import { fuzorPlugin } from "fuzor/vite";
const plugin = fuzorPlugin();
Syntax highlighting
Highlight fenced code at build time with Shiki. The output is styled HTML with no client JavaScript. Install Shiki and enable it:
pnpm add -D shiki
fuzorPlugin({ markdown: { highlight: { theme: "github-dark" } } })
// or light and dark themes as CSS variables:
fuzorPlugin({ markdown: { highlight: { theme: { light: "github-light", dark: "github-dark" } } } })
With two themes, Shiki writes --shiki-light and --shiki-dark custom properties; switch between them in your CSS. Blocks without a language or with an unknown language stay unhighlighted. This site uses github-dark.
Tables
| Input | Output |
|---|---|
index.md | A route document |
layout.html | A wrapper around the route |
components/*.html | Compile-time HTML components |
Markdown is currently rendered at build time with TanStack Markdown. This page doubles as a visible check for headings, links, lists, quotes, code fences, and tables.
Link checking
Same-site references in Markdown are checked during compilation, and every problem is reported in one error:
- page links such as
/docs/routing/or../must match a compiled route; - fragments such as
/docs/routing/#linksmust match anidon that route, its layouts or theindex.htmlshell; - references with a file extension and image sources must exist in
public/.
Absolute URLs (https://…), other protocols such as mailto: and protocol-relative //host/ links are not checked. For same-site paths that Fuzor does not build, such as a server endpoint, list their prefixes in fuzorPlugin({ markdown: { ignorePaths: ["/api/"] } }). Set markdown.linkCheck to "warn" to log problems without failing the build, or "off" to skip the check.
Raw HTML and untrusted input
Raw HTML in Markdown is escaped and shown as text, with one exception: a line that contains only a Fuzor element or one of your components is kept as HTML.
Some Markdown text.
<fuzor-island src="newsletter-form" client:visible></fuzor-island>
<site-callout>This text is not Markdown.</site-callout>
The allowed elements are fuzor-island, fuzor-data, fuzor-toc, fuzor-nav, fuzor-link, fuzor-param and components from components/. The element must open and close on the same line, outside code blocks, and its contents are not Markdown. Everything else, including <div> and elements inside a paragraph, is escaped, so Markdown from other authors cannot inject markup beyond these elements. HTML pages, layouts and components are trusted author files. Malformed Markdown degrades to text rather than failing, while invalid frontmatter fails compilation with the file and line.
UI components in Markdown
Put multiline fuzor/ui markup in a trusted HTML component, then include that component on a standalone line. For example, create components/docs-tip.html:
<fuzor-card>
<fuzor-card-title>Quick tip</fuzor-card-title>
<fuzor-card-description>Use islands for interactive framework code.</fuzor-card-description>
</fuzor-card>
Include it in a Markdown page with <docs-tip></docs-tip>. UI markup remains HTML inside the component; Markdown inside it is not parsed. Use native HTML such as <pre><code> for code examples there. Fuzor's own docs use this pattern for package-manager tabs and FAQ accordions.
Heading anchors and table of contents
Heading IDs are compiled from Unicode heading text. Repeated headings receive -2, -3, and later suffixes. Links to encoded anchors work across SPA and MPA output.
Place <fuzor-toc></fuzor-toc> in a Markdown page or its HTML layout to generate static navigation labeled “On this page”. It includes the page headings in document order and requires no browser JavaScript.
Page metadata
Frontmatter is YAML between --- lines at the very top of a page. It works the same in index.md and index.html pages (and in not-found and error pages), and is removed from the output.
| Field | Meaning |
|---|---|
title | Document <title> |
description | <meta name="description"> |
canonical | <link rel="canonical">, an absolute HTTP(S) URL |
layout | A named layout from layouts/, replacing folder layouts but keeping the root layout |
order, navTitle, navGroup | Position, link text and group in generated navigation |
revalidate | Whole seconds a CDN may keep the server-delivered document; see build profiles |
Metadata is validated during compilation; unknown fields, wrong types and YAML aliases are errors. Pages in dynamic folders can override any field per value in their paths entry's metadata.
---
title: My page
description: What this page explains
canonical: https://example.com/docs/my-page/
order: 1
---
---
title: Pricing · Acme
description: Plans and prices.
---
<h1>Pricing</h1>
SPA navigation updates the title, description and canonical link. MPA and server documents contain them already; nothing is assembled per request. Named layouts must contain a default slot and cannot reference files outside layouts/.
Site search
Set searchIndex: true (or { scope: "/docs/" }) in the plugin options to write search-index.json next to the pages. It lists, for every page, its path, title, headings and the first part of its text, without navigation, code blocks, scripts and islands:
{ "version": 1, "documents": [{ "path": "/docs/api/", "title": "API endpoints", "headings": ["Cookies and sessions"], "text": "…" }] }
The file is built with the pages, so search needs no server, and the dev server serves it too. The shipped docs layout uses Fuzor's deferred dialog component. Its Effect-owned search runtime fetches the index on the first query, shares in-flight requests and cancels requests on disposal. Cmd/Ctrl+K opens the dialog; arrow keys move through results. A normal documentation link remains available without JavaScript. An app can replace it with its own docs-search island.
Generated navigation
Place <fuzor-nav scope="/docs/" label="Documentation"></fuzor-nav> in a layout to list every page below a path. Pages are ordered by order (pages without it come last), then by link text. The link text is navTitle, otherwise the page's first heading. Pages with the same navGroup are listed under that heading, in order of first appearance. The current page's link has aria-current="page". The output is a plain <nav> with <ul> lists and <p data-fuzor-nav-group> headings, compiled into each page, so it needs no JavaScript. This site's sidebar is generated this way.
Built-in docs UI
A pages/docs/ directory without a custom docs layout receives the shipped docs shell. It uses Fuzor UI for category tabs, search, theme switching, mobile navigation, the page outline and hints. No separate UI dependency or manual registration is needed. See the UI reference for components, drivers and restoration.