---
title: "Quartz Components"
slug: "quartz-components"
category: "tooling"
tags: ["tooling", "quartz", "components", "typescript", "ssg", "ui", "customization"]
status: "stable"
last_updated: 2026-08-14
summary: "Built-in Quartz components, how to write a custom component with QuartzComponentConstructor, attaching CSS, and the three component type variants."
related: ["[[tooling/quartz]]", "[[tooling/quartz-layout]]", "[[tooling/quartz-config]]", "[[tooling/quartz-plugins]]", "[[tooling/quartz-debugging]]", "[[howto/deploy-quartz-site]]", "[[ops/github-pages]]"]
---

## Overview

Quartz components are typed TypeScript functions that return JSX. Every slot in `quartz.layout.ts` accepts an array of components. Quartz ships around thirty built-in components in `quartz/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 [[tooling/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.

```ts
// 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.

```ts
Banner.css = `.banner { color: var(--dark); }`
```

Use CSS custom properties (`var(--secondary)`, `var(--light)`) instead of hard-coded hex values. The theme object in [[tooling/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.

```ts
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 [[tooling/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.

```ts
import Banner from "./quartz/components/Banner"

export const defaultContentPageLayout: PageLayout = {
  beforeBody: [
    Component.Breadcrumbs(),
    Banner({ message: "This page is under active revision." }),
    Component.ArticleTitle(),
  ],
  // ...
}
```

## Related

- [[tooling/quartz]]
- [[tooling/quartz-layout]]
- [[tooling/quartz-config]]
- [[tooling/quartz-plugins]]
- [[tooling/quartz-debugging]]
- [[howto/deploy-quartz-site]]
- [[ops/github-pages]]
