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.

ComponentPurpose
ArticleTitleRenders the title frontmatter field as an <h1>
BacklinksLists pages that wikilink to the current page
BreadcrumbsSlash-separated path from root to the current page
ContentRenders the compiled markdown body
ContentMetaDisplays date, reading time, and word count
DarkmodeLight/dark toggle, persisted in localStorage
ExplorerCollapsible file tree for the left sidebar
FooterBottom bar with configurable links
GraphInteractive D3 force graph of wikilink connections
HeadInjects <title>, Open Graph, and canonical tags into <head>
MobileOnly / DesktopOnlyCSS-class visibility wrappers
PageTitleSite-level title linking to /
RecentNotesConfigurable list of recently modified pages
SearchClient-side full-text search
SpacerFlex spacer for sidebar alignment
TableOfContentsIn-page heading navigator, right-sidebar native
TagListRenders 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 QuartzComponentConstructor

Export 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; receives QuartzComponentProps.
  • 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(),
  ],
  // ...
}