Overview
Quartz components are typed TypeScript functions that return JSX. Every slot in quartz.layout.ts accepts an array of components. Quartz ships roughly twenty built-in components covering navigation, metadata display, search, and graph visualization. When the built-ins fall short, write a custom component by implementing the QuartzComponentConstructor type.
Know the built-in component catalog before writing a custom one
Built-in components cover most common needs. Reach for them first.
| Component | Purpose |
|---|---|
ArticleTitle | Renders the title frontmatter field as an <h1> |
Backlinks | Lists pages that wikilink to the current page |
Breadcrumbs | Slash-separated path from root to the current page |
Content | Renders the compiled markdown body |
ContentMeta | Displays date, reading time, and word count |
Darkmode | Light/dark toggle, persisted in localStorage |
Explorer | Collapsible file tree for the left sidebar |
Footer | Bottom bar with configurable links |
Graph | Interactive D3 force graph of wikilink connections |
Head | Injects <title>, Open Graph, and canonical tags into <head> |
MobileOnly / DesktopOnly | CSS-class visibility wrappers |
PageTitle | Site-level title linking to / |
RecentNotes | Configurable list of recently modified pages |
Search | Client-side full-text search |
Spacer | Flex spacer for sidebar alignment |
TableOfContents | In-page heading navigator, right-sidebar native |
TagList | Renders frontmatter tags as linked pills |
See quartz-layout for how these slot into the layout.
Implement QuartzComponentConstructor to write a custom component
A custom component is a factory function that returns a React function component. The factory receives options; the component receives ComponentProps at build time.
// quartz/components/Banner.tsx
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
interface BannerOptions {
message: string
}
function Banner({ message }: BannerOptions) {
return function BannerComponent({ fileData }: QuartzComponentProps) {
if (!fileData.frontmatter?.banner) return null
return <div class="banner">{message}</div>
}
}
Banner.css = `
.banner {
background: var(--light);
padding: 0.5rem 1rem;
border-left: 4px solid var(--secondary);
margin-bottom: 1rem;
}
`
export default (() => Banner) satisfies QuartzComponentConstructorExport the factory wrapped in an arrow function satisfying QuartzComponentConstructor. Quartz calls the factory once at startup and the component on every page render.
Attach styles with the .css static property
Component-scoped CSS goes on the .css property of the component factory. Quartz collects all .css values from the active component set and injects them into a single <style> tag in <head>. This avoids a separate stylesheet round-trip.
Banner.css = `.banner { color: var(--dark); }`Use CSS custom properties (var(--secondary), var(--light)) instead of hard-coded hex values. The theme object in quartz-config populates these properties at build time, so dark mode works automatically.
Use QuartzComponentProps for page-specific data
The ComponentProps passed at render time expose the current page’s data.
interface QuartzComponentProps {
fileData: QuartzPluginData // frontmatter, dates, slug, links, etc.
tree: Root // hast tree of the rendered page
allFiles: QuartzPluginData[] // every page in the vault
displayClass?: string // "mobile-only" | "desktop-only" | undefined
cfg: GlobalConfiguration // the full quartz.config.ts configuration
}allFiles lets a component compute cross-page aggregations at build time. Use it for recent-notes lists or custom index widgets. Avoid expensive allFiles scans in components that render on every page; prefer a transformer plugin that computes the data once and attaches it to fileData.
Understand the three component type variants
Quartz defines three function signatures depending on where the component renders.
- Slot components (
QuartzComponent): standard layout slot usage; receivesQuartzComponentProps. - Head components: inserted into
<head>; same props, but must return valid head elements only. - Inline components: used inside markdown content via shortcodes; receives a subset of props.
Most custom components are slot components. Head-only components must not render <div> or block-level elements; doing so produces invalid HTML that browsers silently repair but validators flag. See quartz-debugging for how to catch invalid HTML during a build.
Register the component in the layout to activate it
Add the component call to the relevant slot in quartz.layout.ts. Quartz does not auto-discover files.
import Banner from "./quartz/components/Banner"
export const defaultContentPageLayout: PageLayout = {
beforeBody: [
Component.Breadcrumbs(),
Banner({ message: "This page is under active revision." }),
Component.ArticleTitle(),
],
// ...
}