Quartz uses a plugin-based architecture to transform, filter, and emit your content. Plugins are organized into three categories that form a sequential pipeline:
Transformers - Process and transform Markdown content
Filters - Decide which content should be published
Emitters - Generate output files (HTML, assets, indexes, etc.)
Plugin Pipeline
The build process follows this flow:
Transformers
Parse frontmatter, transform Markdown syntax, add syntax highlighting, generate table of contents, and process links.
Filters
Determine which files should be published based on frontmatter properties like draft: true.
Emitters
Generate HTML pages, copy assets, create RSS feeds, sitemaps, and other output files.
Configuration
Plugins are configured in quartz.config.ts:
import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
const config : QuartzConfig = {
plugins: {
transformers: [
Plugin . FrontMatter (),
Plugin . CreatedModifiedDate ({
priority: [ "frontmatter" , "git" , "filesystem" ],
}),
Plugin . SyntaxHighlighting ({
theme: {
light: "github-light" ,
dark: "github-dark" ,
},
keepBackground: false ,
}),
Plugin . ObsidianFlavoredMarkdown ({ enableInHtmlEmbed: false }),
Plugin . GitHubFlavoredMarkdown (),
Plugin . TableOfContents (),
Plugin . CrawlLinks ({ markdownLinkResolution: "shortest" }),
Plugin . Description (),
Plugin . Latex ({ renderEngine: "katex" }),
],
filters: [ Plugin . RemoveDrafts ()],
emitters: [
Plugin . AliasRedirects (),
Plugin . ComponentResources (),
Plugin . ContentPage (),
Plugin . FolderPage (),
Plugin . TagPage (),
Plugin . ContentIndex ({
enableSiteMap: true ,
enableRSS: true ,
}),
Plugin . Assets (),
Plugin . Static (),
Plugin . NotFoundPage (),
],
},
}
export default config
Plugin Types
export type QuartzTransformerPluginInstance = {
name : string
textTransform ?: ( ctx : BuildCtx , src : string ) => string
markdownPlugins ?: ( ctx : BuildCtx ) => PluggableList
htmlPlugins ?: ( ctx : BuildCtx ) => PluggableList
externalResources ?: ( ctx : BuildCtx ) => Partial < StaticResources >
}
Unique identifier for the plugin
Transform raw text before Markdown parsing
Return array of remark plugins to process Markdown AST
Return array of rehype plugins to process HTML AST
Inject external CSS/JS resources into pages
Filter Plugin Interface
export type QuartzFilterPluginInstance = {
name : string
shouldPublish ( ctx : BuildCtx , content : ProcessedContent ) : boolean
}
Unique identifier for the plugin
Return true to publish the content, false to exclude it
Emitter Plugin Interface
export type QuartzEmitterPluginInstance = {
name : string
emit : (
ctx : BuildCtx ,
content : ProcessedContent [],
resources : StaticResources ,
) => Promise < FilePath []> | AsyncGenerator < FilePath >
partialEmit ?: (...) => Promise < FilePath []> | AsyncGenerator < FilePath > | null
getQuartzComponents ?: ( ctx : BuildCtx ) => QuartzComponent []
externalResources ?: ( ctx : BuildCtx ) => Partial < StaticResources >
}
Unique identifier for the plugin
Generate output files during full build
Generate output files during incremental rebuild (dev mode)
Return components used for rendering (optimization)
Inject external CSS/JS resources into pages
Plugin Order
Plugin order matters! Transformers run sequentially in the order specified.
Best Practices:
FrontMatter should always be first (parses metadata)
Markdown syntax plugins (GFM, OFM) before link processing
CrawlLinks after syntax plugins (needs final link structure)
Description near the end (needs processed content)
Creating Custom Plugins
plugins/transformers/myPlugin.ts
import { QuartzTransformerPlugin } from "../types"
export interface Options {
customOption : string
}
const defaultOptions : Options = {
customOption: "default" ,
}
export const MyCustomPlugin : QuartzTransformerPlugin < Partial < Options >> = ( userOpts ) => {
const opts = { ... defaultOptions , ... userOpts }
return {
name: "MyCustomPlugin" ,
markdownPlugins () {
return [
() => {
return ( tree , file ) => {
// Transform the Markdown AST
// tree is a unist Node
// file is a VFile
}
},
]
},
}
}
Custom Filter Example
plugins/filters/myFilter.ts
import { QuartzFilterPlugin } from "../types"
export const MyCustomFilter : QuartzFilterPlugin = () => ({
name: "MyCustomFilter" ,
shouldPublish ( _ctx , [ _tree , vfile ]) {
// Return true to publish, false to exclude
return vfile . data ?. frontmatter ?. customFlag !== false
},
})
Custom Emitter Example
plugins/emitters/myEmitter.ts
import { QuartzEmitterPlugin } from "../types"
import { write } from "./helpers"
export const MyCustomEmitter : QuartzEmitterPlugin = () => {
return {
name: "MyCustomEmitter" ,
async * emit ( ctx , content , resources ) {
for ( const [ tree , file ] of content ) {
const slug = file . data . slug !
yield write ({
ctx ,
content: "<html>...</html>" ,
slug ,
ext: ".html" ,
})
}
},
}
}
External Resources
Plugins can inject CSS and JavaScript resources:
export const MyPlugin : QuartzTransformerPlugin = () => {
return {
name: "MyPlugin" ,
externalResources () {
return {
css: [
{ content: "https://cdn.example.com/style.css" }
],
js: [
{
src: "https://cdn.example.com/script.js" ,
loadTime: "afterDOMReady" ,
contentType: "external" ,
},
],
}
},
}
}
Transformers Process and transform Markdown content
Filters Control which content gets published
Emitters Generate output files and assets
See Also