Overview
Plugins come in three types that match pipeline stages: transformers parse and enrich each page, filters decide what publishes, and emitters write files to public/. This page was checked against the Quartz 4.5.2 source in this repo.
Order plugins by build phase, not by guesswork
A build runs these phases in sequence:
- Every transformer’s
textTransformruns on the raw file text. - Every transformer’s
markdownPlugins(remark) run in array order. - The markdown tree converts to HTML.
- Every transformer’s
htmlPlugins(rehype) run in array order. - Filters run, then emitters.
Array order matters only within a phase. A transformer that reads file.data.frontmatter in its markdownPlugins must come after FrontMatter, which is why FrontMatter goes first. CrawlLinks is an HTML-phase plugin, so it runs after every markdown-phase plugin whatever its position. Within the HTML phase, keep ObsidianFlavoredMarkdown before CrawlLinks: its HTML step begins with rehypeRaw, which parses raw HTML, and CrawlLinks can only rewrite anchors that exist by then.
Know what the built-in transformers own
| Plugin | Responsibility |
|---|---|
FrontMatter | Parses YAML or TOML; normalizes tags, aliases, permalink, cssclasses, socialImage, and the date keys |
CreatedModifiedDate | Dates from frontmatter, git, or filesystem (quartz-config) |
ObsidianFlavoredMarkdown | Wikilinks, embeds, callouts, Mermaid, ==highlights==, #tags, block references |
GitHubFlavoredMarkdown | Tables, task lists, autolinks, SmartyPants quotes, heading anchors |
SyntaxHighlighting | Shiki highlighting through rehype-pretty-code |
TableOfContents | Builds the ToC; maxDepth defaults to 3; enableToc: false in frontmatter hides it |
CrawlLinks | Rewrites internal links and records the link graph; logs as LinkProcessing |
Description | Uses frontmatter description, else the first sentences of the body |
Latex | Math through KaTeX (default), MathJax, or Typst |
Mermaid is handled by ObsidianFlavoredMarkdown (option mermaid, default true); do not add a second Mermaid plugin. With KaTeX, the page loads its stylesheet and a copy helper from cdn.jsdelivr.net at runtime, which matters under a strict Content Security Policy.
Other transformers ship but are off by default: Citations, HardLineBreaks, OxHugoFlavouredMarkdown, RoamFlavoredMarkdown.
Choose a filter: RemoveDrafts or ExplicitPublish
RemoveDrafts drops a page only when frontmatter draft is true or "true". It ignores a status: draft field, so a page marked that way still publishes unless the repo adds its own filter. ExplicitPublish inverts the default: only pages with publish: true ship.
A custom filter returns a boolean from shouldPublish:
import { QuartzFilterPlugin } from "../types"
export const RemoveInternal: QuartzFilterPlugin = () => ({
name: "RemoveInternal",
shouldPublish(_ctx, [_tree, vfile]) {
const tags: string[] = vfile.data.frontmatter?.tags ?? []
return !tags.includes("internal")
},
})A filtered page leaves every emitter’s output, including backlinks and the link graph.
Know what the emitters write
| Plugin | Output |
|---|---|
ContentPage, FolderPage, TagPage | HTML for pages, folder indexes, and tag indexes |
ContentIndex | sitemap.xml, the RSS feed at index.xml, and static/contentIndex.json for search |
ComponentResources | index.css, prescript.js, and postscript.js from component styles and scripts |
Assets | Copies every non-markdown file from content/ |
Static | Copies quartz/static/ to public/static/ |
AliasRedirects | A redirect stub at each path listed in aliases |
CNAME | public/CNAME from baseUrl |
NotFoundPage, Favicon, CustomOgImages | 404.html, favicon, and social images |
RawMarkdown | This site’s addition: each page’s source at <slug>.md |
AliasRedirects writes an HTML file with a meta refresh, a canonical link to the real page, and noindex. It is a client-side redirect, not an HTTP 301. Use it when you merge or rename a page.
Write a custom transformer
A transformer returns a name and any of textTransform, markdownPlugins, htmlPlugins, and externalResources. Use markdownPlugins for remark and htmlPlugins for rehype.
import { QuartzTransformerPlugin } from "../types"
export const StampBuild: QuartzTransformerPlugin = () => ({
name: "StampBuild",
markdownPlugins: () => [
() => (_tree, file) => {
file.data.frontmatter = { ...file.data.frontmatter!, builtBy: "ci" }
},
],
})Export it from quartz/plugins/transformers/index.ts, then add Plugin.StampBuild() after Plugin.FrontMatter() in quartz.config.ts. The name appears in verbose logs, so keep it specific; see quartz-debugging.