# Table of Contents - [Welcome to Quartz 5](#welcome-to-quartz-5) - [Architecture](#architecture) - [Creating Component Plugins](#creating-component-plugins) - [Advanced](#advanced) - [Paths in Quartz](#paths-in-quartz) - [quartz build](#quartz-build) - [quartz create](#quartz-create) - [CLI Reference](#cli-reference) - [quartz restore](#quartz-restore) - [Making your own plugins](#making-your-own-plugins) - [quartz plugin](#quartz-plugin) - [quartz sync](#quartz-sync) - [quartz tui](#quartz-tui) - [quartz upgrade](#quartz-upgrade) - [Community](#community) - [Citations](#citations) - [Canvas Support](#canvas-support) - [Docker Support](#docker-support) - [Roam Research Compatibility](#roam-research-compatibility) - [Mermaid Diagrams](#mermaid-diagrams) - [OxHugo Compatibility](#oxhugo-compatibility) - [LaTeX](#latex) - [Backlinks](#backlinks) - [RSS Feed](#rss-feed) - [SPA Routing](#spa-routing) - [Breadcrumbs](#breadcrumbs) - [Darkmode](#darkmode) - [Internationalization](#internationalization) - [feature/filter](#feature-filter) - [Philosophy of Quartz](#philosophy-of-quartz) - [Canvas](#canvas) - [Full-text Search](#full-text-search) - [Quartz Showcase](#quartz-showcase) - [plugin/filter](#plugin-filter) - [ArticleTitle](#articletitle) - [Backlinks](#backlinks) - [ContentMeta](#contentmeta) - [Darkmode](#darkmode) - [Favicon](#favicon) - [Footer](#footer) - [feature/emitter](#feature-emitter) - [Breadcrumbs](#breadcrumbs) - [Comments](#comments) - [Explorer](#explorer) - [NotFoundPage](#notfoundpage) - [Spacer](#spacer) - [Graph View](#graph-view) - [Social Media Preview Cards](#social-media-preview-cards) - [Reader Mode](#reader-mode) - [Obsidian Compatibility](#obsidian-compatibility) - [CNAME](#cname) - [ContentIndex](#contentindex) - [Citations](#citations) - [ComponentResources](#componentresources) - [ContentPage](#contentpage) - [HardLineBreaks](#hardlinebreaks) - [GitHubFlavoredMarkdown](#githubflavoredmarkdown) - [NoteProperties](#noteproperties) - [AliasRedirects](#aliasredirects) - [CanvasPage](#canvaspage) - [PageTitle](#pagetitle) - [ReaderMode](#readermode) - [RoamFlavoredMarkdown](#roamflavoredmarkdown) - [Search](#search) - [TagList](#taglist) - [Callouts](#callouts) - [Popover Previews](#popover-previews) - [RecentNotes](#recentnotes) - [StackedPages](#stackedpages) - [Graph](#graph) - [CreatedModifiedDate](#createdmodifieddate) - [FolderPage](#folderpage) - [Static](#static) - [Latex](#latex) - [TagPage](#tagpage) - [TableOfContents](#tableofcontents) - [Upgrading Quartz](#upgrading-quartz) - [Comments](#comments) - [Higher-Order Layout Components](#higher-order-layout-components) - [UnlistedPages](#unlistedpages) - [Recent Notes](#recent-notes) - [Assets](#assets) - [Description](#description) - [ExplicitPublish](#explicitpublish) - [RemoveDrafts](#removedrafts) - [SyntaxHighlighting](#syntaxhighlighting) - [Syntax Highlighting](#syntax-highlighting) - [Explorer](#explorer) - [Private Pages](#private-pages) - [Table of Contents](#table-of-contents) - [Fonts](#fonts) - [Folder and Tag Listings](#folder-and-tag-listings) - [plugin/pagetype](#plugin-pagetype) - [Wikilinks](#wikilinks) - [Getting Started](#getting-started) - [CrawlLinks](#crawllinks) - [OxHugoFlavoredMarkdown](#oxhugoflavoredmarkdown) - [BasesPage](#basespage) - [Troubleshooting](#troubleshooting) - [Custom OG Images](#custom-og-images) - [Authoring Content](#authoring-content) - [ObsidianFlavoredMarkdown](#obsidianflavoredmarkdown) - [feature/transformer](#feature-transformer) - [plugin/emitter](#plugin-emitter) - [Installation](#installation) - [Migrating to Quartz 5](#migrating-to-quartz-5) - [Frontmatter](#frontmatter) - [Hosting](#hosting) - [What's New in Quartz 5](#what-s-new-in-quartz-5) - [EncryptedPages](#encryptedpages) - [Components](#components) - [feature](#feature) - [plugin/transformer](#plugin-transformer) - [Layout](#layout) - [plugin/component](#plugin-component) - [Feature List](#feature-list) - [Plugins](#plugins) - [Plugins](#plugins) - [Configuration](#configuration) - [Tag Index](#tag-index) - [Bases Support](#bases-support) - [Installation](#installation) - [Base](#base) - [Encrypted Pages Demo](#encrypted-pages-demo) --- # Welcome to Quartz 5 Welcome to Quartz 5 =================== Jun 11, 20263 min read Quartz is a fast, batteries-included static-site generator that transforms Markdown content into fully functional websites. Thousands of students, developers, and teachers are [already using Quartz](https://quartz.jzhao.xyz/showcase) to publish personal notes, websites, and [digital gardens](https://jzhao.xyz/posts/networked-thought) to the web. 🪴 Get Started[](https://quartz.jzhao.xyz/#-get-started) --------------------------------------------------------- Quartz requires **at least [Node](https://nodejs.org/) v22** and `npm` v10.9.2 to function correctly. Ensure you have these installed on your machine before continuing. See the [prerequisites](https://quartz.jzhao.xyz/getting-started/#prerequisites) for help installing them. > GitHub users > > You can also use the **[GitHub template](https://github.com/jackyzha0/quartz/generate) > ** to create your repository in one click, then clone that instead. See [Option A](https://quartz.jzhao.xyz/getting-started/installation#option-a-use-the-github-template-recommended) > in the installation guide. `# 1. Clone the Quartz repository git clone https://github.com/jackyzha0/quartz.git cd quartz # 2. Install dependencies npm i # 3. Initialize your site (choose a template, set your base URL, import content) npx quartz create # 4. Install plugins referenced by your chosen template npx quartz plugin install --from-config # 5. Preview your site locally npx quartz build --serve` Your site is now running at `http://localhost:8080`. From here: * **[Write content](https://quartz.jzhao.xyz/authoring-content) ** in the `content/` folder * **[Push to GitHub](https://quartz.jzhao.xyz/getting-started/installation) ** with `npx quartz sync` * **[Deploy](https://quartz.jzhao.xyz/hosting) ** to GitHub Pages, Cloudflare, Netlify, or Vercel For the full walkthrough, see the [Getting Started](https://quartz.jzhao.xyz/getting-started/) guide. ### Returning User?[](https://quartz.jzhao.xyz/#returning-user) Already have a Quartz repository and cloning it on a new machine? `git clone https://github.com//.git cd npm ci npx quartz plugin install npx quartz build --serve` > Tip > > If you hit build errors on a fresh clone, try `npx quartz plugin install --latest` to refresh plugins to their latest versions. See [troubleshooting > Plugins fail to build on a fresh clone](https://quartz.jzhao.xyz/troubleshooting#plugins-fail-to-build-on-a-fresh-clone) > for details. 🔧 Features[](https://quartz.jzhao.xyz/#-features) --------------------------------------------------- * [Obsidian compatibility](https://quartz.jzhao.xyz/features/obsidian-compatibility) , [full-text search](https://quartz.jzhao.xyz/features/full-text-search) , [graph view](https://quartz.jzhao.xyz/features/graph-view) , [wikilinks, transclusions](https://quartz.jzhao.xyz/features/wikilinks) , [Backlinks](https://quartz.jzhao.xyz/plugins/backlinks) , [Latex](https://quartz.jzhao.xyz/features/latex) , [syntax highlighting](https://quartz.jzhao.xyz/features/syntax-highlighting) , [popover previews](https://quartz.jzhao.xyz/features/popover-previews) , [Docker Support](https://quartz.jzhao.xyz/features/docker-support) , [internationalization](https://quartz.jzhao.xyz/features/i18n) , [comments](https://quartz.jzhao.xyz/features/comments) and [many more](https://quartz.jzhao.xyz/features/) right out of the box * Hot-reload on configuration edits and incremental rebuilds for content edits * Simple JSX layouts and [page components](https://quartz.jzhao.xyz/advanced/creating-components) * [Ridiculously fast page loads](https://quartz.jzhao.xyz/features/spa-routing) and tiny bundle sizes * Fully-customizable parsing, filtering, and page generation through [plugins](https://quartz.jzhao.xyz/advanced/making-plugins) For a comprehensive list of features, visit the [features page](https://quartz.jzhao.xyz/features/) . You can read more about the _why_ behind these features on the [philosophy](https://quartz.jzhao.xyz/philosophy) page and a technical overview on the [architecture](https://quartz.jzhao.xyz/advanced/architecture) page. ### 🚧 Troubleshooting + Updating[](https://quartz.jzhao.xyz/#-troubleshooting--updating) Having trouble with Quartz? Try searching for your issue using the search feature or check the [troubleshooting](https://quartz.jzhao.xyz/troubleshooting) page. If you haven’t already, [upgrade](https://quartz.jzhao.xyz/upgrading) to the newest version of Quartz to see if this fixes your issue. If you’re still having trouble, feel free to [submit an issue](https://github.com/jackyzha0/quartz/issues) if you feel you found a bug or ask for help in our [Discord Community](https://discord.gg/cRFFHYye7t) . You can also browse the [community](https://quartz.jzhao.xyz/community) page for third-party plugins and resources. * * * --- # Architecture Architecture ============ Apr 03, 20269 min read Quartz is a static site generator. How does it work? This question is best answered by tracing what happens when a user (you!) runs `npx quartz build` in the command line: On the server[](https://quartz.jzhao.xyz/advanced/architecture#on-the-server) ------------------------------------------------------------------------------ 1. After running `npx quartz build`, npm will look at `package.json` to find the `bin` entry for `quartz` which points at `./quartz/bootstrap-cli.mjs`. 2. This file has a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line at the top which tells npm to execute it using Node. 3. `bootstrap-cli.mjs` is responsible for a few things: 1. Parsing the command-line arguments using [yargs](http://yargs.js.org/) . The `plugin` subcommand is also handled here for managing external plugins. 2. Transpiling and bundling the rest of Quartz (which is in Typescript) to regular JavaScript using [esbuild](https://esbuild.github.io/) . The `esbuild` configuration here is slightly special as it also handles `.scss` file imports using [esbuild-sass-plugin v2](https://www.npmjs.com/package/esbuild-sass-plugin) . Additionally, we bundle ‘inline’ client-side scripts (any `.inline.ts` file) that components declare using a custom `esbuild` plugin that runs another instance of `esbuild` which bundles for the browser instead of `node`. Modules of both types are imported as plain text. 3. Running the local preview server if `--serve` is set. This starts two servers: 1. A WebSocket server on port 3001 to handle hot-reload signals. This tracks all inbound connections and sends a ‘rebuild’ message a server-side change is detected (either content or configuration). 2. An HTTP file-server on a user defined port (normally 8080) to serve the actual website files. 4. If the `--serve` flag is set, it also starts a file watcher to detect source-code changes (e.g. anything that is `.ts`, `.tsx`, `.scss`, or packager files). On a change, we rebuild the module (step 2 above) using esbuild’s [rebuild API](https://esbuild.github.io/api/#rebuild) which drastically reduces the build times. 5. After transpiling the main Quartz build module (`quartz/build.ts`), we write it to a cache file `.quartz-cache/transpiled-build.mjs` and then dynamically import this using `await import(cacheFile)`. However, we need to be pretty smart about how to bust Node’s [import cache](https://github.com/nodejs/modules/issues/307) so we add a random query string to fake Node into thinking it’s a new module. This does, however, cause memory leaks so we just hope that the user doesn’t hot-reload their configuration too many times in a single session :)) (it leaks about ~350kB memory on each reload). After importing the module, we then invoke it, passing in the command line arguments we parsed earlier along with a callback function to signal the client to refresh. 4. In `build.ts`, we start by installing source map support manually to account for the query string cache busting hack we introduced earlier. Then, we start processing content: 1. Clean the output directory. 2. Recursively glob all files in the `content` folder, respecting the `.gitignore`. 3. Parse the Markdown files. 1. Quartz detects the number of threads available and chooses to spawn worker threads if there are >128 pieces of content to parse (rough heuristic). If it needs to spawn workers, it will invoke esbuild again to transpile the worker script `quartz/worker.ts`. Then, a work-stealing [workerpool](https://www.npmjs.com/package/workerpool) is then created and batches of 128 files are assigned to workers. 2. Each worker (or just the main thread if there is no concurrency) creates a [unified](https://github.com/unifiedjs/unified) parser based off of the plugins defined in the [configuration](https://quartz.jzhao.xyz/configuration) . 3. Parsing has three steps: 1. Read the file into a [vfile](https://github.com/vfile/vfile) . 2. Applied plugin-defined text transformations over the content. 3. Slugify the file path and store it in the data for the file. See the page on [paths](https://quartz.jzhao.xyz/advanced/paths) for more details about how path logic works in Quartz (spoiler: its complicated). 4. Markdown parsing using [remark-parse](https://www.npmjs.com/package/remark-parse) (text to [mdast](https://github.com/syntax-tree/mdast) ). 5. Apply plugin-defined Markdown-to-Markdown transformations. 6. Convert Markdown into HTML using [remark-rehype](https://github.com/remarkjs/remark-rehype) ([mdast](https://github.com/syntax-tree/mdast) to [hast](https://github.com/syntax-tree/hast) ). 7. Apply plugin-defined HTML-to-HTML transformations. 4. Filter out unwanted content using plugins. 5. Emit files using plugins. 1. Gather all the static resources (e.g. external CSS, JS modules, etc.) each emitter plugin declares. 2. Emitters that emit HTML files do a bit of extra work here as they need to transform the [hast](https://github.com/syntax-tree/hast) produced in the parse step to JSX. This is done using [hast-util-to-jsx-runtime](https://github.com/syntax-tree/hast-util-to-jsx-runtime) with the [Preact](https://preactjs.com/) runtime. Finally, the JSX is rendered to HTML using [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) which statically renders the JSX to HTML (i.e. doesn’t care about `useState`, `useEffect`, or any other React/Preact interactive bits). Here, we also do a bunch of fun stuff like assemble the page [layout](https://quartz.jzhao.xyz/layout) from `quartz.config.yaml`, assemble all the inline scripts that actually get shipped to the client, and all the transpiled styles. The bulk of this logic can be found in `quartz/components/renderPage.tsx`. Other fun things of note: 1. CSS is minified and transformed using [Lightning CSS](https://github.com/parcel-bundler/lightningcss) to add vendor prefixes and do syntax lowering. 2. Scripts are split into `beforeDOMLoaded` and `afterDOMLoaded` and are inserted in the `` and `` respectively. 3. Finally, each emitter plugin is responsible for emitting and writing it’s own emitted files to disk. 6. If the `--serve` flag was detected, we also set up another file watcher to detect content changes (only `.md` files). We keep a content map that tracks the parsed AST and plugin data for each slug and update this on file changes. Newly added or modified paths are rebuilt and added to the content map. Then, all the filters and emitters are run over the resulting content map. This file watcher is debounced with a threshold of 250ms. On success, we send a client refresh signal using the passed in callback function. On the client[](https://quartz.jzhao.xyz/advanced/architecture#on-the-client) ------------------------------------------------------------------------------ 1. The browser opens a Quartz page and loads the HTML. The `` also links to page styles (emitted to `public/index.css`) and page-critical JS (emitted to `public/prescript.js`) 2. Then, once the body is loaded, the browser loads the non-critical JS (emitted to `public/postscript.js`) 3. Once the page is done loading, the page will then dispatch a custom synthetic browser event `"nav"`. This is used so client-side scripts declared by components can ‘setup’ anything that requires access to the page DOM. 1. If the [enableSPA option](https://quartz.jzhao.xyz/features/spa-routing) is enabled in the [configuration](https://quartz.jzhao.xyz/configuration) , this `"nav"` event is also fired on any client-navigation to allow for components to unregister and reregister any event handlers and state. 2. If it’s not, we wire up the `"nav"` event to just be fired a single time after page load to allow for consistency across how state is setup across both SPA and non-SPA contexts. 3. A separate `"render"` event can be dispatched when the DOM is updated in-place without a full navigation (e.g. after content decryption). Components that attach listeners to content elements should listen for both `"nav"` and `"render"`. Community Package Layering[](https://quartz.jzhao.xyz/advanced/architecture#community-package-layering) -------------------------------------------------------------------------------------------------------- Quartz v5 separates shared code into three community packages, each with a distinct responsibility: * **`@quartz-community/types`** — Type definitions, interfaces, and the canonical `vfile` DataMap augmentation. This is the “contract” between Quartz and plugins. It has no runtime dependencies. * **`@quartz-community/utils`** — Shared utility functions (path manipulation, DOM helpers, sorting, date formatting, JSX conversion, etc.). Depends on `@quartz-community/types`. * **`@quartz-community/runtime`** — Browser-only utilities for client-side scripts (event handling, navigation, storage, script loading). Depends on both `types` and `utils`. `types (no deps) ↑ utils (depends on types) ↑ runtime (depends on types + utils) ↑ plugins (depend on any combination)` Plugins should import types from `@quartz-community/types`, utility functions from `@quartz-community/utils`, and browser utilities from `@quartz-community/runtime`. This layering ensures plugins don’t depend on Quartz core. Plugin System[](https://quartz.jzhao.xyz/advanced/architecture#plugin-system) ------------------------------------------------------------------------------ Page types define how a category of pages is rendered. They are configured in the `pageTypes` array in `quartz.config.yaml`. Quartz v5 introduces a community plugin system. Plugins are standalone Git repositories that are cloned into `.quartz/plugins/` and re-exported through an auto-generated index file at `.quartz/plugins/index.ts`. ### Plugin Types[](https://quartz.jzhao.xyz/advanced/architecture#plugin-types) There are now four plugin categories: * **Transformers**: Map over content (parse frontmatter, generate descriptions, syntax highlighting) * **Filters**: Filter content (remove drafts, explicit publish) * **Emitters**: Reduce over content (generate RSS, sitemaps, alias redirects, OG images) * **Page Types**: Define how pages are rendered. Each page type handles a specific kind of page (content notes, folder listings, tag listings, 404). The `PageTypeDispatcher` emitter routes pages to the appropriate page type plugin based on the content. * **Bases Views**: Custom view renderers for the `bases-page` plugin’s database-like view system. Plugins can register new view types (e.g., timeline, kanban) via the `ViewRegistry`. See [making plugins > Bases Views](https://quartz.jzhao.xyz/advanced/making-plugins#bases-views) for details. Note that plugin types are **not mutually exclusive** — a single plugin can be a transformer AND provide components (e.g., `obsidian-flavored-markdown`), or be a page type AND provide custom frames (e.g., `canvas-page`). ### Plugin Resolution[](https://quartz.jzhao.xyz/advanced/architecture#plugin-resolution) When `npx quartz plugin add github:quartz-community/explorer` is run: 1. The repository is cloned into `.quartz/plugins/explorer/` 2. The plugin is built using `tsup` (defined in each plugin’s `tsup.config.ts`) 3. An auto-generated `.quartz/plugins/index.ts` re-exports all installed plugins 4. The plugin’s commit hash is recorded in `quartz.lock.json` ### Plugin CLI Commands[](https://quartz.jzhao.xyz/advanced/architecture#plugin-cli-commands) * `npx quartz plugin add github:quartz-community/` — Install a community plugin * `npx quartz plugin install --latest` — Update all plugins to latest commits * `npx quartz plugin install --clean` — Restore plugins from locked commits in `quartz.lock.json` (used in CI/CD) * `npx quartz plugin remove ` — Remove an installed plugin ### Plugin Structure[](https://quartz.jzhao.xyz/advanced/architecture#plugin-structure) Each community plugin repository contains: * `src/index.ts` — Plugin entry point exporting the plugin function * `tsup.config.ts` — Build configuration using tsup * `package.json` — Declares dependencies on `@quartz-community/types` and `@quartz-community/utils` The architecture and design of the plugin system was intentionally left pretty vague here as this is described in much more depth in the guide on [creating plugins](https://quartz.jzhao.xyz/advanced/making-plugins) . Page Frames[](https://quartz.jzhao.xyz/advanced/architecture#page-frames) -------------------------------------------------------------------------- Page frames control the inner HTML structure of each page. While the outer shell (``, ``, ``, `#quartz-root`) is always the same (required for [SPA Routing](https://quartz.jzhao.xyz/features/spa-routing) ), the frame determines how layout slots are arranged inside the page. The frame system lives in `quartz/components/frames/` and consists of: * `types.ts` — Defines the `PageFrame` and `PageFrameProps` interfaces * `DefaultFrame.tsx` — Three-column layout (left sidebar, center, right sidebar, footer) * `FullWidthFrame.tsx` — No sidebars, single center column * `MinimalFrame.tsx` — No sidebars, no header/beforeBody, just content and footer * `registry.ts` — `FrameRegistry` singleton for plugin-registered frames * `index.ts` — `resolveFrame()` function and built-in frame registry ### Frame Registry[](https://quartz.jzhao.xyz/advanced/architecture#frame-registry) The `FrameRegistry` (`quartz/components/frames/registry.ts`) is a singleton that stores frames registered by community plugins. It mirrors the design of the `ComponentRegistry`. Plugins declare frames in their `package.json` manifest under the `"quartz"."frames"` field, and these are loaded by `quartz/plugins/loader/frameLoader.ts` during plugin initialization. ### Frame Resolution[](https://quartz.jzhao.xyz/advanced/architecture#frame-resolution) The rendering pipeline in `quartz/components/renderPage.tsx` delegates to the resolved frame’s `render()` function. Frame resolution happens in the `PageTypeDispatcher` emitter (`quartz/plugins/pageTypes/dispatcher.ts`) using this priority: 1. YAML config: `layout.byPageType..template` 2. Plugin-registered frame: looked up by name in the `FrameRegistry` 3. Built-in frame: looked up by name in the `builtinFrames` map 4. Fallback: `"default"` The active frame name is set as a `data-frame` attribute on the `.page` element, enabling frame-specific CSS overrides in `quartz/styles/base.scss`. ### Plugin-Provided Frames[](https://quartz.jzhao.xyz/advanced/architecture#plugin-provided-frames) Community plugins can ship their own frames by exporting them from a `./frames` subpath and declaring them in the plugin manifest. For example, the `canvas-page` plugin provides a `"canvas"` frame with a fullscreen layout and togglable sidebar. See [making plugins > Providing Custom Frames](https://quartz.jzhao.xyz/advanced/making-plugins#providing-custom-frames) for implementation details. See [layout > Page Frames](https://quartz.jzhao.xyz/layout#page-frames) for user-facing documentation and [making plugins > Page Types](https://quartz.jzhao.xyz/advanced/making-plugins#page-types) for how to set frames in page type plugins. * * * --- # Creating Component Plugins Creating Component Plugins ========================== Apr 03, 20267 min read > Warning > > This guide assumes you have experience writing JavaScript and are familiar with TypeScript. Normally on the web, we write layout code using HTML which looks something like the following:

An article header

Some content

This piece of HTML represents an article with a leading header that says “An article header” and a paragraph that contains the text “Some content”. This is combined with CSS to style the page and JavaScript to add interactivity. However, HTML doesn’t let you create reusable templates. If you wanted to create a new page, you would need to copy and paste the above snippet and edit the header and content yourself. This isn’t great if we have a lot of content on our site that shares a lot of similar layout. The smart people who created React also had similar complaints and invented the concept of Components — JavaScript functions that return JSX — to solve the code duplication problem. In effect, components allow you to write a JavaScript function that takes some data and produces HTML as an output. **While Quartz doesn’t use React, it uses the same component concept to allow you to easily express layout templates in your Quartz site.** Community Component Plugins[](https://quartz.jzhao.xyz/advanced/creating-components#community-component-plugins) ----------------------------------------------------------------------------------------------------------------- In v5, most components are community plugins — standalone repositories that export a `QuartzComponent`. These plugins are decoupled from the core Quartz repository, allowing for easier maintenance and sharing. ### Getting Started[](https://quartz.jzhao.xyz/advanced/creating-components#getting-started) To create a new component plugin, you can use the official plugin template: git clone https://github.com/quartz-community/plugin-template.git my-component cd my-component npm install ### Plugin Structure[](https://quartz.jzhao.xyz/advanced/creating-components#plugin-structure) A component plugin’s `src/index.ts` typically exports a function (a constructor) that returns a `QuartzComponent`. This allows users to pass configuration options to your component. src/index.ts import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps, } from "@quartz-community/types" interface Options { favouriteNumber: number } const defaultOptions: Options = { favouriteNumber: 42, } const MyComponent: QuartzComponentConstructor = (userOpts?: Options) => { const opts = { ...defaultOptions, ...userOpts } const Component: QuartzComponent = (props: QuartzComponentProps) => { if (opts.favouriteNumber < 0) return null return

My favourite number is {opts.favouriteNumber}

} return Component } export default MyComponent ### Props[](https://quartz.jzhao.xyz/advanced/creating-components#props) All Quartz components accept the same set of props: export type QuartzComponentProps = { fileData: QuartzPluginData cfg: GlobalConfiguration tree: Node allFiles: QuartzPluginData[] displayClass?: "mobile-only" | "desktop-only" } * `fileData`: Any metadata plugins may have added to the current page. * `fileData.slug`: slug of the current page. * `fileData.frontmatter`: any frontmatter parsed. * `cfg`: The `configuration` field in `quartz.config.yaml`. * `tree`: the resulting [HTML AST](https://github.com/syntax-tree/hast) after processing and transforming the file. * `allFiles`: Metadata for all files that have been parsed. Useful for doing page listings or figuring out the overall site structure. * `displayClass`: a utility class that indicates a preference from the user about how to render it in a mobile or desktop setting. ### Styling[](https://quartz.jzhao.xyz/advanced/creating-components#styling) In community plugins, styles are bundled with the plugin. You can define styles using the `.css` property on the component: Component.css = ` .my-component { color: red; } ` For SCSS, you can import it and assign it to the `.css` property. The build system will handle the transformation: import styles from "./styles.scss" Component.css = styles > Warning > > Quartz does not use CSS modules so any styles you declare here apply _globally_. If you only want it to apply to your component, make sure you use specific class names and selectors. ### Internationalization[](https://quartz.jzhao.xyz/advanced/creating-components#internationalization) Component plugins should use the i18n pattern for any user-facing strings. See [making plugins > Internationalization (i18n)](https://quartz.jzhao.xyz/advanced/making-plugins#internationalization-i18n) for the full setup guide. Quick reference: import { i18n } from "../i18n" const MyComponent: QuartzComponent = ({ cfg }) => { const t = i18n(cfg.locale ?? "en-US").components.myComponent return

{t.title}

} Always provide at least an `en-US` locale as the fallback. Additional locales are optional but encouraged for international reach. ### Scripts and Interactivity[](https://quartz.jzhao.xyz/advanced/creating-components#scripts-and-interactivity) For interactivity, you can declare `.beforeDOMLoaded` and `.afterDOMLoaded` properties on the component. These should be strings containing the JavaScript to be executed in the browser. * `.beforeDOMLoaded`: Executed _before_ the page is done loading. Used for prefetching or early initialization. * `.afterDOMLoaded`: Executed once the page has been completely loaded. If you need to create an `afterDOMLoaded` script that depends on page-specific elements that may change when navigating, listen for the `"nav"` event: document.addEventListener("nav", () => { // do page specific logic here const toggleSwitch = document.querySelector("#switch") as HTMLInputElement if (toggleSwitch) { toggleSwitch.addEventListener("change", switchTheme) window.addCleanup(() => toggleSwitch.removeEventListener("change", switchTheme)) } }) You can also use the `"prenav"` event, which fires before the page is replaced during SPA navigation. The `"render"` event fires when the DOM has been updated in-place without a full navigation — for example, after content decryption or dynamic DOM modifications by other plugins. If your component attaches event listeners to content elements, listen for `"render"` in addition to `"nav"` to ensure re-initialization: function setupMyComponent() { const elements = document.querySelectorAll(".my-interactive") for (const el of elements) { el.addEventListener("click", handleClick) window.addCleanup(() => el.removeEventListener("click", handleClick)) } } document.addEventListener("nav", setupMyComponent) document.addEventListener("render", setupMyComponent) It is best practice to track any event handlers via `window.addCleanup` to prevent memory leaks during SPA navigation. #### Importing Code[](https://quartz.jzhao.xyz/advanced/creating-components#importing-code) In community plugins, TypeScript scripts should be transpiled at build time. The plugin template includes an `inlineScriptPlugin` in `tsup.config.ts` that automatically transpiles `.inline.ts` files imported as text: src/index.ts import script from "./script.inline.ts" const Component: QuartzComponent = (props) => { return } Component.afterDOMLoaded = script The `inlineScriptPlugin` handles transpiling TypeScript to browser-compatible JavaScript during the build step, allowing you to write type-safe client-side code. ### Installing Your Component[](https://quartz.jzhao.xyz/advanced/creating-components#installing-your-component) Once your component is published (e.g., to GitHub or npm), users can install it using the Quartz CLI: npx quartz plugin add github:your-username/my-component Then, they can add it to their `quartz.config.yaml`: quartz.config.yaml plugins: - source: github:your-username/my-component enabled: true options: favouriteNumber: 42 layout: position: left priority: 60 For advanced usage via the TS override in `quartz.ts`: quartz.ts (override) import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" import Plugin from "./.quartz/plugins" const config = await loadQuartzConfig() export default config export const layout = await loadQuartzLayout({ byPageType: { content: { left: [Plugin.MyComponent({ favouriteNumber: 42 })], }, }, }) ### Receiving YAML Options in Component-Only Plugins[](https://quartz.jzhao.xyz/advanced/creating-components#receiving-yaml-options-in-component-only-plugins) Component plugins that also belong to a processing category (transformer, filter, emitter, page type) receive options through their factory function automatically. However, **component-only plugins** — those whose manifest declares only `"category": ["component"]` — are loaded via side-effect import and don’t go through the factory path. To receive YAML options in a component-only plugin, export an `init` function from your entry point: src/index.ts export function init(options?: Record): void { // options contains merged defaultOptions + user's YAML options const myFlag = (options?.myFlag as boolean) ?? false // Use options to configure registrations, global state, etc. } Quartz’s config-loader calls `init()` after importing the module, passing the merged result of your manifest’s `defaultOptions` and the user’s `options` from `quartz.config.yaml`. The merge follows the same `{ ...defaultOptions, ...userOptions }` pattern used for processing plugins — user values take precedence. Declare your defaults in `package.json`: package.json { "quartz": { "category": ["component"], "defaultOptions": { "myFlag": false } } } If your plugin does not export `init`, it continues to work as a pure side-effect import — this is fully backward compatible. Internal Components[](https://quartz.jzhao.xyz/advanced/creating-components#internal-components) ------------------------------------------------------------------------------------------------- Quartz also has internal components that provide layout utilities. These live in `quartz/components/` and are primarily used for structural purposes: * `Component.Head()` — renders the `` tag * `Component.Spacer()` — adds flexible space * `Component.Flex()` — flexible layout container * `Component.MobileOnly()` — shows component only on mobile * `Component.DesktopOnly()` — shows component only on desktop * `Component.ConditionalRender()` — conditionally renders based on page data See [layout-components](https://quartz.jzhao.xyz/layout-components) for more details on these utilities. > Hint > > Look at existing community plugins like [Explorer](https://github.com/quartz-community/explorer) > or [Darkmode](https://github.com/quartz-community/darkmode) > for real-world examples. * * * --- # Advanced Advanced ======== Mar 14, 20261 min read This section covers advanced topics for users who want to extend or deeply customize Quartz. * **[architecture](https://quartz.jzhao.xyz/advanced/architecture) ** — How Quartz works under the hood: the parse, filter, and emit pipeline * **[making plugins](https://quartz.jzhao.xyz/advanced/making-plugins) ** — Build your own transformer, filter, emitter, or component plugin * **[creating components](https://quartz.jzhao.xyz/advanced/creating-components) ** — Create custom layout components with JSX * **[paths](https://quartz.jzhao.xyz/advanced/paths) ** — How Quartz resolves and transforms file paths 4 items under this folder. * Jul 27, 2026 ### [Making your own plugins](https://quartz.jzhao.xyz/advanced/making-plugins) * Apr 03, 2026 ### [Architecture](https://quartz.jzhao.xyz/advanced/architecture) * Apr 03, 2026 ### [Creating Component Plugins](https://quartz.jzhao.xyz/advanced/creating-components) * May 29, 2024 ### [Paths in Quartz](https://quartz.jzhao.xyz/advanced/paths) * * * --- # Paths in Quartz Paths in Quartz =============== May 29, 20243 min read Paths are pretty complex to reason about because, especially for a static site generator, they can come from so many places. A full file path to a piece of content? Also a path. What about a slug for a piece of content? Yet another path. It would be silly to type these all as `string` and call it a day as it’s pretty common to accidentally mistake one type of path for another. Unfortunately, TypeScript does not have [nominal types](https://en.wikipedia.org/wiki/Nominal_type_system) for type aliases meaning even if you made custom types of a server-side slug or a client-slug slug, you can still accidentally assign one to another and TypeScript wouldn’t catch it. Luckily, we can mimic nominal typing using [brands](https://www.typescriptlang.org/play#example/nominal-typing) . // instead of type FullSlug = string // we do type FullSlug = string & { __brand: "full" } // that way, the following will fail typechecking const slug: FullSlug = "some random string" While this prevents most typing mistakes _within_ our nominal typing system (e.g. mistaking a server slug for a client slug), it doesn’t prevent us from _accidentally_ mistaking a string for a client slug when we forcibly cast it. Thus, we still need to be careful when casting from a string to one of these nominal types in the ‘entrypoints’, illustrated with hexagon shapes in the diagram below. The following diagram draws the relationships between all the path sources, nominal path types, and what functions in `quartz/path.ts` convert between them. `graph LR Browser{{Browser}} --> Window{{Body}} & LinkElement{{Link Element}} Window --"getFullSlug()"--> FullSlug[Full Slug] LinkElement --".href"--> Relative[Relative URL] FullSlug --"simplifySlug()" --> SimpleSlug[Simple Slug] SimpleSlug --"pathToRoot()"--> Relative SimpleSlug --"resolveRelative()" --> Relative MD{{Markdown File}} --> FilePath{{File Path}} & Links[Markdown links] Links --"transformLink()"--> Relative FilePath --"slugifyFilePath()"--> FullSlug[Full Slug] style FullSlug stroke-width:4px` Here are the main types of slugs with a rough description of each type of path: * `FilePath`: a real file path to a file on disk. Cannot be relative and must have a file extension. * `FullSlug`: cannot be relative and may not have leading or trailing slashes. It can have `index` as it’s last segment. Use this wherever possible is it’s the most ‘general’ interpretation of a slug. * `SimpleSlug`: cannot be relative and shouldn’t have `/index` as an ending or a file extension. It _can_ however have a trailing slash to indicate a folder path. * `RelativeURL`: must start with `.` or `..` to indicate it’s a relative URL. Shouldn’t have `/index` as an ending or a file extension but can contain a trailing slash. To get a clearer picture of how these relate to each other, take a look at the path tests in `quartz/util/path.test.ts`. * * * --- # quartz build quartz build ============ Properties1 | | | | --- | --- | | aliases | build | Apr 11, 20262 min read The `build` command transforms your Markdown content into a static HTML website. It processes your files through the configured plugins and outputs the final site to a directory of your choice. Flags[](https://quartz.jzhao.xyz/cli/build#flags) -------------------------------------------------- | Flag | Shorthand | Description | Default | | --- | --- | --- | --- | | `--directory` | `-d` | The directory containing your Quartz project | Current directory | | `--verbose` | `-v` | Enable detailed logging for debugging | `false` | | `--output` | `-o` | The directory where the built site will be saved | `public` | | `--serve` | | Start a local development server | `false` | | `--watch` | | Rebuild the site when files change | `false` | | `--port` | | The port for the development server | `8080` | | `--wsPort` | | The port for the WebSocket hot-reload server | `3001` | | `--baseDir` | | Set a base directory for the site (e.g. for GitHub Pages) | `/` | | `--remoteDevHost` | | The hostname to use for the development server | `localhost` | | `--bundleInfo` | | Output a JSON file with bundle size information | `false` | | `--concurrency` | `-c` | Number of worker threads to use for building | CPU core count | Examples[](https://quartz.jzhao.xyz/cli/build#examples) -------------------------------------------------------- ### Basic Build[](https://quartz.jzhao.xyz/cli/build#basic-build) Generate your site into the `public` folder. npx quartz build ### Development Mode[](https://quartz.jzhao.xyz/cli/build#development-mode) Start a local server and watch for changes. This is the most common way to preview your site while writing. npx quartz build --serve ### Custom Output and Port[](https://quartz.jzhao.xyz/cli/build#custom-output-and-port) Build to a specific folder and run the server on a different port. npx quartz build --serve --output dist --port 3000 ### Performance Tuning[](https://quartz.jzhao.xyz/cli/build#performance-tuning) If you have a very large vault, you can limit the number of concurrent workers to save memory. npx quartz build --concurrency 2 Serve vs Watch[](https://quartz.jzhao.xyz/cli/build#serve-vs-watch) -------------------------------------------------------------------- The `--serve` and `--watch` flags control different behaviors: * **`--serve`** starts a local development server AND automatically watches for changes (implies `--watch`). This is the recommended mode for local development. * **`--watch`** only watches for file changes and rebuilds automatically, without starting a server. This is useful for CI pipelines or custom server setups where you want automatic rebuilds but handle serving separately. In most cases, you want `--serve`: npx quartz build --serve Development Server[](https://quartz.jzhao.xyz/cli/build#development-server) ---------------------------------------------------------------------------- The `--serve` flag starts a local web server. This server is intended for development and previewing only. It is not designed for production use. For information on how to deploy your site, see [hosting](https://quartz.jzhao.xyz/hosting) . ### Hot Reloading[](https://quartz.jzhao.xyz/cli/build#hot-reloading) When running with `--serve`, Quartz automatically enables `--watch`. It uses a WebSocket connection (on the port specified by `--wsPort`) to notify your browser when a file has changed. The browser will then automatically refresh to show the latest version of your content. * * * --- # quartz create quartz create ============= May 24, 20264 min read The `create` command initializes a new Quartz project. It helps you set up your content folder, choose a configuration template, set your site’s base URL, and configure how Quartz should handle your Markdown files. Flags[](https://quartz.jzhao.xyz/cli/create#flags) --------------------------------------------------- | Flag | Shorthand | Description | | --- | --- | --- | | `--template` | `-t` | Configuration template (`default`, `obsidian`, `ttrpg`, or `blog`) | | `--directory` | `-d` | The directory where Quartz will be initialized | | `--source` | `-s` | The source directory of your Markdown files | | `--strategy` | `-X` | How to handle the source files (`new`, `copy`, or `symlink`) | | `--links` | `-l` | How to resolve internal links (`absolute`, `shortest`, or `relative`) | | `--baseUrl` | `-b` | Base URL for your site (e.g. `mysite.github.io/quartz`) | | `--verbose` | `-v` | Enable detailed logging | Templates[](https://quartz.jzhao.xyz/cli/create#templates) ----------------------------------------------------------- When you run `quartz create`, you can choose a configuration template that pre-configures Quartz for your use case. The selected template always overwrites `quartz.config.yaml`, even if one already exists. After applying the template, Quartz automatically runs plugin resolution to install any plugins the template requires and remove any that are no longer referenced. * **Default**: A clean Quartz setup with sensible defaults. Best for starting from scratch. * **Obsidian**: Optimized for Obsidian vaults with full Obsidian Flavored Markdown support (wikilinks, callouts, mermaid diagrams, etc.). Automatically sets link resolution to `shortest` and skips the link resolution prompt. * **TTRPG**: Builds on the Obsidian template with the addition of the [Leaflet bases plugin](https://github.com/Requiae/quartz-leaflet-bases-plugin) and [ITS Theme](https://github.com/saberzero1/quartz-themes) (`its-theme.ttrpg-dnd`). Great for D&D and TTRPG wikis. Also skips the link resolution prompt. * **Blog**: A blog-focused setup with [recent notes](https://github.com/quartz-community/recent-notes) enabled (showing the 5 most recent posts with tags) and [comments](https://github.com/quartz-community/comments) enabled via giscus. You’ll need to fill in the `TODO:` placeholder values in `quartz.config.yaml` with your own giscus repository details. Base URL[](https://quartz.jzhao.xyz/cli/create#base-url) --------------------------------------------------------- During setup, Quartz will ask for the base URL of your site. This is the URL where your site will be deployed (e.g. `mysite.github.io/quartz`). * Do **not** include the protocol (`https://`) — if you do, it will be automatically stripped. * Trailing slashes are also removed automatically. * See [configuration](https://quartz.jzhao.xyz/configuration) for more details on how `baseUrl` is used. Strategies[](https://quartz.jzhao.xyz/cli/create#strategies) ------------------------------------------------------------- When you run `quartz create`, you must choose a strategy for your content: * **new**: Creates a fresh, empty content folder. Use this if you are starting a new project from scratch. * **copy**: Copies all files from your source directory into the Quartz content folder. This is the safest option for existing vaults as it doesn’t touch your original files. * **symlink**: Creates a symbolic link from the Quartz content folder to your source directory. Any changes you make in your source directory (e.g. in Obsidian) will be immediately reflected in Quartz. Link Resolution[](https://quartz.jzhao.xyz/cli/create#link-resolution) ----------------------------------------------------------------------- Quartz needs to know how to interpret the internal links in your Markdown files: * **shortest**: Resolves links to the closest matching file name. This is the default for Obsidian. * **absolute**: Resolves links relative to the root of your content folder. * **relative**: Resolves links relative to the current file’s location. > Note > > When using the **Obsidian** or **TTRPG** templates, link resolution is automatically set to `shortest` and the prompt is skipped. Interactive Walkthrough[](https://quartz.jzhao.xyz/cli/create#interactive-walkthrough) --------------------------------------------------------------------------------------- If you run `npx quartz create` without any arguments, it will guide you through an interactive setup: 1. **Choose a template**: Select a configuration template (`Default`, `Obsidian`, `TTRPG`, or `Blog`). 2. **Select a strategy**: Choose between `new`, `copy`, or `symlink`. 3. **Enter base URL**: Provide the URL where your site will be hosted. 4. **Select link resolution**: Choose how your links are formatted (skipped for Obsidian and TTRPG templates). 5. **Finish**: Quartz will set up the directory structure, create your configuration, and automatically install any plugins referenced in the template. Example: Importing an Obsidian Vault[](https://quartz.jzhao.xyz/cli/create#example-importing-an-obsidian-vault) ---------------------------------------------------------------------------------------------------------------- To create a Quartz project that links directly to an existing Obsidian vault: npx quartz create --template obsidian --strategy symlink --source ~/Documents/MyVault This command tells Quartz to use the Obsidian template (with full OFM support and shortest link resolution), look at your vault in `~/Documents/MyVault`, and use symbolic links so changes are synced. Example: Setting Up a Blog[](https://quartz.jzhao.xyz/cli/create#example-setting-up-a-blog) -------------------------------------------------------------------------------------------- To quickly set up a blog with recent notes and comments: npx quartz create --template blog --strategy new --baseUrl myblog.github.io After setup, edit `quartz.config.yaml` to fill in your giscus repository details in the comments plugin section. * * * --- # CLI Reference CLI Reference ============= Apr 17, 20262 min read The Quartz CLI is the primary way to interact with your Quartz project. It provides commands for creating new projects, building static sites, syncing with GitHub, and managing plugins. You can run the CLI using `npx quartz`. Quick Reference[](https://quartz.jzhao.xyz/cli/#quick-reference) ----------------------------------------------------------------- | Command | Description | Example | | --- | --- | --- | | `create` | Initialize a new Quartz project with template selection | `npx quartz create` | | `build` | Generate static HTML files | `npx quartz build` | | `sync` | Sync content with GitHub | `npx quartz sync` | | `upgrade` | Upgrade Quartz to the latest version (alias: `update`) | `npx quartz upgrade` | | `plugin` | Manage Quartz plugins (install, add, remove, etc.) | `npx quartz plugin list` | | `tui` | Launch the interactive plugin manager | `npx quartz tui` | Commands[](https://quartz.jzhao.xyz/cli/#commands) --------------------------------------------------- * [create](https://quartz.jzhao.xyz/cli/create) : Initialize a new Quartz project with a choice of templates (default, obsidian, ttrpg, blog) and base URL configuration. * [build](https://quartz.jzhao.xyz/build) : Build your Quartz site into static HTML. Includes a development server. * [sync](https://quartz.jzhao.xyz/cli/sync) : Push and pull changes between your local machine and GitHub. * [upgrade](https://quartz.jzhao.xyz/cli/upgrade) : Upgrade the Quartz framework to the latest version. Also available as `npx quartz update`. * [restore](https://quartz.jzhao.xyz/cli/restore) : Recover your content folder from the local cache. * [plugin](https://quartz.jzhao.xyz/cli/plugin) : Install, add, remove, prune, and configure plugins. Use `plugin install` with flags for lockfile/config sync, updates, and checks. * [tui](https://quartz.jzhao.xyz/cli/tui) : Use a terminal interface to manage plugins and layout. Global Flags[](https://quartz.jzhao.xyz/cli/#global-flags) ----------------------------------------------------------- These flags are accepted by every Quartz command: | Flag | Shorthand | Description | Default | | --- | --- | --- | --- | | `--directory` | `-d` | The directory containing your Quartz project | `content` | | `--verbose` | `-v` | Enable detailed logging for debugging | `false` | | `--concurrency` | `-c` | Max parallel workers for operations that run in parallel (e.g. `build`, `plugin install`, `plugin add`) | CPU core count | Commands that don’t perform parallel work accept `-c` as a no-op, so it’s always safe to pass. See [build](https://quartz.jzhao.xyz/build#performance-tuning) and [plugin](https://quartz.jzhao.xyz/cli/plugin#installing-on-low-end-hardware) for practical examples. Help and Versioning[](https://quartz.jzhao.xyz/cli/#help-and-versioning) ------------------------------------------------------------------------- To see a full list of available flags for any command, use the `--help` flag. npx quartz --help npx quartz build --help To check which version of Quartz you are currently running, use the `--version` flag. npx quartz --version 7 items under this folder. * May 24, 2026 ### [quartz create](https://quartz.jzhao.xyz/cli/create) * Apr 27, 2026 ### [quartz plugin](https://quartz.jzhao.xyz/cli/plugin) * Apr 17, 2026 ### [quartz sync](https://quartz.jzhao.xyz/cli/sync) * Apr 17, 2026 ### [quartz upgrade](https://quartz.jzhao.xyz/cli/upgrade) * Apr 11, 2026 ### [quartz build](https://quartz.jzhao.xyz/cli/build) * Mar 31, 2026 ### [quartz restore](https://quartz.jzhao.xyz/cli/restore) * Mar 14, 2026 ### [quartz tui](https://quartz.jzhao.xyz/cli/tui) * * * --- # quartz restore quartz restore ============== Mar 31, 20261 min read The `restore` command is a safety mechanism that allows you to recover your **content folder** from a local cache. This command only affects your Markdown files and does not restore plugins or configuration. To restore plugins to a specific state, use [npx quartz plugin install](https://quartz.jzhao.xyz/cli/plugin) . When to Use[](https://quartz.jzhao.xyz/cli/restore#when-to-use) ---------------------------------------------------------------- You should use `restore` if: * A `quartz upgrade` failed and corrupted your content. * You accidentally deleted files in your content folder. * You encountered complex merge conflicts that you want to undo. How it Works[](https://quartz.jzhao.xyz/cli/restore#how-it-works) ------------------------------------------------------------------ Quartz maintains a hidden cache of your content folder. Every time you run certain commands, Quartz ensures that a backup of your Markdown files exists. The `restore` command simply copies these files back into your main content directory. npx quartz restore Example Workflow[](https://quartz.jzhao.xyz/cli/restore#example-workflow) -------------------------------------------------------------------------- If an update fails and leaves your project in a broken state: 1. **Restore**: Run `npx quartz restore` to bring back your content. 2. **Clean**: Use Git to reset any other broken code files. 3. **Retry**: Attempt the update again or manually apply the changes you need. * * * --- # Making your own plugins Making your own plugins ======================= Jul 27, 202618 min read > Warning > > This part of the documentation will assume you have working knowledge in TypeScript and will include code snippets that describe the interface of what Quartz plugins should look like. Quartz’s plugins are a series of transformations over content. This is illustrated in the diagram of the processing pipeline below: ![](https://quartz.jzhao.xyz/images/quartz-transform-pipeline.png) All plugins are defined as a function that takes in a single parameter for options `type OptionType = object | undefined` and return an object that corresponds to the type of plugin it is. type OptionType = object | undefined type QuartzPlugin = (opts?: Options) => QuartzPluginInstance type QuartzPluginInstance = | QuartzTransformerPluginInstance | QuartzFilterPluginInstance | QuartzEmitterPluginInstance | QuartzPageTypePluginInstance The following sections will go into detail for what methods can be implemented for each plugin type. Before we do that, let’s clarify a few more ambiguous types: * `BuildCtx` is defined in `@quartz-community/types`. It consists of * `argv`: The command line arguments passed to the Quartz [build](https://quartz.jzhao.xyz/build) command * `cfg`: The full Quartz [configuration](https://quartz.jzhao.xyz/configuration) * `allSlugs`: a list of all the valid content slugs (see [paths](https://quartz.jzhao.xyz/advanced/paths) for more information on what a slug is) * `StaticResources` is defined in `@quartz-community/types`. It consists of * `css`: a list of CSS style definitions that should be loaded. A CSS style is described with the `CSSResource` type. It accepts either a source URL or the inline content of the stylesheet. * `js`: a list of scripts that should be loaded. A script is described with the `JSResource` type. It allows you to define a load time (either before or after the DOM has been loaded), whether it should be a module, and either the source URL or the inline content of the script. * `additionalHead`: a list of JSX elements or functions that return JSX elements to be added to the `` tag of the page. Functions receive the page’s data as an argument and can conditionally render elements. Getting Started[](https://quartz.jzhao.xyz/advanced/making-plugins#getting-started) ------------------------------------------------------------------------------------ In v5, plugins are standalone repositories. The easiest way to create one is using the plugin template: # Use the plugin template to create a new repository on GitHub # Then clone it locally git clone https://github.com/your-username/my-plugin.git cd my-plugin npm install The template provides the build configuration (`tsup.config.ts`), TypeScript setup, and correct package structure. Plugin Structure[](https://quartz.jzhao.xyz/advanced/making-plugins#plugin-structure) -------------------------------------------------------------------------------------- The basic file structure of a plugin is as follows: my-plugin/ ├── src/ │ └── index.ts # Plugin entry point ├── tsup.config.ts # Build configuration ├── package.json # Dependencies and exports └── tsconfig.json # TypeScript configuration The plugin’s `package.json` should declare dependencies on `@quartz-community/types` (for type definitions) and optionally `@quartz-community/utils` (for shared utilities). Plugin Types[](https://quartz.jzhao.xyz/advanced/making-plugins#plugin-types) ------------------------------------------------------------------------------ Choosing a Plugin Type[](https://quartz.jzhao.xyz/advanced/making-plugins#choosing-a-plugin-type) -------------------------------------------------------------------------------------------------- Quartz supports six plugin capabilities. A single plugin can combine multiple types. | I want to… | Plugin Type | | --- | --- | | Transform Markdown/HTML content | Transformer | | Decide which pages to publish | Filter | | Generate output files (RSS, sitemaps, manifests) | Emitter | | Define how a category of pages renders | Page Type | | Add a UI component to the layout | Component | | Add a custom view to the Bases database system | Bases View | These are **not mutually exclusive**. For example: * `obsidian-flavored-markdown` is both a **transformer** (processes OFM syntax) and provides **components** (mermaid rendering) * `canvas-page` is a **page type** that also provides a custom **frame** * A plugin could be a **transformer** that adds metadata AND a **component** that displays it ### Transformers[](https://quartz.jzhao.xyz/advanced/making-plugins#transformers) Transformers **map** over content, taking a Markdown file and outputting modified content or adding metadata to the file itself. export type QuartzTransformerPluginInstance = { name: string textTransform?: (ctx: BuildCtx, src: string) => string markdownPlugins?: (ctx: BuildCtx) => PluggableList htmlPlugins?: (ctx: BuildCtx) => PluggableList externalResources?: (ctx: BuildCtx) => Partial } All transformer plugins must define at least a `name` field to register the plugin and a few optional functions that allow you to hook into various parts of transforming a single Markdown file. * `textTransform` performs a text-to-text transformation _before_ a file is parsed into the [Markdown AST](https://github.com/syntax-tree/mdast) . * `markdownPlugins` defines a list of [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) . `remark` is a tool that transforms Markdown to Markdown in a structured way. * `htmlPlugins` defines a list of [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) . Similar to how `remark` works, `rehype` is a tool that transforms HTML to HTML in a structured way. * `externalResources` defines any external resources the plugin may need to load on the client-side for it to work properly. Normally for both `remark` and `rehype`, you can find existing plugins that you can use. If you’d like to create your own `remark` or `rehype` plugin, checkout the [guide to creating a plugin](https://unifiedjs.com/learn/guide/create-a-plugin/) using `unified` (the underlying AST parser and transformer library). A good example of a transformer plugin that borrows from the `remark` and `rehype` ecosystems is the [Latex](https://quartz.jzhao.xyz/plugins/latex) plugin: import remarkMath from "remark-math" import rehypeKatex from "rehype-katex" import rehypeMathjax from "rehype-mathjax/svg" import { QuartzTransformerPlugin } from "@quartz-community/types" interface Options { renderEngine: "katex" | "mathjax" } export const Latex: QuartzTransformerPlugin = (opts?: Options) => { const engine = opts?.renderEngine ?? "katex" return { name: "Latex", markdownPlugins() { return [remarkMath] }, htmlPlugins() { if (engine === "katex") { // if you need to pass options into a plugin, you // can use a tuple of [plugin, options] return [[rehypeKatex, { output: "html" }]] } else { return [rehypeMathjax] } }, externalResources() { if (engine === "katex") { return { css: [\ {\ // base css\ content: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css",\ },\ ], js: [\ {\ // fix copy behaviour: https://github.com/KaTeX/KaTeX/blob/main/contrib/copy-tex/README.md\ src: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/copy-tex.min.js",\ loadTime: "afterDOMReady",\ contentType: "external",\ },\ ], } } }, } } Another common thing that transformer plugins will do is parse a file and add extra data for that file: import { QuartzTransformerPlugin } from "@quartz-community/types" export const AddWordCount: QuartzTransformerPlugin = () => { return { name: "AddWordCount", markdownPlugins() { return [\ () => {\ return (tree, file) => {\ // tree is an `mdast` root element\ // file is a `vfile`\ const text = file.value\ const words = text.split(" ").length\ file.data.wordcount = words\ }\ },\ ] }, } } // tell typescript about our custom data fields we are adding // other plugins will then also be aware of this data field declare module "vfile" { interface DataMap { wordcount: number } } Finally, you can also perform transformations over Markdown or HTML ASTs using the `visit` function from the `unist-util-visit` package or the `findAndReplace` function from the `mdast-util-find-and-replace` package. import { visit } from "unist-util-visit" import { findAndReplace } from "mdast-util-find-and-replace" import { QuartzTransformerPlugin } from "@quartz-community/types" import { Link } from "mdast" export const TextTransforms: QuartzTransformerPlugin = () => { return { name: "TextTransforms", markdownPlugins() { return [\ () => {\ return (tree, file) => {\ // replace _text_ with the italics version\ findAndReplace(tree, /_(.+)_/, (_value: string, ...capture: string[]) => {\ // inner is the text inside of the () of the regex\ const [inner] = capture\ // return an mdast node\ // https://github.com/syntax-tree/mdast\ return {\ type: "emphasis",\ children: [{ type: "text", value: inner }],\ }\ })\ \ // remove all links (replace with just the link content)\ // match by 'type' field on an mdast node\ // https://github.com/syntax-tree/mdast#link in this example\ visit(tree, "link", (link: Link) => {\ return {\ type: "paragraph",\ children: [{ type: "text", value: link.title }],\ }\ })\ }\ },\ ] }, } } A parting word: transformer plugins are quite complex so don’t worry if you don’t get them right away. Take a look at the built in transformers and see how they operate over content to get a better sense for how to accomplish what you are trying to do. ### Filters[](https://quartz.jzhao.xyz/advanced/making-plugins#filters) Filters **filter** content, taking the output of all the transformers and determining what files to actually keep and what to discard. export type QuartzFilterPlugin = ( opts?: Options, ) => QuartzFilterPluginInstance export type QuartzFilterPluginInstance = { name: string shouldPublish(ctx: BuildCtx, content: ProcessedContent): boolean } A filter plugin must define a `name` field and a `shouldPublish` function that takes in a piece of content that has been processed by all the transformers and returns a `true` or `false` depending on whether it should be passed to the emitter plugins or not. For example, here is the built-in plugin for removing drafts: import { QuartzFilterPlugin } from "@quartz-community/types" export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({ name: "RemoveDrafts", shouldPublish(_ctx, [_tree, vfile]) { // uses frontmatter parsed from transformers const draftFlag: boolean = vfile.data?.frontmatter?.draft ?? false return !draftFlag }, }) ### Emitters[](https://quartz.jzhao.xyz/advanced/making-plugins#emitters) Emitters **reduce** over content, taking in a list of all the transformed and filtered content and creating output files. export type QuartzEmitterPlugin = ( opts?: Options, ) => QuartzEmitterPluginInstance export type QuartzEmitterPluginInstance = { name: string emit( ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources, ): Promise | AsyncGenerator partialEmit?( ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources, changeEvents: ChangeEvent[], ): Promise | AsyncGenerator | null getQuartzComponents(ctx: BuildCtx): QuartzComponent[] } An emitter plugin must define a `name` field, an `emit` function, and a `getQuartzComponents` function. It can optionally implement a `partialEmit` function for incremental builds. * `emit` is responsible for looking at all the parsed and filtered content and then appropriately creating files and returning a list of paths to files the plugin created. * `partialEmit` is an optional function that enables incremental builds. It receives information about which files have changed (`changeEvents`) and can selectively rebuild only the necessary files. This is useful for optimizing build times in development mode. If `partialEmit` is undefined, it will default to the `emit` function. * `getQuartzComponents` declares which Quartz components the emitter uses to construct its pages. Creating new files can be done via regular Node [fs module](https://nodejs.org/api/fs.html) (i.e. `fs.cp` or `fs.writeFile`) or via the `write` function in `@quartz-community/utils` if you are creating files that contain text. `write` has the following signature: export type WriteOptions = (data: { // the build context ctx: BuildCtx // the name of the file to emit (not including the file extension) slug: FullSlug // the file extension ext: `.${string}` | "" // the file content to add content: string }) => Promise This is a thin wrapper around writing to the appropriate output folder and ensuring that intermediate directories exist. If you choose to use the native Node `fs` APIs, ensure you emit to the `argv.output` folder as well. If you are creating an emitter plugin that needs to render components, there are three more things to be aware of: * Your component should use `getQuartzComponents` to declare a list of `QuartzComponents` that it uses to construct the page. See the page on [creating components](https://quartz.jzhao.xyz/advanced/creating-components) for more information. * You can use the `renderPage` function defined in `@quartz-community/utils` to render Quartz components into HTML. * If you need to render an HTML AST to JSX, you can use the `htmlToJsx` function from `@quartz-community/utils`. For example, the following is a simplified version of the content page plugin that renders every single page. import { QuartzEmitterPlugin, FullPageLayout, QuartzComponentProps } from "@quartz-community/types" import { renderPage, canonicalizeServer, pageResources, write } from "@quartz-community/utils" export const ContentPage: QuartzEmitterPlugin = () => { return { name: "ContentPage", getQuartzComponents(ctx) { const { head, header, beforeBody, pageBody, afterBody, left, right, footer } = ctx.cfg.layout return [head, ...header, ...beforeBody, pageBody, ...afterBody, ...left, ...right, footer] }, async emit(ctx, content, resources): Promise { const cfg = ctx.cfg.configuration const fps: FilePath[] = [] const allFiles = content.map((c) => c[1].data) for (const [tree, file] of content) { const slug = canonicalizeServer(file.data.slug!) const externalResources = pageResources(slug, file.data, resources) const componentData: QuartzComponentProps = { fileData: file.data, externalResources, cfg, children: [], tree, allFiles, } const content = renderPage(cfg, slug, componentData, {}, externalResources) const fp = await write({ ctx, content, slug: file.data.slug!, ext: ".html", }) fps.push(fp) } return fps }, } } Page types define how a category of pages is rendered. They are the primary way to add support for new file types or virtual pages in Quartz. export interface QuartzPageTypePluginInstance { name: string priority?: number fileExtensions?: string[] match: PageMatcher generate?: PageGenerator layout: string frame?: string body: QuartzComponentConstructor } * `name`: A unique identifier for this page type. * `priority`: Controls matching order when multiple page types could match a slug. Higher priority page types are checked first. Default: `0`. * `fileExtensions`: Array of file extensions this page type handles (e.g. `[".canvas"]`, `[".base"]`). Content files (`.md`) are handled by the default content page type. * `match`: A function that determines whether a given slug/file should be rendered by this page type. * `generate`: An optional function that produces virtual pages (pages not backed by files on disk, such as folder listings or tag indices). * `layout`: The layout configuration key (e.g. `"content"`, `"folder"`, `"tag"`). This determines which `byPageType` entry in `quartz.config.yaml` provides the layout overrides for this page type. * `frame`: The [page frame](https://quartz.jzhao.xyz/layout#page-frames) to use for this page type. Controls the overall HTML structure (e.g. `"default"`, `"full-width"`, `"minimal"`, or a custom frame provided by your plugin). If not set, defaults to `"default"`. Can be overridden per-page-type via `layout.byPageType..template` in `quartz.config.yaml`. * `body`: The Quartz component constructor that renders the page body content. ### Providing Custom Frames[](https://quartz.jzhao.xyz/advanced/making-plugins#providing-custom-frames) Plugins can ship their own [page frames](https://quartz.jzhao.xyz/layout#page-frames) — custom page layouts that control how the HTML structure (sidebars, header, content area, footer) is arranged. This is useful for page types that need fundamentally different layouts (e.g. a fullscreen canvas, a presentation mode, a dashboard). To provide a custom frame: **1\. Create the frame file:** src/frames/MyFrame.tsx import type { PageFrame, PageFrameProps } from "@quartz-community/types" import type { ComponentChildren } from "preact" export const MyFrame: PageFrame = { name: "my-frame", css: ` .page[data-frame="my-frame"] > #quartz-body { grid-template-columns: 1fr; grid-template-areas: "center"; } `, render({ componentData, pageBody: Content, footer: Footers }: PageFrameProps): unknown { const renderSlot = (C: (props: typeof componentData) => unknown): ComponentChildren => C(componentData) as ComponentChildren return (
{(Content as any)(componentData)} {Footers.map((Footer) => (Footer as any)(componentData))}
) }, } Key requirements: * `name`: A unique string identifier. This is what page types and YAML config reference. * `render()`: Receives all layout slots (header, sidebars, content, footer) and returns JSX for the inner page structure. Note that `footer` is a `QuartzComponent[]` (an array) — frames should iterate over it with `.map()` to render all footer components. * `css` (optional): Frame-specific CSS. Scope it with `.page[data-frame="my-frame"]` selectors to avoid conflicts. **2\. Re-export the frame:** src/frames/index.ts export { MyFrame } from "./MyFrame" **3\. Declare the frame in `package.json`:** package.json { "exports": { ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" }, "./frames": { "import": "./dist/frames/index.js", "types": "./dist/frames/index.d.ts" } }, "quartz": { "frames": { "MyFrame": { "exportName": "MyFrame" } } } } The `"frames"` field in the `"quartz"` manifest maps export names to frame metadata. The key (e.g. `"MyFrame"`) must match the export name in `src/frames/index.ts`. **4\. Add the frame entry point to your build config:** tsup.config.ts export default defineConfig({ entry: ["src/index.ts", "src/frames/index.ts"], // ... }) **5\. Reference the frame in your page type:** export const MyPageType: QuartzPageTypePlugin = () => ({ name: "MyPageType", frame: "my-frame", // References the frame by its name property // ... }) When a user installs your plugin, Quartz automatically loads the frame from the `./frames` export and registers it in the Frame Registry. The frame is then available by name in any page type or YAML config override. > Tip > > See the [`canvas-page`](https://github.com/quartz-community/canvas-page) > plugin for a complete real-world example of a plugin-provided frame. ### Bases Views[](https://quartz.jzhao.xyz/advanced/making-plugins#bases-views) The `bases-page` plugin provides a database-like view system similar to Obsidian Bases. Other plugins can register custom view types via the `ViewRegistry`: import { viewRegistry } from "@quartz-community/bases-page"; import type { ViewTypeRegistration } from "@quartz-community/bases-page"; viewRegistry.register({ id: "timeline", name: "Timeline", icon: "git-branch", render: ({ entries, view, slug, allSlugs }) => (
{entries.map(entry =>
{entry.properties.title}
)}
), css: `.bases-timeline { display: flex; flex-direction: column; }`, afterDOMLoaded: `document.addEventListener("nav", () => { /* setup */ })`, }); Each view registration includes: * `id`: Unique identifier (e.g., `"timeline"`, `"kanban"`) * `name`: Display name shown in the view selector * `icon`: Optional Lucide icon name * `render`: Function that receives `ViewRendererProps` and returns Preact JSX * `css`: Optional CSS string (deduplicated by view ID) * `afterDOMLoaded`: Optional client-side script (same lifecycle as component scripts) * `options`: Optional configuration passed to every render invocation The `ViewRegistry` is a global singleton (via `Symbol.for`) ensuring all copies of the module share the same registry. Building and Distribution[](https://quartz.jzhao.xyz/advanced/making-plugins#building-and-distribution) -------------------------------------------------------------------------------------------------------- Quartz v5 plugins ship pre-built `dist/` in their repositories. When a user installs your plugin, Quartz detects the pre-built output and skips the install/build cycle entirely — making installation near-instant. ### Build Configuration[](https://quartz.jzhao.xyz/advanced/making-plugins#build-configuration) The plugin template’s `tsup.config.ts` bundles all dependencies by default. Only **singleton externals** — packages that must be the same instance across all plugins — are left unbundled: const SINGLETON_EXTERNALS = [\ "preact",\ "preact/hooks",\ "preact/jsx-runtime",\ "preact/compat",\ "@jackyzha0/quartz",\ "@jackyzha0/quartz/*",\ "vfile",\ "vfile/*",\ "unified",\ ] export default defineConfig({ // ... noExternal: [/.*/], // Bundle everything external: SINGLETON_EXTERNALS, // Except singletons }) This means your plugin’s `dist/index.js` is self-contained — no `npm install` needed at install time. ### Shipping Pre-built Output[](https://quartz.jzhao.xyz/advanced/making-plugins#shipping-pre-built-output) Your plugin’s `dist/` directory should be committed to the repository: 1. **Do NOT add `dist/` to `.gitignore`** 2. Run `npm run build` before committing 3. The CI workflow verifies `dist/` is up to date on every push If `dist/` is missing or gitignored, Quartz falls back to the full install/build cycle (useful during local development with symlinked plugins). ### Plugins with Native Dependencies[](https://quartz.jzhao.xyz/advanced/making-plugins#plugins-with-native-dependencies) Plugins that require native packages (e.g. `sharp` for image processing) cannot bundle those. For these plugins: 1. Set `"requiresInstall": true` in your `package.json` quartz manifest 2. Declare the native package as a `peerDependency` 3. Quartz will install it into the host project at build time # Build the plugin npm run build # or npx tsup What to Import from Where[](https://quartz.jzhao.xyz/advanced/making-plugins#what-to-import-from-where) -------------------------------------------------------------------------------------------------------- | You need… | Import from | | --- | --- | | Type definitions (`QuartzTransformerPlugin`, `QuartzComponent`, etc.) | `@quartz-community/types` | | Path utilities (`simplifySlug`, `resolveRelative`, `pathToRoot`) | `@quartz-community/utils/path` | | DOM utilities (`removeAllChildren`, `registerEscapeHandler`) | `@quartz-community/utils/dom` | | JSX conversion (`htmlToJsx`) | `@quartz-community/utils/jsx` | | Language utilities (`classNames`, `capitalize`) | `@quartz-community/utils/lang` | | Date/sort utilities (`formatDate`, `getDate`, `byDateAndAlphabetical`) | `@quartz-community/utils/date` and `@quartz-community/utils/sort` | | HTML escaping (`escapeHTML`, `unescapeHTML`) | `@quartz-community/utils/escape` | | Emoji utilities (`getIconCode`) | `@quartz-community/utils/emoji` | | Browser runtime (`onNav`, `onRender`, `fetchContentIndex`) | `@quartz-community/runtime` | Do **not** import from `@jackyzha0/quartz` or from `vfile` directly. Use the community packages instead. Internationalization (i18n)[](https://quartz.jzhao.xyz/advanced/making-plugins#internationalization-i18n) ---------------------------------------------------------------------------------------------------------- Plugins should provide their own translations for user-facing strings. Do **not** hardcode strings in components. ### Setting Up i18n[](https://quartz.jzhao.xyz/advanced/making-plugins#setting-up-i18n) Create the following structure: src/i18n/ ├── index.ts └── locales/ └── en-US.ts **`src/i18n/locales/en-US.ts`** (required base locale): export default { components: { myPlugin: { title: "My Plugin", description: "A description", itemCount: ({ count }: { count: number }) => (count === 1 ? "1 item" : `${count} items`), }, }, } **`src/i18n/index.ts`**: import enUS from "./locales/en-US" const locales: Record = { "en-US": enUS, } export function i18n(locale: string) { return locales[locale] || enUS } ### Using i18n in Components[](https://quartz.jzhao.xyz/advanced/making-plugins#using-i18n-in-components) import { i18n } from "../i18n" const MyComponent: QuartzComponent = ({ cfg }) => { const locale = cfg.locale ?? "en-US" const t = i18n(locale).components.myPlugin return

{t.title}

} ### Adding Translations[](https://quartz.jzhao.xyz/advanced/making-plugins#adding-translations) To add a new locale, copy `en-US.ts`, translate the strings, and register it: // src/i18n/locales/fr-FR.ts export default { components: { myPlugin: { title: "Mon Plugin", description: "Une description", itemCount: ({ count }: { count: number }) => count === 1 ? "1 élément" : `${count} éléments`, }, }, } // src/i18n/index.ts import enUS from "./locales/en-US" import frFR from "./locales/fr-FR" const locales: Record = { "en-US": enUS, "fr-FR": frFR, } Use [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) locale codes (e.g., `en-US`, `de-DE`, `ja-JP`, `zh-CN`). For dynamic content, use function-based translations as shown with `itemCount` above. Installing Your Plugin[](https://quartz.jzhao.xyz/advanced/making-plugins#installing-your-plugin) -------------------------------------------------------------------------------------------------- # In your Quartz project npx quartz plugin add github:your-username/my-plugin This clones the plugin and adds it to both `quartz.config.yaml` and `quartz.lock.json`. If the plugin ships pre-built `dist/` (recommended), installation completes in seconds with no build step. You can then configure it in your config: quartz.config.yaml plugins: - source: github:your-username/my-plugin enabled: true For options that require JavaScript callback functions (not expressible in YAML), use the TS override in `quartz.ts`: quartz.ts (override) import * as ExternalPlugin from "./.quartz/plugins" // Must be placed before loadQuartzConfig() ExternalPlugin.MyPlugin({ customFn: (data) => { // ... }, }) Options set via `quartz.ts` are merged with YAML options at instantiation time, with `quartz.ts` overrides taking precedence. These calls must be placed **before** `loadQuartzConfig()` in your `quartz.ts`. ### Development Workflow[](https://quartz.jzhao.xyz/advanced/making-plugins#development-workflow) During plugin development, you’ll frequently install and uninstall your plugin to test changes. The following commands help manage this cycle: # Remove your plugin and clean up npx quartz plugin remove my-plugin # Re-add after making changes npx quartz plugin add github:your-username/my-plugin If you’ve updated your `quartz.config.yaml` to reference a plugin that isn’t installed yet, you can install it without manually running `add`: # Install all config-referenced plugins missing from the lockfile npx quartz plugin install --from-config # Preview first without making changes npx quartz plugin install --from-config --dry-run To clean up plugins that are installed but no longer referenced in your config: # Remove orphaned plugins npx quartz plugin prune # Preview first without making changes npx quartz plugin prune --dry-run > Tip > > Both `resolve` and `prune` fall back to `quartz.config.default.yaml` if no `quartz.config.yaml` is present. This is useful for CI environments where the default config is the source of truth. See [prune](https://quartz.jzhao.xyz/cli/plugin#prune) > and [resolve](https://quartz.jzhao.xyz/cli/plugin#resolve) > for full details. Component Plugins[](https://quartz.jzhao.xyz/advanced/making-plugins#component-plugins) ---------------------------------------------------------------------------------------- For plugins that provide visual components (like Explorer, Graph, Search), see the [creating component plugins](https://quartz.jzhao.xyz/advanced/creating-components) guide. Component-only plugins (those with `"category": ["component"]` in their manifest) are loaded via side-effect import rather than a factory function. If your component-only plugin needs to receive user options from `quartz.config.yaml`, export an `init(options)` function — see [receiving YAML options](https://quartz.jzhao.xyz/advanced/creating-components#receiving-yaml-options-in-component-only-plugins) for details. * * * --- # quartz plugin quartz plugin ============= Apr 27, 20267 min read The `plugin` command is the heart of the Quartz v5 plugin management system. it allows you to install, configure, and update plugins directly from the command line. All plugins are stored in the `.quartz/plugins/` directory, and their versions are tracked in `quartz.lock.json`. Subcommands[](https://quartz.jzhao.xyz/cli/plugin#subcommands) --------------------------------------------------------------- ### list[](https://quartz.jzhao.xyz/cli/plugin#list) List all currently installed plugins and their versions. npx quartz plugin list ### add[](https://quartz.jzhao.xyz/cli/plugin#add) Add a new plugin from a Git repository. npx quartz plugin add github:username/repo To install from a specific branch or ref, append `#ref` to the source: npx quartz plugin add github:username/repo#my-branch npx quartz plugin add git+https://github.com/username/repo.git#my-branch npx quartz plugin add https://github.com/username/repo.git#my-branch You can also add a plugin from a local directory. This is useful for local development or airgapped environments: npx quartz plugin add ./path/to/my-plugin npx quartz plugin add ../sibling-plugin npx quartz plugin add /absolute/path/to/plugin Local plugins are symlinked into `.quartz/plugins/`, so any changes you make to the source directory are reflected immediately without re-installing. When a branch is specified, it is stored in the lockfile. All subsequent commands (`install`, `prune`) will respect that branch automatically. Use `install --latest` to fetch the latest commit from that branch. > Tip > > `plugin add` also accepts `--concurrency` / `-c` to limit how many remote repositories are cloned and built at the same time. This is the same flag documented under [install](https://quartz.jzhao.xyz/cli/plugin#install) > and is useful when adding several plugins at once on low-end hardware. ### remove[](https://quartz.jzhao.xyz/cli/plugin#remove) Remove an installed plugin. npx quartz plugin remove plugin-name ### install[](https://quartz.jzhao.xyz/cli/plugin#install) Install plugins for your Quartz project. By default, this installs all plugins listed in your `quartz.lock.json` file. npx quartz plugin install #### Flags[](https://quartz.jzhao.xyz/cli/plugin#flags) * `--from-config`: Synchronize plugins with `quartz.config.yaml` instead of the lockfile. This will install missing plugins and prune orphaned ones. * `--latest`: Fetch the latest version of plugins from their remote sources instead of using the version in the lockfile. * `--clean`: Skip existing directories and perform a fresh installation. * `--dry-run`: Preview the changes without actually installing or removing any files. * `--concurrency`, `-c`: Maximum number of plugins to clone, fetch, and build in parallel. Defaults to the number of CPU cores. Lower this (e.g. `-c 1` or `-c 2`) on memory- or CPU-constrained machines where the default parallelism causes failures, OOMs, or hangs. See [Installing on low-end hardware](https://quartz.jzhao.xyz/cli/plugin#installing-on-low-end-hardware) below. #### Positional Arguments[](https://quartz.jzhao.xyz/cli/plugin#positional-arguments) * `[names..]`: Optional list of specific plugin names to install or update. # Update specific plugins to latest npx quartz plugin install --latest plugin-a plugin-b # Preview what would be installed from config npx quartz plugin install --from-config --dry-run ### enable / disable[](https://quartz.jzhao.xyz/cli/plugin#enable--disable) Toggle a plugin’s status in your `quartz.config.yaml` without removing its files. npx quartz plugin enable plugin-name npx quartz plugin disable plugin-name ### config[](https://quartz.jzhao.xyz/cli/plugin#config) View or modify the configuration for a specific plugin. # View config npx quartz plugin config plugin-name # Set a value npx quartz plugin config plugin-name --set key=value ### prune[](https://quartz.jzhao.xyz/cli/plugin#prune) Remove installed plugins that are no longer referenced in your `quartz.config.yaml`. This is useful for cleaning up after removing plugin entries from your configuration. > Note > > Running `plugin install --from-config` also removes orphaned plugins as part of its synchronization. Use `prune` when you only want to clean up without installing anything new. npx quartz plugin prune Use `--dry-run` to preview which plugins would be removed without making changes: npx quartz plugin prune --dry-run Common Workflows[](https://quartz.jzhao.xyz/cli/plugin#common-workflows) ------------------------------------------------------------------------- ### Adding and Enabling a Plugin[](https://quartz.jzhao.xyz/cli/plugin#adding-and-enabling-a-plugin) To add a new plugin and start using it: 1. Add the plugin: `npx quartz plugin add github:quartz-community/example` 2. Enable it: `npx quartz plugin enable example` ### Updating Everything[](https://quartz.jzhao.xyz/cli/plugin#updating-everything) To keep your plugins fresh: npx quartz plugin install --latest ### Installing on low-end hardware[](https://quartz.jzhao.xyz/cli/plugin#installing-on-low-end-hardware) By default, `plugin install` and `plugin add` clone, fetch, and build plugins in parallel across all your CPU cores. On memory-constrained machines (low-end laptops, Raspberry Pi, small VPS instances, restrictive CI runners) this can exhaust RAM or overwhelm the system because each worker may kick off its own `npm install` / `npm run build` at the same time. > Note > > Most community plugins now ship with a pre-built `dist/` directory. When Quartz finds this, it skips the installation and build steps entirely, making the process much faster and lighter on resources. This section is primarily relevant for plugins in development or those that don’t provide pre-built distribution. If `plugin install` fails, hangs, or OOMs on your machine, lower the concurrency with `--concurrency` / `-c`: # Install one plugin at a time (safest, slowest) npx quartz plugin install --latest -c 1 # Two at a time — usually a good balance on 4 GB machines npx quartz plugin install --latest --concurrency 2 The same flag works on `plugin add` and the other plugin subcommands that perform parallel work: npx quartz plugin add github:quartz-community/some-plugin -c 1 ### Managing Configuration[](https://quartz.jzhao.xyz/cli/plugin#managing-configuration) If you want to change a plugin setting without opening the YAML file: npx quartz plugin config explorer --set useSavedState=true ### Cleaning Up Unused Plugins[](https://quartz.jzhao.xyz/cli/plugin#cleaning-up-unused-plugins) If you’ve removed plugins from your config and want to clean up leftover files: npx quartz plugin prune --dry-run # preview first npx quartz plugin prune # remove orphaned plugins ### Setting Up from Config[](https://quartz.jzhao.xyz/cli/plugin#setting-up-from-config) When setting up on a new machine or in CI, `install --from-config` ensures your installed plugins match your config — installing missing plugins and removing any that are no longer referenced: npx quartz plugin install --from-config ### Testing with Branches[](https://quartz.jzhao.xyz/cli/plugin#testing-with-branches) If a plugin author has a fix or feature on a separate branch, you can install it directly without waiting for a release to the default branch: # Install from a feature branch npx quartz plugin add github:username/repo#fix/some-bug # Later, switch back to the default branch by re-adding without a ref npx quartz plugin remove repo npx quartz plugin add github:username/repo The branch ref is tracked in `quartz.lock.json`, so `install --latest` will continue to follow the specified branch until the plugin is re-added without one. Both `prune` and `install --from-config` will fall back to `quartz.config.default.yaml` if no `quartz.config.yaml` is present. ### Local Plugin Development[](https://quartz.jzhao.xyz/cli/plugin#local-plugin-development) For local plugin development or airgapped environments, you can add a plugin from a local directory: npx quartz plugin add ./my-local-plugin Local plugins are symlinked into `.quartz/plugins/`, so changes reflect immediately. When you run `install --latest`, local plugins are rebuilt (npm install + npm run build) without any git operations. > Note > > Local symlinked plugins typically use this build-on-install fallback because the `dist/` directory is usually gitignored during development. The `install --latest --dry-run` command will show local plugins with a “local” status instead of checking for remote updates. To switch a local plugin back to a git source: npx quartz plugin remove my-local-plugin npx quartz plugin add github:username/my-local-plugin ### Subdirectory (Monorepo) Plugins[](https://quartz.jzhao.xyz/cli/plugin#subdirectory-monorepo-plugins) Some plugins live in a subdirectory of a larger repository rather than at the root. For these, you can specify the plugin source as an object in `quartz.config.yaml` with a `subdir` field: quartz.config.yaml plugins: - source: repo: "https://github.com/username/monorepo.git" subdir: plugin enabled: true This tells Quartz to clone the full repository but install only the contents of the specified subdirectory. You can combine `subdir` with `ref` to pin a branch or tag, and `name` to override the plugin directory name: quartz.config.yaml plugins: - source: repo: "https://github.com/username/monorepo.git" subdir: packages/my-plugin ref: v2.0 name: my-plugin enabled: true See [Advanced Source Options](https://quartz.jzhao.xyz/configuration#advanced-source-options) for the full reference on object source fields. > Note > > The `plugin add` CLI command works with string sources. To use the object source format with `subdir`, edit `quartz.config.yaml` directly, then run `npx quartz plugin install --from-config` to install it. Migration from Deprecated Commands[](https://quartz.jzhao.xyz/cli/plugin#migration-from-deprecated-commands) ------------------------------------------------------------------------------------------------------------- | Old command | New equivalent | | --- | --- | | `npx quartz plugin restore` | `npx quartz plugin install --clean` | | `npx quartz plugin update` | `npx quartz plugin install --latest` | | `npx quartz plugin update my-plugin` | `npx quartz plugin install --latest my-plugin` | | `npx quartz plugin check` | `npx quartz plugin install --latest --dry-run` | | `npx quartz plugin resolve` | `npx quartz plugin install --from-config` | | `npx quartz plugin resolve --dry-run` | `npx quartz plugin install --from-config --dry-run` | | `npx quartz update` | `npx quartz plugin install --latest` | The old commands still work as hidden aliases but will print a deprecation warning. Plugin Status[](https://quartz.jzhao.xyz/cli/plugin#plugin-status) ------------------------------------------------------------------- Running the plugin command without any subcommand shows a status dashboard of all installed plugins, including whether updates are available: npx quartz plugin This displays each plugin with its source, commit, enabled/disabled status, and checks for available updates in parallel. For the full interactive management interface, use [npx quartz tui](https://quartz.jzhao.xyz/cli/tui) instead. * * * --- # quartz sync quartz sync =========== Apr 17, 20262 min read The `sync` command automates the process of pushing your local changes to GitHub and pulling updates from your remote repository. It simplifies the Git workflow for users who want to keep their site updated without running manual Git commands. Flags[](https://quartz.jzhao.xyz/cli/sync#flags) ------------------------------------------------- | Flag | Shorthand | Description | Default | | --- | --- | --- | --- | | `--directory` | `-d` | The directory of your Quartz project | Current directory | | `--verbose` | `-v` | Enable detailed logging | `false` | | `--commit` | | Whether to commit changes | `true` | | `--no-commit` | | Skip committing changes | `false` | | `--message` | `-m` | Custom commit message | `update content` | | `--push` | | Whether to push changes to remote | `true` | | `--no-push` | | Skip pushing changes | `false` | | `--pull` | | Whether to pull changes from remote | `true` | | `--no-pull` | | Skip pulling changes | `false` | Workflow[](https://quartz.jzhao.xyz/cli/sync#workflow) ------------------------------------------------------- When you run `npx quartz sync`, Quartz performs the following steps: 1. **Pull**: It fetches and merges changes from your remote GitHub repository. 2. **Add**: It stages all new and modified files in your project. 3. **Commit**: It creates a new commit with your changes. 4. **Push**: It sends your new commit to GitHub. Common Workflows[](https://quartz.jzhao.xyz/cli/sync#common-workflows) ----------------------------------------------------------------------- ### Regular Sync[](https://quartz.jzhao.xyz/cli/sync#regular-sync) The most common usage is to simply run the command with no flags. This pulls, commits, and pushes everything. npx quartz sync ### First Sync[](https://quartz.jzhao.xyz/cli/sync#first-sync) If you have just set up a new repository and haven’t pushed anything yet, you might want to skip the pull step. npx quartz sync --no-pull ### Custom Commit Message[](https://quartz.jzhao.xyz/cli/sync#custom-commit-message) You can provide a more descriptive message for your changes. npx quartz sync --message "add new notes about gardening" ### Sync from Another Device[](https://quartz.jzhao.xyz/cli/sync#sync-from-another-device) If you are working on a different computer and just want to get the latest changes without pushing anything back yet. npx quartz sync --no-push --no-commit Troubleshooting[](https://quartz.jzhao.xyz/cli/sync#troubleshooting) --------------------------------------------------------------------- ### Git Buffer[](https://quartz.jzhao.xyz/cli/sync#git-buffer) If you have a very large number of changes, Git might occasionally fail due to buffer limits. If this happens, try syncing smaller batches of files or increasing your Git post buffer size. ### Autostash[](https://quartz.jzhao.xyz/cli/sync#autostash) Quartz uses `git pull --rebase --autostash` internally. This means if you have unstaged changes when you run `sync`, Quartz will temporarily hide them, pull the remote changes, and then bring your changes back. If a conflict occurs during this process, you will need to resolve it manually using standard Git tools. For more information on initial setup, see [installation](https://quartz.jzhao.xyz/getting-started/installation) . * * * --- # quartz tui quartz tui ========== Mar 14, 20262 min read The `tui` command launches an interactive terminal user interface for managing your Quartz project. It provides a visual way to manage plugins, arrange your site layout, and edit general settings. Prerequisites[](https://quartz.jzhao.xyz/cli/tui#prerequisites) ---------------------------------------------------------------- To use the TUI, you must have the following: 1. **Bun**: The TUI requires the Bun runtime. You can find installation instructions at [bun.sh](https://bun.sh/docs/installation) . 2. **TUI Plugin**: You must install the TUI plugin in your Quartz project. ### Installation[](https://quartz.jzhao.xyz/cli/tui#installation) Run the following command to add the TUI plugin: npx quartz plugin add github:quartz-community/tui Interface Panels[](https://quartz.jzhao.xyz/cli/tui#interface-panels) ---------------------------------------------------------------------- The TUI is divided into three main panels that you can navigate between. ### Plugins Panel[](https://quartz.jzhao.xyz/cli/tui#plugins-panel) This panel allows you to browse all available and installed plugins. You can: * Enable or disable plugins with a single keystroke. * Configure plugin-specific settings. * Install new plugins from the community or remove existing ones. ### Layout Panel[](https://quartz.jzhao.xyz/cli/tui#layout-panel) The Layout panel is where you define where components appear on your site. You can: * Move components between different sections (e.g. `left`, `right`, `beforeBody`). * Reorder components within a section to change their vertical stack. * Set priorities for components to control their placement. ### Settings Panel[](https://quartz.jzhao.xyz/cli/tui#settings-panel) This panel provides a central place to edit your `quartz.config.yaml` settings. You can update: * `pageTitle` * Theme colors and fonts * Analytics configuration * Deployment settings Navigation[](https://quartz.jzhao.xyz/cli/tui#navigation) ---------------------------------------------------------- The TUI uses standard terminal navigation keys: * **Arrow Keys**: Move between items and panels. * **Enter**: Select an item or confirm a change. * **Esc**: Go back or cancel an action. * **Tab**: Cycle through different interface elements. Important Note[](https://quartz.jzhao.xyz/cli/tui#important-note) ------------------------------------------------------------------ All changes made within the TUI are written directly to your `quartz.config.yaml` file. It is a good practice to have a clean Git state before using the TUI so you can easily review and undo any changes it makes. For command-line based plugin management, see [quartz plugin](https://quartz.jzhao.xyz/cli/plugin) . * * * --- # quartz upgrade quartz upgrade ============== Apr 17, 20262 min read The `upgrade` command upgrades the Quartz framework itself to the latest version by pulling changes from the official Quartz repository. Usage[](https://quartz.jzhao.xyz/cli/upgrade#usage) ---------------------------------------------------- npx quartz upgrade How it Works[](https://quartz.jzhao.xyz/cli/upgrade#how-it-works) ------------------------------------------------------------------ When you run `npx quartz upgrade`, Quartz performs the following steps: 1. **Backs up your content** — your content folder is cached locally to prevent data loss. 2. **Pulls the latest Quartz code** — fetches and merges from the official upstream repository (`upstream/v5`) using Git. 3. **Shows version changes** — displays the version transition (e.g., `v5.0.0 → v5.1.0`) or confirms you’re already up to date. 4. **Updates dependencies** — runs `npm install` to ensure all packages match the new version. 5. **Restores plugins** — reinstalls plugins from `quartz.lock.json` to ensure compatibility. 6. **Checks plugin compatibility** — verifies that installed plugins are compatible with the new Quartz version. Handling Conflicts[](https://quartz.jzhao.xyz/cli/upgrade#handling-conflicts) ------------------------------------------------------------------------------ Because Quartz allows you to customize almost every part of the code, upgrades can sometimes result in merge conflicts. This happens if you have modified a file that the Quartz team has also updated. Quartz automatically handles merge conflicts in `quartz.lock.json` by backing up your lockfile before pulling and restoring it afterward. This prevents the most common source of conflicts during upgrades. For other files, if a conflict occurs: 1. Git will mark the conflicting sections in the affected files. 2. You will need to open these files and manually choose which changes to keep. 3. After resolving the conflicts, you can commit the changes. Recovery[](https://quartz.jzhao.xyz/cli/upgrade#recovery) ---------------------------------------------------------- If an upgrade goes wrong or leaves your project in an unusable state, you can use the [restore](https://quartz.jzhao.xyz/cli/restore) command to recover your content from the local cache. Flags[](https://quartz.jzhao.xyz/cli/upgrade#flags) ---------------------------------------------------- The `upgrade` command supports the standard [common flags](https://quartz.jzhao.xyz/cli/) (`--directory`, `--verbose`). See Also[](https://quartz.jzhao.xyz/cli/upgrade#see-also) ---------------------------------------------------------- * [quartz plugin install —latest](https://quartz.jzhao.xyz/cli/plugin) — update installed plugins * [Upgrading Quartz](https://quartz.jzhao.xyz/upgrading) — detailed upgrading guide * [quartz restore](https://quartz.jzhao.xyz/cli/restore) — recover content from cache * * * --- # Community Community ========= Mar 31, 20261 min read Quartz has a vibrant community of users and contributors. This page highlights community-created plugins, tools, and resources that extend Quartz. > Contributing > > Know of a great community resource? Submit a pull request to add it to this page! Community Plugins[](https://quartz.jzhao.xyz/community#community-plugins) -------------------------------------------------------------------------- Third-party plugins that extend Quartz functionality. Install them with the [plugin CLI](https://quartz.jzhao.xyz/cli/plugin) : npx quartz plugin add _No community plugins listed yet. Be the first to share yours!_ Tools & Integrations[](https://quartz.jzhao.xyz/community#tools--integrations) ------------------------------------------------------------------------------- Tools, scripts, and integrations built by the community to work with Quartz. _No community tools listed yet._ Templates & Themes[](https://quartz.jzhao.xyz/community#templates--themes) --------------------------------------------------------------------------- Custom themes, CSS snippets, and starter templates for Quartz sites. _No community templates listed yet._ Guides & Tutorials[](https://quartz.jzhao.xyz/community#guides--tutorials) --------------------------------------------------------------------------- Community-written guides, blog posts, and tutorials about using Quartz. _No community guides listed yet._ Related Projects[](https://quartz.jzhao.xyz/community#related-projects) ------------------------------------------------------------------------ Projects and tools in the digital garden / PKM ecosystem that pair well with Quartz. * **[Obsidian](https://obsidian.md/) ** — Knowledge base and note-taking app (recommended editor for Quartz content) * * * Looking to see sites built with Quartz? Check out the [Quartz Showcase](https://quartz.jzhao.xyz/showcase) . Want to chat with other Quartz users? [Join the Discord community](https://discord.gg/cRFFHYye7t) . * * * --- # Citations Citations ========= Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | Feb 25, 20251 min read Quartz uses [rehype-citation](https://github.com/timlrx/rehype-citation) to support parsing of a BibTex bibliography file. Under the default configuration, a citation key `[@templeton2024scaling]` will be exported as `(Templeton et al., 2024)`. > BibTex file > > bibliography.bib > > @article{templeton2024scaling, > title={Scaling Monosemanticity: Extracting Interpretable Features from Claude 3 Sonnet}, > author={Templeton, Adly and Conerly, Tom and Marcus, Jonathan and Lindsey, Jack and Bricken, Trenton and Chen, Brian and Pearce, Adam and Citro, Craig and Ameisen, Emmanuel and Jones, Andy and Cunningham, Hoagy and Turner, Nicholas L and McDougall, Callum and MacDiarmid, Monte and Freeman, C. Daniel and Sumers, Theodore R. and Rees, Edward and Batson, Joshua and Jermyn, Adam and Carter, Shan and Olah, Chris and Henighan, Tom}, > year={2024}, > journal={Transformer Circuits Thread}, > url={https://transformer-circuits.pub/2024/scaling-monosemanticity/index.html} > } > Behaviour of references > > By default, the references will be included at the end of the file. To control where the references to be included, uses `[^ref]` > > Refer to `rehype-citation` docs for more information. Customization[](https://quartz.jzhao.xyz/features/citations#customization) --------------------------------------------------------------------------- Citation parsing is a functionality of the [Citation](https://quartz.jzhao.xyz/plugins/citations) plugin. **This plugin is not enabled by default**. See the plugin page for customization options. * * * --- # Canvas Support Canvas Support ============== Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | Mar 14, 20261 min read Quartz supports rendering [JSON Canvas](https://jsoncanvas.org/) (`.canvas`) files as interactive, pannable and zoomable canvas pages. This brings your Obsidian canvas files to the web, preserving text nodes, file references, link nodes, group nodes, and edges with full visual fidelity. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Canvas support is provided by the [CanvasPage](https://quartz.jzhao.xyz/plugins/canvaspage) plugin. See the plugin page for configuration options and a full list of supported features. Demo[](https://quartz.jzhao.xyz/features/canvas#demo) ------------------------------------------------------ > Canvas > ====== > > Node Types > > Configuration > > Preset Colors > > Edges & Connections > > CanvasPage Plugin > ================= > > This plugin renders [JSON Canvas](https://jsoncanvas.org/) > (`.canvas`) files as interactive, pannable and zoomable canvas pages. It supports the full [JSON Canvas 1.0 spec](https://jsoncanvas.org/spec/1.0/) > . > > Install: `npx quartz plugin add github:quartz-community/canvas-page` > > Text Nodes > ---------- > > Text nodes render **Markdown** content with GFM support: > > * **Bold** and _italic_ text > * ~Strikethrough~ text > * [External links](https://jsoncanvas.org/) > > * `Inline code` blocks > * Lists (like this one) > > ### Headings Work Too > > All standard Markdown syntax is rendered at build time. > > File Nodes > ---------- > > File nodes reference other pages in your vault. They appear as clickable links and support **popover previews** on hover. > > The node below links to the CanvasPage documentation: > > [CanvasPage](https://quartz.jzhao.xyz/plugins/canvaspage) > > This plugin is a page type plugin that renders [JSON Canvas](https://jsoncanvas.org/) > (`.canvas`) files as interactive, pannable and zoomable canvas pages. It uses a custom `"canvas"` [page frame](https://quartz.jzhao.xyz/layout#page-frames) > that provides a fullscreen, always-on canvas experience with a togglable left sidebar for navigation. It supports the full [JSON Canvas 1.0 spec](https://jsoncanvas.org/spec/1.0/) > , including text nodes with Markdown rendering, file nodes that link to other pages in your vault, link nodes for external URLs, and group nodes for visual organization. Edges between nodes are rendered as SVG paths with optional labels, arrow markers, and colors. > > > Note > > > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > > page. > > This plugin accepts the following configuration options: > > * `enableInteraction`: Whether to enable pan and zoom interaction on the canvas. Default: `true`. > * `initialZoom`: The initial zoom level when the canvas is first displayed. Default: `1`. > * `minZoom`: The minimum zoom level allowed when zooming out. Default: `0.1`. > * `maxZoom`: The maximum zoom level allowed when zooming in. Default: `5`. > > ### Canvas Frame[](https://quartz.jzhao.xyz/features/canvas#canvas-frame) > > The canvas-page plugin provides its own `"canvas"` page frame via the [Frame Registry](https://quartz.jzhao.xyz/layout#page-frames) > . This frame: > > * Renders the canvas in **fullscreen mode** by default (100vw × 100vh), giving the canvas maximum screen space — leaning into the “endless canvas” concept of JSON Canvas. > * Provides a **togglable left sidebar** that slides in from the left edge. This is the only layout slot available — it renders the same components as the `left` sidebar on content pages (e.g., Explorer, Search, Page Title). > * The sidebar toggle button (hamburger/close icon) is positioned in the top-left corner. > * Canvas controls (zoom in, zoom out, reset) are positioned on the right side. > * On mobile, the sidebar overlays the canvas rather than pushing it aside. > > Users can override this frame via `quartz.config.yaml` if needed: > > quartz.config.yaml > > layout: > byPageType: > canvas: > template: default # Use standard three-column layout instead > > ### Features[](https://quartz.jzhao.xyz/features/canvas#features) > > * **Text nodes**: Render Markdown content including headings, bold, italic, strikethrough, lists, links, and code blocks via [GFM](https://github.github.com/gfm/) > support. > * **File nodes**: Link to other pages in your vault. Supports popover previews on hover. > * **Link nodes**: Reference external URLs. > * **Group nodes**: Visual grouping containers with optional labels and background colors. > * **Edges**: SVG connections between nodes with optional labels, arrow markers, and colors. Supports all four sides (top, right, bottom, left) and both preset colors (1–6) and custom hex colors. > * **Togglable sidebar**: Hamburger button in the top-left corner toggles the left sidebar for navigation. Press `Escape` or click the close button to dismiss. > * **Preset colors**: Six preset colors (red, orange, yellow, green, cyan, purple) plus custom hex colors (`#RRGGBB`) for nodes and edges. > > API[](https://quartz.jzhao.xyz/features/canvas#api) > > ---------------------------------------------------- > > * Category: Page Type > * Function name: `ExternalPlugin.CanvasPage()`. > * Source: [`quartz-community/canvas-page`](https://github.com/quartz-community/canvas-page) > > * Install: `npx quartz plugin add github:quartz-community/canvas-page` > > Link Nodes > ---------- > > Link nodes reference external URLs. The node below links to the JSON Canvas specification: > > [jsoncanvas.org](https://jsoncanvas.org/spec/1.0/) > > [Open jsoncanvas.org in new tab](https://jsoncanvas.org/spec/1.0/) > > **Color 1** — Red > > **Color 2** — Orange > > **Color 3** — Yellow > > **Color 4** — Green > > **Color 5** — Cyan > > **Color 6** — Purple > > **Custom hex color** — `#ff6600` > > Configuration Options > --------------------- > > * `enableInteraction` — Enable pan and zoom. Default: `true` > * `initialZoom` — Initial zoom level. Default: `1` > * `minZoom` — Minimum zoom level. Default: `0.1` > * `maxZoom` — Maximum zoom level. Default: `5` > * `defaultFullscreen` — Start in fullscreen mode. Default: `false` > > Configure in `quartz.config.yaml`: > > CanvasPage({ defaultFullscreen: false, initialZoom: 1 }) > > > Fullscreen Mode > --------------- > > Click the **expand button** (top-right corner) to toggle fullscreen mode. The canvas fills the entire viewport. > > * Press **Escape** to exit fullscreen > * Set `defaultFullscreen: true` to start in fullscreen > * The toggle button switches between expand and collapse icons > > Quartz Integration > ------------------ > > * **Popover previews**: Hover over file nodes to see a preview > * **Internal links**: File nodes link to pages in your vault > * **Dark mode**: Canvas adapts to your theme settings > > Edges > ----- > > Edges connect nodes with SVG paths. They support **labels**, **arrows**, and **colors**. > > This edge has a **label** and an arrow marker. > > This edge has a **custom color** (`#ff6600`). > > Edges can use the same **preset colors** (1–6) as nodes, or custom **hex colors** like `#ff6600`. > > API > --- > > * **Category**: Page Type > * **Function name**: `ExternalPlugin.CanvasPage()` > * **Source**: [quartz-community/canvas-page](https://github.com/quartz-community/canvas-page) > > * **Install**: `npx quartz plugin add github:quartz-community/canvas-page` > > JSON Canvas Spec > ---------------- > > This plugin implements the [JSON Canvas 1.0](https://jsoncanvas.org/spec/1.0/) > specification — an open file format for infinite canvas data. > > Canvas files use the `.canvas` extension and are standard JSON. They are natively supported by [Obsidian](https://obsidian.md/) > . > > supportslabeled edge > > [Link to original](https://quartz.jzhao.xyz/canvas.canvas) Customization[](https://quartz.jzhao.xyz/features/canvas#customization) ------------------------------------------------------------------------ * Install: `npx quartz plugin add github:quartz-community/canvas-page` * Source: [`quartz-community/canvas-page`](https://github.com/quartz-community/canvas-page) * * * --- # Docker Support Docker Support ============== Properties1 | | | | --- | --- | | tags | [feature](https://quartz.jzhao.xyz/tags/feature) | Mar 14, 20261 min read Quartz comes shipped with a Docker image that will allow you to preview your Quartz locally without installing Node. You can run the below one-liner to run Quartz in Docker. docker run --rm -itp 8080:8080 -p 3001:3001 -v ./content:/usr/src/app/content $(docker build -q .) > Not to be used for production > > Serve mode is intended for local previews only. For production workloads, see the page on [hosting](https://quartz.jzhao.xyz/hosting) > . * * * --- # Roam Research Compatibility Roam Research Compatibility =========================== Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | May 24, 20261 min read [Roam Research](https://roamresearch.com/) is a note-taking tool that organizes your knowledge graph in a unique and interconnected way. Quartz supports transforming the special Markdown syntax from Roam Research (like `{{[[components]]}}` and other formatting) into regular Markdown via the [RoamFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/roamflavoredmarkdown) plugin. quartz.config.yaml plugins: - source: github:quartz-community/roam enabled: true order: 25 # must come before obsidian-flavored-markdown - source: github:quartz-community/obsidian-flavored-markdown enabled: true order: 30 For the TS override approach, place overrides before `loadQuartzConfig()` in `quartz.ts`: quartz.ts (override) import * as ExternalPlugin from "./.quartz/plugins" ExternalPlugin.RoamFlavoredMarkdown() > Warning > > In YAML, plugin execution order is controlled by the `order` field. Ensure `roam` has a lower `order` value than `obsidian-flavored-markdown` so it runs first. Customization[](https://quartz.jzhao.xyz/features/roam-research-compatibility#customization) --------------------------------------------------------------------------------------------- This functionality is provided by the [RoamFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/roamflavoredmarkdown) plugin. See the plugin page for customization options. * * * --- # Mermaid Diagrams Mermaid Diagrams ================ Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | Feb 23, 20241 min read Quartz supports Mermaid which allows you to add diagrams and charts to your notes. Mermaid supports a range of diagrams, such as [flow charts](https://mermaid.js.org/syntax/flowchart.html) , [sequence diagrams](https://mermaid.js.org/syntax/sequenceDiagram.html) , and [timelines](https://mermaid.js.org/syntax/timeline.html) . This is enabled as a part of [Obsidian compatibility](https://quartz.jzhao.xyz/features/obsidian-compatibility) and can be configured and enabled/disabled from that plugin. By default, Quartz will render Mermaid diagrams to match the site theme. > Warning > > Wondering why Mermaid diagrams may not be showing up even if you have them enabled? You may need to reorder your plugins so that [ObsidianFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/obsidianflavoredmarkdown) > is _after_ [SyntaxHighlighting](https://quartz.jzhao.xyz/plugins/syntaxhighlighting) > . Syntax[](https://quartz.jzhao.xyz/features/mermaid-diagrams#syntax) -------------------------------------------------------------------- To add a Mermaid diagram, create a mermaid code block. ```mermaid sequenceDiagram Alice->>+John: Hello John, how are you? Alice->>+John: John, can you hear me? John-->>-Alice: Hi Alice, I can hear you! John-->>-Alice: I feel great! ``` `sequenceDiagram Alice->>+John: Hello John, how are you? Alice->>+John: John, can you hear me? John-->>-Alice: Hi Alice, I can hear you! John-->>-Alice: I feel great!` * * * --- # OxHugo Compatibility OxHugo Compatibility ==================== Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | May 24, 20262 min read [org-roam](https://www.orgroam.com/) is a plain-text personal knowledge management system for [emacs](https://en.wikipedia.org/wiki/Emacs) . [ox-hugo](https://github.com/kaushalmodi/ox-hugo) is org exporter backend that exports `org-mode` files to [Hugo](https://gohugo.io/) compatible Markdown. Because the Markdown generated by ox-hugo is not pure Markdown but Hugo specific, we need to transform it to fit into Quartz. This is done by the [OxHugoFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/oxhugoflavoredmarkdown) plugin. Even though this plugin was written with `ox-hugo` in mind, it should work for any Hugo specific Markdown. quartz.config.yaml plugins: - source: github:quartz-community/obsidian-flavored-markdown enabled: true order: 30 - source: github:quartz-community/ox-hugo enabled: true order: 25 # must come before obsidian-flavored-markdown - source: github:quartz-community/github-flavored-markdown enabled: true order: 40 - source: github:quartz-community/note-properties enabled: true options: delimiters: "+++" language: toml # if using toml frontmatter order: 5 For the TS override approach, place overrides before `loadQuartzConfig()` in `quartz.ts`: quartz.ts (override) import * as ExternalPlugin from "./.quartz/plugins" ExternalPlugin.NoteProperties({ delims: "+++", language: "toml" }) ExternalPlugin.OxHugoFlavouredMarkdown() > Note > > In YAML, plugin execution order is controlled by the `order` field. Lower numbers execute first. Ensure `ox-hugo` has a lower `order` than `obsidian-flavored-markdown`. Usage[](https://quartz.jzhao.xyz/features/oxhugo-compatibility#usage) ---------------------------------------------------------------------- Quartz by default doesn’t understand `org-roam` files as they aren’t Markdown. You’re responsible for using an external tool like `ox-hugo` to export the `org-roam` files as Markdown content to Quartz and managing the static assets so that they’re available in the final output. Configuration[](https://quartz.jzhao.xyz/features/oxhugo-compatibility#configuration) -------------------------------------------------------------------------------------- This functionality is provided by the [OxHugoFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/oxhugoflavoredmarkdown) plugin. See the plugin page for customization options. * * * --- # LaTeX LaTeX ===== Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | Mar 14, 20262 min read Quartz uses [Katex](https://katex.org/) by default to typeset both inline and block math expressions at build time. Syntax[](https://quartz.jzhao.xyz/features/latex#syntax) --------------------------------------------------------- ### Block Math[](https://quartz.jzhao.xyz/features/latex#block-math) Block math can be rendered by delimiting math expression with `$$`. $$ f(x) = \int_{-\infty}^\infty f\hat(\xi),e^{2 \pi i \xi x} \,d\xi $$ f(x)\=∫−∞∞​f(^​ξ),e2πiξxdξ a​\=b+c\=e+f​ \[1a​2b​3c​\] EψEf(x)​\=Hψ\=−2mℏ2​∂x2∂2​ψ+21​mωx2ψ\=−2mℏ2​\[4k2x2f(x)+2(−2kx)f′(x)+f′′(x)\]e−kx2+21​mωx2f(x)e−kx2⇓\=−2mℏ2​\[4k2x2f(x)−4kxf′(x)+f′′(x)\]+21​mωx2f(x)\=−2mℏ2​\[−4kxf′(x)+f′′(x)\]​Expanding the Hamiltonian OperatorUsing the ansatz ψ(x)\=e−kx2f(x), hoping to cancel the x2 termRemoving the e−kx2 term from both sidesChoosing k\=2im​ℏω​​ to cancel the x2 term, via −2mℏ2​4k2\=21​mω​ > Warn > > Due to limitations in the [underlying parsing library](https://github.com/remarkjs/remark-math) > , block math in Quartz requires the `$$` delimiters to be on newlines like above. ### Inline Math[](https://quartz.jzhao.xyz/features/latex#inline-math) Similarly, inline math can be rendered by delimiting math expression with a single `$`. For example, `$e^{i\pi} = -1$` produces eiπ\=−1 ### Escaping symbols[](https://quartz.jzhao.xyz/features/latex#escaping-symbols) There will be cases where you may have more than one `$` in a paragraph at once which may accidentally trigger MathJax/Katex. To get around this, you can escape the dollar sign by doing `\$` instead. For example: * Incorrect: `I have $1 and you have $2` produces I have 1andyouhave2 * Correct: `I have \$1 and you have \$2` produces I have $1 and you have $2 ### Using mhchem[](https://quartz.jzhao.xyz/features/latex#using-mhchem) If you are using the community Latex plugin, you can add `mhchem` support by forking the plugin repository and adding the following import to the top of `src/index.ts` (before all the other imports): src/index.ts import "katex/contrib/mhchem" Customization[](https://quartz.jzhao.xyz/features/latex#customization) ----------------------------------------------------------------------- Latex parsing is a functionality of the [Latex](https://quartz.jzhao.xyz/plugins/latex) plugin. See the plugin page for customization options. * * * --- # Backlinks Backlinks ========= Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | Mar 14, 20261 min read A backlink for a note is a link from another note to that note. Links in the backlink pane also feature rich [popover previews](https://quartz.jzhao.xyz/features/popover-previews) if you have that feature enabled. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Customization[](https://quartz.jzhao.xyz/features/backlinks#customization) --------------------------------------------------------------------------- * Removing backlinks: remove the `backlinks` entry from `quartz.config.yaml` or set `enabled: false`. * Hide when empty: hide `Backlinks` if given page doesn’t contain any backlinks (default to `true`). To disable this, set `hideWhenEmpty: false` in the plugin options in `quartz.config.yaml`. * Install: `npx quartz plugin add github:quartz-community/backlinks` * Source: [`quartz-community/backlinks`](https://github.com/quartz-community/backlinks) * * * --- # RSS Feed RSS Feed ======== Mar 05, 20251 min read Quartz emits an RSS feed for all the content on your site by generating an `index.xml` file that RSS readers can subscribe to. Because of the RSS spec, this requires the `baseUrl` property in your [configuration](https://quartz.jzhao.xyz/configuration) to be set properly for RSS readers to pick it up properly. > Info > > After deploying, the generated RSS link will be available at `https://${baseUrl}/index.xml` by default. > > The `index.xml` path can be customized by passing the `rssSlug` option to the [ContentIndex](https://quartz.jzhao.xyz/plugins/contentindex) > plugin. Configuration[](https://quartz.jzhao.xyz/features/rss-feed#configuration) -------------------------------------------------------------------------- This functionality is provided by the [ContentIndex](https://quartz.jzhao.xyz/plugins/contentindex) plugin. See the plugin page for customization options. * * * --- # SPA Routing SPA Routing =========== Properties1 | | | | --- | --- | | tags | [feature](https://quartz.jzhao.xyz/tags/feature) | Mar 14, 20261 min read Single-page-app style rendering. This prevents flashes of unstyled content and improves the smoothness of Quartz. Under the hood, this is done by hijacking page navigations and instead fetching the HTML via a `GET` request and then diffing and selectively replacing parts of the page using [micromorph](https://github.com/natemoo-re/micromorph) . This allows us to change the content of the page without fully refreshing the page, reducing the amount of content that the browser needs to load. Configuration[](https://quartz.jzhao.xyz/features/spa-routing#configuration) ----------------------------------------------------------------------------- * Disable SPA Routing: set the `enableSPA` field of the [configuration](https://quartz.jzhao.xyz/configuration) in `quartz.config.yaml` to be `false`. * * * --- # Breadcrumbs Breadcrumbs =========== Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | May 24, 20262 min read Breadcrumbs provide a way to navigate a hierarchy of pages within your site using a list of its parent folders. By default, the element at the very top of your page is the breadcrumb navigation bar (can also be seen at the top on this page!). > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Customization[](https://quartz.jzhao.xyz/features/breadcrumbs#customization) ----------------------------------------------------------------------------- Most configuration can be done via the `options` section of the breadcrumbs plugin entry in `quartz.config.yaml`. For example, here’s what the default configuration looks like: quartz.config.yaml plugins: - source: github:quartz-community/breadcrumbs enabled: true options: spacerSymbol: "❯" rootName: Home resolveFrontmatterTitle: true showCurrentPage: true layout: position: beforeBody priority: 5 For the TS override approach: quartz.ts (override) // Must be placed before loadQuartzConfig() ExternalPlugin.Breadcrumbs({ spacerSymbol: "❯", rootName: "Home", resolveFrontmatterTitle: true, showCurrentPage: true, }) When passing in your own options, you can omit any or all of these fields if you’d like to keep the default value for that field. You can also adjust where the breadcrumbs will be displayed by changing the `layout.position` field in the plugin entry in `quartz.config.yaml` (see [layout](https://quartz.jzhao.xyz/layout) ). Want to customize it even more? * Removing breadcrumbs: remove the `breadcrumbs` entry from `quartz.config.yaml` or set `enabled: false`. * Install: `npx quartz plugin add github:quartz-community/breadcrumbs` * Source: [`quartz-community/breadcrumbs`](https://github.com/quartz-community/breadcrumbs) * * * --- # Darkmode Darkmode ======== Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | Mar 14, 20261 min read Quartz supports darkmode out of the box that respects the user’s theme preference. Any future manual toggles of the darkmode switch will be saved in the browser’s local storage so it can be persisted across future page loads. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Customization[](https://quartz.jzhao.xyz/features/darkmode#customization) -------------------------------------------------------------------------- * Removing darkmode: remove the `darkmode` entry from `quartz.config.yaml` or set `enabled: false`. * Install: `npx quartz plugin add github:quartz-community/darkmode` * Source: [`quartz-community/darkmode`](https://github.com/quartz-community/darkmode) You can also listen to the `themechange` event to perform any custom logic when the theme changes. document.addEventListener("themechange", (e) => { console.log("Theme changed to " + e.detail.theme) // either "light" or "dark" // your logic here }) * * * --- # Internationalization Internationalization ==================== Mar 14, 20261 min read Internationalization allows users to translate text in the Quartz interface into various supported languages without needing to make extensive code changes. This can be changed via the `locale` [configuration](https://quartz.jzhao.xyz/configuration) field in `quartz.config.yaml`. The locale field generally follows a certain format: `{language}-{REGION}` * `{language}` is usually a [2-letter lowercase language code](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) . * `{REGION}` is usually a [2-letter uppercase region code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) > Interested in contributing? > > We [gladly welcome translation PRs](https://github.com/jackyzha0/quartz/tree/v5/quartz/i18n/locales) > ! To contribute a translation, do the following things: > > 1. In the `quartz/i18n/locales` folder, copy the `en-US.ts` file. > 2. Rename it to `{language}-{REGION}.ts` so it matches a locale of the format shown above. > 3. Fill in the translations! > 4. Add the entry under `TRANSLATIONS` in `quartz/i18n/index.ts`. * * * --- # feature/filter feature/filter ============== 1 item with this tag. * Mar 14, 2026 ### [Private Pages](https://quartz.jzhao.xyz/features/private-pages) * [feature/filter](https://quartz.jzhao.xyz/tags/feature/filter) * * * --- # Philosophy of Quartz Philosophy of Quartz ==================== Mar 14, 20264 min read A garden should be a true hypertext[](https://quartz.jzhao.xyz/philosophy#a-garden-should-be-a-true-hypertext) --------------------------------------------------------------------------------------------------------------- > The garden is the web as topology. Every walk through the garden creates new paths, new meanings, and when we add things to the garden we add them in a way that allows many future, unpredicted relationships. > > _(The Garden and the Stream)_ The problem with the file cabinet is that it focuses on efficiency of access and interoperability rather than generativity and creativity. Thinking is not linear, nor is it hierarchical. In fact, not many things are linear or hierarchical at all. Then why is it that most tools and thinking strategies assume a nice chronological or hierarchical order for my thought processes? The ideal tool for thought for me would embrace the messiness of my mind, and organically help insights emerge from chaos instead of forcing an artificial order. A rhizomatic, not arboresecent, form of note taking. My goal with a digital garden is not purely as an organizing system and information store (though it works nicely for that). I want my digital garden to be a playground for new ways ideas can connect together. As a result, existing formal organizing systems like Zettelkasten or the hierarchical folder structures of Notion don’t work well for me. There is way too much upfront friction that by the time I’ve thought about how to organize my thought into folders categories, I’ve lost it. Quartz embraces the inherent rhizomatic and web-like nature of our thinking and tries to encourage note-taking in a similar form. * * * A garden should be shared[](https://quartz.jzhao.xyz/philosophy#a-garden-should-be-shared) ------------------------------------------------------------------------------------------- The goal of digital gardening should be to tap into your network’s collective intelligence to create constructive feedback loops. If done well, I have a shareable representation of my thoughts that I can send out into the world and people can respond. Even for my most half-baked thoughts, this helps me create a feedback cycle to strengthen and fully flesh out that idea. Quartz is designed first and foremost as a tool for publishing [digital gardens](https://jzhao.xyz/posts/networked-thought) to the web. To me, digital gardening is not just passive knowledge collection. It’s a form of expression and sharing. > “\[One\] who works with the door open gets all kinds of interruptions, but \[they\] also occasionally gets clues as to what the world is and what might be important.” — Richard Hamming **The goal of Quartz is to make sharing your digital garden free and simple.** * * * A garden should be your own[](https://quartz.jzhao.xyz/philosophy#a-garden-should-be-your-own) ----------------------------------------------------------------------------------------------- At its core, Quartz is designed to be easy to use enough for non-technical people to get going but also powerful enough that senior developers can tweak it to work how they’d like it to work. 1. If you like the default configuration of Quartz and just want to change the content, the only thing that you need to change is the contents of the `content` folder. 2. If you’d like to make basic configuration tweaks but don’t want to edit source code, one can tweak the plugins and components in `quartz.config.yaml` in a guided manner to their liking. 3. If you’d like to tweak the actual source code of the underlying plugins, components, or even build process, Quartz purposefully ships its full source code to the end user to allow customization at this level too. Most software either confines you to either 1. Makes it easy to tweak content but not the presentation 2. Gives you too many knobs to tune the presentation without good opinionated defaults **Quartz should feel powerful but ultimately be an intuitive tool fully within your control.** It should be a piece of [agentic software](https://jzhao.xyz/posts/agentic-computing) . Ultimately, it should have the right affordances to nudge users towards good defaults but never dictate what the ‘correct’ way of using it is. * * * --- # Canvas Node Types Configuration Preset Colors Edges & Connections CanvasPage Plugin ================= This plugin renders [JSON Canvas](https://jsoncanvas.org/) (`.canvas`) files as interactive, pannable and zoomable canvas pages. It supports the full [JSON Canvas 1.0 spec](https://jsoncanvas.org/spec/1.0/) . Install: `npx quartz plugin add github:quartz-community/canvas-page` Text Nodes ---------- Text nodes render **Markdown** content with GFM support: * **Bold** and _italic_ text * ~Strikethrough~ text * [External links](https://jsoncanvas.org/) * `Inline code` blocks * Lists (like this one) ### Headings Work Too All standard Markdown syntax is rendered at build time. File Nodes ---------- File nodes reference other pages in your vault. They appear as clickable links and support **popover previews** on hover. The node below links to the CanvasPage documentation: [CanvasPage](https://quartz.jzhao.xyz/plugins/canvaspage) This plugin is a page type plugin that renders [JSON Canvas](https://jsoncanvas.org/) (`.canvas`) files as interactive, pannable and zoomable canvas pages. It uses a custom `"canvas"` [page frame](https://quartz.jzhao.xyz/layout#page-frames) that provides a fullscreen, always-on canvas experience with a togglable left sidebar for navigation. It supports the full [JSON Canvas 1.0 spec](https://jsoncanvas.org/spec/1.0/) , including text nodes with Markdown rendering, file nodes that link to other pages in your vault, link nodes for external URLs, and group nodes for visual organization. Edges between nodes are rendered as SVG paths with optional labels, arrow markers, and colors. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `enableInteraction`: Whether to enable pan and zoom interaction on the canvas. Default: `true`. * `initialZoom`: The initial zoom level when the canvas is first displayed. Default: `1`. * `minZoom`: The minimum zoom level allowed when zooming out. Default: `0.1`. * `maxZoom`: The maximum zoom level allowed when zooming in. Default: `5`. ### Canvas Frame[](https://quartz.jzhao.xyz/canvas.canvas#canvas-frame) The canvas-page plugin provides its own `"canvas"` page frame via the [Frame Registry](https://quartz.jzhao.xyz/layout#page-frames) . This frame: * Renders the canvas in **fullscreen mode** by default (100vw × 100vh), giving the canvas maximum screen space — leaning into the “endless canvas” concept of JSON Canvas. * Provides a **togglable left sidebar** that slides in from the left edge. This is the only layout slot available — it renders the same components as the `left` sidebar on content pages (e.g., Explorer, Search, Page Title). * The sidebar toggle button (hamburger/close icon) is positioned in the top-left corner. * Canvas controls (zoom in, zoom out, reset) are positioned on the right side. * On mobile, the sidebar overlays the canvas rather than pushing it aside. Users can override this frame via `quartz.config.yaml` if needed: quartz.config.yaml layout: byPageType: canvas: template: default # Use standard three-column layout instead ### Features[](https://quartz.jzhao.xyz/canvas.canvas#features) * **Text nodes**: Render Markdown content including headings, bold, italic, strikethrough, lists, links, and code blocks via [GFM](https://github.github.com/gfm/) support. * **File nodes**: Link to other pages in your vault. Supports popover previews on hover. * **Link nodes**: Reference external URLs. * **Group nodes**: Visual grouping containers with optional labels and background colors. * **Edges**: SVG connections between nodes with optional labels, arrow markers, and colors. Supports all four sides (top, right, bottom, left) and both preset colors (1–6) and custom hex colors. * **Togglable sidebar**: Hamburger button in the top-left corner toggles the left sidebar for navigation. Press `Escape` or click the close button to dismiss. * **Preset colors**: Six preset colors (red, orange, yellow, green, cyan, purple) plus custom hex colors (`#RRGGBB`) for nodes and edges. API[](https://quartz.jzhao.xyz/canvas.canvas#api) -------------------------------------------------- * Category: Page Type * Function name: `ExternalPlugin.CanvasPage()`. * Source: [`quartz-community/canvas-page`](https://github.com/quartz-community/canvas-page) * Install: `npx quartz plugin add github:quartz-community/canvas-page` Link Nodes ---------- Link nodes reference external URLs. The node below links to the JSON Canvas specification: [jsoncanvas.org](https://jsoncanvas.org/spec/1.0/) [Open jsoncanvas.org in new tab](https://jsoncanvas.org/spec/1.0/) **Color 1** — Red **Color 2** — Orange **Color 3** — Yellow **Color 4** — Green **Color 5** — Cyan **Color 6** — Purple **Custom hex color** — `#ff6600` Configuration Options --------------------- * `enableInteraction` — Enable pan and zoom. Default: `true` * `initialZoom` — Initial zoom level. Default: `1` * `minZoom` — Minimum zoom level. Default: `0.1` * `maxZoom` — Maximum zoom level. Default: `5` * `defaultFullscreen` — Start in fullscreen mode. Default: `false` Configure in `quartz.config.yaml`: CanvasPage({ defaultFullscreen: false, initialZoom: 1 }) Fullscreen Mode --------------- Click the **expand button** (top-right corner) to toggle fullscreen mode. The canvas fills the entire viewport. * Press **Escape** to exit fullscreen * Set `defaultFullscreen: true` to start in fullscreen * The toggle button switches between expand and collapse icons Quartz Integration ------------------ * **Popover previews**: Hover over file nodes to see a preview * **Internal links**: File nodes link to pages in your vault * **Dark mode**: Canvas adapts to your theme settings Edges ----- Edges connect nodes with SVG paths. They support **labels**, **arrows**, and **colors**. This edge has a **label** and an arrow marker. This edge has a **custom color** (`#ff6600`). Edges can use the same **preset colors** (1–6) as nodes, or custom **hex colors** like `#ff6600`. API --- * **Category**: Page Type * **Function name**: `ExternalPlugin.CanvasPage()` * **Source**: [quartz-community/canvas-page](https://github.com/quartz-community/canvas-page) * **Install**: `npx quartz plugin add github:quartz-community/canvas-page` JSON Canvas Spec ---------------- This plugin implements the [JSON Canvas 1.0](https://jsoncanvas.org/spec/1.0/) specification — an open file format for infinite canvas data. Canvas files use the `.canvas` extension and are standard JSON. They are natively supported by [Obsidian](https://obsidian.md/) . supportslabeled edge --- # Full-text Search Full-text Search ================ Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | Mar 14, 20262 min read Full-text search in Quartz is powered by [Flexsearch](https://github.com/nextapps-de/flexsearch) . It’s fast enough to return search results in under 10ms for Quartzs as large as half a million words. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. It can be opened by either clicking on the search bar or pressing `⌘`/`ctrl` + `K`. The top 5 search results are shown on each query. Matching subterms are highlighted and the most relevant 30 words are excerpted. Clicking on a search result will navigate to that page. To search content by tags, you can either press `⌘`/`ctrl` + `shift` + `K` or start your query with `#` (e.g. `#components`). This component is also keyboard accessible: Tab and Shift+Tab will cycle forward and backward through search results and Enter will navigate to the highlighted result (first result by default). You are also able to navigate search results using `ArrowUp` and `ArrowDown`. > Info > > Search requires the `ContentIndex` emitter plugin to be present in the [configuration](https://quartz.jzhao.xyz/configuration) > . ### Indexing Behaviour[](https://quartz.jzhao.xyz/features/full-text-search#indexing-behaviour) By default, it indexes every page on the site with **Markdown syntax removed**. This means link URLs for instance are not indexed. It properly tokenizes Chinese, Korean, and Japenese characters and constructs separate indexes for the title, content and tags, weighing title matches above content matches. Customization[](https://quartz.jzhao.xyz/features/full-text-search#customization) ---------------------------------------------------------------------------------- * Removing search: remove the `search` entry from `quartz.config.yaml` or set `enabled: false`. * Install: `npx quartz plugin add github:quartz-community/search` * Source: [`quartz-community/search`](https://github.com/quartz-community/search) * * * --- # Quartz Showcase Quartz Showcase =============== May 28, 20251 min read Want to see what Quartz can do? Here are some cool community gardens: * [Quartz Documentation (this site!)](https://quartz.jzhao.xyz/) * [Jacky Zhao’s Garden](https://jzhao.xyz/) * [Aaron Pham’s Garden](https://aarnphm.xyz/) * [The Pond](https://turntrout.com/welcome) * [Eilleen’s Everything Notebook](https://quartz.eilleeenz.com/) * [Morrowind Modding Wiki](https://morrowind-modding.github.io/) * [Stanford CME 302 Numerical Linear Algebra](https://ericdarve.github.io/NLA/) * [Socratica Toolbox](https://toolbox.socratica.info/) * [A Pattern Language - Christopher Alexander (Architecture)](https://patternlanguage.cc/) * [Sideny’s 3D Artist’s Handbook](https://sidney-eliot.github.io/3d-artists-handbook/) * [Brandon Boswell’s Garden](https://brandonkboswell.com/) * [Data Engineering Vault: A Second Brain Knowledge Network](https://vault.ssp.sh/) * [🪴Aster’s notebook](https://notes.asterhu.com/) * [Gatekeeper Wiki](https://www.gatekeeper.wiki/) * [Ellie’s Notes](https://ellie.wtf/) * [Eledah’s Crystalline](https://blog.eledah.ir/) * [🌓 Projects & Privacy - FOSS, tech, law](https://be-far.com/) * * * --- # plugin/filter plugin/filter ============= 2 items with this tag. * May 24, 2026 ### [ExplicitPublish](https://quartz.jzhao.xyz/plugins/explicitpublish) * [plugin/filter](https://quartz.jzhao.xyz/tags/plugin/filter) * May 24, 2026 ### [RemoveDrafts](https://quartz.jzhao.xyz/plugins/removedrafts) * [plugin/filter](https://quartz.jzhao.xyz/tags/plugin/filter) * * * --- # ArticleTitle ArticleTitle ============ Properties2 | | | | --- | --- | | description | Renders the article title as an h1 heading. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin renders the article title from the page’s frontmatter as an `

` heading at the top of the page content. It reads the `title` field from frontmatter (falling back to the filename if no title is set). > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/articletitle#api) --------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.ArticleTitle()`. * Source: [`quartz-community/article-title`](https://github.com/quartz-community/article-title) * Install: `npx quartz plugin add github:quartz-community/article-title` * * * --- # Backlinks Backlinks ========= Properties2 | | | | --- | --- | | description | Shows pages that link to the current page. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Shows pages that link to the current page. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [Backlinks](https://quartz.jzhao.xyz/plugins/backlinks) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/backlinks#configuration) -------------------------------------------------------------------------- This plugin accepts the following configuration options: * `hideWhenEmpty`: Hide the backlinks section if the current page has no backlinks. Defaults to `true`. ### Default options[](https://quartz.jzhao.xyz/plugins/backlinks#default-options) quartz.config.yaml - source: github:quartz-community/backlinks enabled: true options: hideWhenEmpty: true API[](https://quartz.jzhao.xyz/plugins/backlinks#api) ------------------------------------------------------ * Category: Component * Function name: `ExternalPlugin.Backlinks()`. * Source: [`quartz-community/backlinks`](https://github.com/quartz-community/backlinks) * Install: `npx quartz plugin add github:quartz-community/backlinks` * * * --- # ContentMeta ContentMeta =========== Properties2 | | | | --- | --- | | description | Displays creation date and reading time. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin displays content metadata below the article title, such as the creation date and estimated reading time. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Configuration[](https://quartz.jzhao.xyz/plugins/contentmeta#configuration) ---------------------------------------------------------------------------- This plugin accepts the following configuration options: * `showReadingTime`: Whether to display the estimated reading time. Defaults to `true`. * `showComma`: Whether to display a comma between metadata items. Defaults to `true`. ### Default options[](https://quartz.jzhao.xyz/plugins/contentmeta#default-options) quartz.config.yaml - source: github:quartz-community/content-meta enabled: true options: showReadingTime: true showComma: true API[](https://quartz.jzhao.xyz/plugins/contentmeta#api) -------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.ContentMeta()`. * Source: [`quartz-community/content-meta`](https://github.com/quartz-community/content-meta) * Install: `npx quartz plugin add github:quartz-community/content-meta` * * * --- # Darkmode Darkmode ======== Properties2 | | | | --- | --- | | description | Toggle between light and dark themes. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Dark mode toggle. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [Darkmode](https://quartz.jzhao.xyz/plugins/darkmode) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/darkmode#configuration) ------------------------------------------------------------------------- This plugin accepts the following configuration options: * `enabled`: Whether to enable the dark mode toggle. Defaults to `true`. ### Default options[](https://quartz.jzhao.xyz/plugins/darkmode#default-options) quartz.config.yaml - source: github:quartz-community/darkmode enabled: true API[](https://quartz.jzhao.xyz/plugins/darkmode#api) ----------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Darkmode()`. * Source: [`quartz-community/darkmode`](https://github.com/quartz-community/darkmode) * Install: `npx quartz plugin add github:quartz-community/darkmode` * * * --- # Favicon Favicon ======= Properties2 | | | | --- | --- | | description | Emits the site favicon. | | tags | [plugin/emitter](https://quartz.jzhao.xyz/tags/plugin/emitter) | May 24, 20261 min read This plugin emits a `favicon.ico` into the `public` folder. It creates the favicon from `icon.png` located in the `quartz/static` folder. The plugin resizes `icon.png` to 48x48px to make it as small as possible. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/favicon#api) ---------------------------------------------------- * Category: Emitter * Function name: `ExternalPlugin.Favicon()`. * Source: [`quartz-community/favicon`](https://github.com/quartz-community/favicon) * Install: `npx quartz plugin add github:quartz-community/favicon` * * * --- # Footer Footer ====== Properties2 | | | | --- | --- | | description | Page footer with configurable links. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin renders a footer at the bottom of the page with a “Created with Quartz” message and a set of configurable links. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Configuration[](https://quartz.jzhao.xyz/plugins/footer#configuration) ----------------------------------------------------------------------- This plugin accepts the following configuration options: * `links`: A map of link labels to their URLs to display in the footer. Defaults to `{}`. ### Default options[](https://quartz.jzhao.xyz/plugins/footer#default-options) quartz.config.yaml - source: github:quartz-community/footer enabled: true options: links: GitHub: https://github.com/jackyzha0/quartz Discord Community: https://discord.gg/cRFFHYye7t API[](https://quartz.jzhao.xyz/plugins/footer#api) --------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Footer()`. * Source: [`quartz-community/footer`](https://github.com/quartz-community/footer) * Install: `npx quartz plugin add github:quartz-community/footer` * * * --- # feature/emitter feature/emitter =============== 2 items with this tag. * May 24, 2026 ### [Custom OG Images](https://quartz.jzhao.xyz/plugins/customogimages) * [feature/emitter](https://quartz.jzhao.xyz/tags/feature/emitter) * Jul 10, 2024 ### [Folder and Tag Listings](https://quartz.jzhao.xyz/features/folder-and-tag-listings) * [feature/emitter](https://quartz.jzhao.xyz/tags/feature/emitter) * [component](https://quartz.jzhao.xyz/tags/component) * * * --- # Breadcrumbs Breadcrumbs =========== Properties2 | | | | --- | --- | | description | Breadcrumb navigation trail. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Navigation breadcrumb trail. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [Breadcrumbs](https://quartz.jzhao.xyz/plugins/breadcrumbs) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/breadcrumbs#configuration) ---------------------------------------------------------------------------- This plugin accepts the following configuration options: * `spacerSymbol`: The symbol to use between breadcrumb items. Defaults to `"❯"`. * `rootName`: The name of the root page. Defaults to `Home`. * `resolveFrontmatterTitle`: Whether to use the `title` frontmatter field for breadcrumb items. Defaults to `true`. * `showCurrentPage`: Whether to show the current page in the breadcrumb trail. Defaults to `true`. ### Default options[](https://quartz.jzhao.xyz/plugins/breadcrumbs#default-options) quartz.config.yaml - source: github:quartz-community/breadcrumbs enabled: true options: spacerSymbol: "❯" rootName: Home resolveFrontmatterTitle: true showCurrentPage: true API[](https://quartz.jzhao.xyz/plugins/breadcrumbs#api) -------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Breadcrumbs()`. * Source: [`quartz-community/breadcrumbs`](https://github.com/quartz-community/breadcrumbs) * Install: `npx quartz plugin add github:quartz-community/breadcrumbs` * * * --- # Comments Comments ======== Properties2 | | | | --- | --- | | description | Comment system integration (Giscus, Utterances, etc.). | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Comment system (giscus, utterances, etc.). > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [Comments](https://quartz.jzhao.xyz/plugins/comments) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/comments#configuration) ------------------------------------------------------------------------- This plugin accepts the following configuration options: * `provider`: The comment provider to use. Currently only `giscus` is supported. * `options`: Provider-specific options. * `repo`: The GitHub repository to use for comments. * `repoId`: The ID of the GitHub repository. * `category`: The discussion category to use. * `categoryId`: The ID of the discussion category. * `lang`: The language for the comment system. Defaults to `en`. * `themeUrl`: URL to a folder with custom themes. * `lightTheme`: Filename for the light theme CSS file. Defaults to `light`. * `darkTheme`: Filename for the dark theme CSS file. Defaults to `dark`. * `mapping`: How to map pages to discussions. Defaults to `url`. * `strict`: Use strict title matching. Defaults to `true`. * `reactionsEnabled`: Whether to enable reactions for the main post. Defaults to `true`. * `inputPosition`: Where to put the comment input box relative to the comments. Defaults to `bottom`. ### Default options[](https://quartz.jzhao.xyz/plugins/comments#default-options) quartz.config.yaml - source: github:quartz-community/comments enabled: true options: provider: giscus options: repo: jackyzha0/quartz repoId: MDEwOlJlcG9zaXRvcnkzODcyMTMyMDg category: Announcements categoryId: DIC_kwDOFxRnmM4B-Xg6 lang: en API[](https://quartz.jzhao.xyz/plugins/comments#api) ----------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Comments()`. * Source: [`quartz-community/comments`](https://github.com/quartz-community/comments) * Install: `npx quartz plugin add github:quartz-community/comments` * * * --- # Explorer Explorer ======== Properties2 | | | | --- | --- | | description | File tree explorer sidebar. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20262 min read File tree explorer sidebar. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [Explorer](https://quartz.jzhao.xyz/plugins/explorer) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/explorer#configuration) ------------------------------------------------------------------------- This plugin accepts the following configuration options: **YAML options** (in `quartz.config.yaml`): * `title`: The title of the explorer. Defaults to `Explorer`. * `folderClickBehavior`: The behavior when a folder is clicked. Can be `"link"` to navigate or `"collapse"` to toggle. Defaults to `link`. * `folderDefaultState`: The default state of folders. Can be `"collapsed"` or `"open"`. Defaults to `collapsed`. * `useSavedState`: Whether to use local storage to save the state of the explorer. Defaults to `true`. **TS override options** (in `quartz.ts`, for callback functions that can’t be expressed in YAML): * `sortFn`: Custom sort function for ordering files and folders. * `filterFn`: Custom filter function to exclude specific nodes. * `mapFn`: Custom map function to transform node properties (e.g. display names). * `order`: Array controlling the order of operations. Defaults to `["filter", "map", "sort"]`. ### Default options[](https://quartz.jzhao.xyz/plugins/explorer#default-options) quartz.config.yaml - source: github:quartz-community/explorer enabled: true options: title: Explorer folderClickBehavior: link folderDefaultState: collapsed useSavedState: true ### TS override example[](https://quartz.jzhao.xyz/plugins/explorer#ts-override-example) quartz.ts import * as ExternalPlugin from "./.quartz/plugins" // Must be placed before loadQuartzConfig() ExternalPlugin.Explorer({ mapFn: (node) => { node.displayName = node.displayName.toUpperCase() return node }, }) See [features/explorer > Advanced customization](https://quartz.jzhao.xyz/features/explorer#advanced-customization) for more examples. API[](https://quartz.jzhao.xyz/plugins/explorer#api) ----------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Explorer()`. * Source: [`quartz-community/explorer`](https://github.com/quartz-community/explorer) * Install: `npx quartz plugin add github:quartz-community/explorer` * * * --- # NotFoundPage NotFoundPage ============ Properties1 | | | | --- | --- | | tags | [plugin/pagetype](https://quartz.jzhao.xyz/tags/plugin/pagetype) | Mar 19, 20261 min read This plugin emits a 404 (Not Found) page for broken or non-existent URLs. It uses the `minimal` [page frame](https://quartz.jzhao.xyz/layout#page-frames) (no sidebars, no header or beforeBody chrome — only content and footer) to present a clean error page. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/notfoundpage#api) --------------------------------------------------------- * Category: Page Type * Function name: `Plugin.NotFoundPage()` (internal plugin). * Source: [`quartz/plugins/pageTypes/404.ts`](https://github.com/jackyzha0/quartz/blob/v5/quartz/plugins/pageTypes/404.ts) * * * --- # Spacer Spacer ====== Properties2 | | | | --- | --- | | description | Flexible spacer for layout groups. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin renders a flexible spacer element that pushes adjacent components apart within a layout group. It uses CSS `flex: 2 1 auto` to fill available space, making it useful for spacing out items in toolbars or sidebars (for example, separating the search bar from the darkmode toggle in the left sidebar toolbar). > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/spacer#api) --------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Spacer()`. * Source: [`quartz-community/spacer`](https://github.com/quartz-community/spacer) * Install: `npx quartz plugin add github:quartz-community/spacer` * * * --- # Graph View Graph View ========== Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | Mar 14, 20262 min read Quartz features a graph-view that can show both a local graph view and a global graph view. * The local graph view shows files that either link to the current file or are linked from the current file. In other words, it shows all notes that are _at most_ one hop away. * The global graph view can be toggled by clicking the graph icon on the top-right of the local graph view. It shows _all_ the notes in your graph and how they connect to each other. > Info > > The Graph View is now a community plugin. This demonstrates how external plugins can extend Quartz functionality while serving as a reference implementation for plugin developers. Installation[](https://quartz.jzhao.xyz/features/graph-view#installation) -------------------------------------------------------------------------- The Graph View is available as a community plugin from GitHub: npm install github:quartz-community/graph --legacy-peer-deps Then add it to your `quartz.config.yaml`: quartz.config.yaml plugins: - source: github:quartz-community/graph enabled: true layout: position: right priority: 10 Features[](https://quartz.jzhao.xyz/features/graph-view#features) ------------------------------------------------------------------ By default, the node radius is proportional to the total number of incoming and outgoing internal links from that file. Additionally, similar to how browsers highlight visited links a different colour, the graph view will also show nodes that you have visited in a different colour. > Info > > Graph View requires the `ContentIndex` emitter plugin to be present in the [configuration](https://quartz.jzhao.xyz/configuration) > . Customization[](https://quartz.jzhao.xyz/features/graph-view#customization) ---------------------------------------------------------------------------- Most configuration can be done by passing in options to `Graph()`. For example, here’s what the default configuration looks like: quartz.config.yaml plugins: - source: github:quartz-community/graph enabled: true options: localGraph: drag: true zoom: true depth: 1 scale: 1.1 repelForce: 0.5 centerForce: 0.3 linkDistance: 30 fontSize: 0.6 opacityScale: 1 removeTags: [] showTags: true enableRadial: false globalGraph: drag: true zoom: true depth: -1 scale: 0.9 repelForce: 0.5 centerForce: 0.3 linkDistance: 30 fontSize: 0.6 opacityScale: 1 removeTags: [] showTags: true focusOnHover: true enableRadial: true layout: position: right priority: 10 When passing in your own options, you can omit any or all of these fields if you’d like to keep the default value for that field. Want to customize it even more? * Removing graph view: remove the `graph` entry from `quartz.config.yaml` or set `enabled: false` * Component source: [https://github.com/quartz-community/graph](https://github.com/quartz-community/graph) * * * --- # Social Media Preview Cards Social Media Preview Cards ========================== Mar 13, 20251 min read A lot of social media platforms can display a rich preview for your website when sharing a link (most notably, a cover image, a title and a description). Quartz can also dynamically generate and use new cover images for every page to be used in link previews on social media for you. Showcase[](https://quartz.jzhao.xyz/features/social-images#showcase) --------------------------------------------------------------------- After enabling the [CustomOgImages](https://quartz.jzhao.xyz/plugins/customogimages) emitter plugin, the social media link preview for [Authoring Content](https://quartz.jzhao.xyz/authoring-content) looks like this: | Light | Dark | | --- | --- | | ![](https://quartz.jzhao.xyz/images/social-image-preview-light.png) | ![](https://quartz.jzhao.xyz/images/social-image-preview-dark.png) | Configuration[](https://quartz.jzhao.xyz/features/social-images#configuration) ------------------------------------------------------------------------------- This functionality is provided by the [CustomOgImages](https://quartz.jzhao.xyz/plugins/customogimages) plugin. See the plugin page for customization options. * * * --- # Reader Mode Reader Mode =========== Properties1 | | | | --- | --- | | tags | [component](https://quartz.jzhao.xyz/tags/component) | Mar 14, 20261 min read Reader Mode is a feature that allows users to focus on the content by hiding the sidebars and other UI elements. When enabled, it provides a clean, distraction-free reading experience. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Configuration[](https://quartz.jzhao.xyz/features/reader-mode#configuration) ----------------------------------------------------------------------------- Reader Mode is enabled by default. To disable it, set `enabled: false` in your `quartz.config.yaml`: quartz.config.yaml plugins: - source: github:quartz-community/reader-mode enabled: false Or remove the plugin entirely: npx quartz plugin remove github:quartz-community/reader-mode * Install: `npx quartz plugin add github:quartz-community/reader-mode` * Source: [`quartz-community/reader-mode`](https://github.com/quartz-community/reader-mode) Usage[](https://quartz.jzhao.xyz/features/reader-mode#usage) ------------------------------------------------------------- The Reader Mode toggle appears as a button with a book icon. When clicked: * Sidebars are hidden * Hovering over the content area reveals the sidebars temporarily Unlike Dark Mode, Reader Mode state is not persisted between page reloads but is maintained during SPA navigation within the site. Customization[](https://quartz.jzhao.xyz/features/reader-mode#customization) ----------------------------------------------------------------------------- You can customize the appearance of Reader Mode through CSS variables and styles. The component uses the following classes: * `.readermode`: The toggle button * `.readerIcon`: The book icon * `[reader-mode="on"]`: Applied to the root element when Reader Mode is active Example customization in your custom CSS: .readermode { // Customize the button svg { stroke: var(--custom-color); } } * * * --- # Obsidian Compatibility Obsidian Compatibility ====================== Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | May 25, 20265 min read Quartz was originally designed as a tool to publish Obsidian vaults as websites. Even as the scope of Quartz has widened over time, it hasn’t lost the ability to seamlessly interoperate with Obsidian. By default, Quartz ships with the [ObsidianFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/obsidianflavoredmarkdown) plugin, which is a transformer plugin that adds support for [Obsidian Flavored Markdown](https://help.obsidian.md/Editing+and+formatting/Obsidian+Flavored+Markdown) . This includes support for features like [wikilinks](https://quartz.jzhao.xyz/features/wikilinks) and [Mermaid diagrams](https://quartz.jzhao.xyz/features/mermaid-diagrams) . It also ships with support for [frontmatter parsing](https://help.obsidian.md/Editing+and+formatting/Properties) with the same fields that Obsidian uses through the [Frontmatter](https://quartz.jzhao.xyz/plugins/frontmatter) transformer plugin. Finally, Quartz also provides [CrawlLinks](https://quartz.jzhao.xyz/plugins/crawllinks) plugin, which allows you to customize Quartz’s link resolution behaviour to match Obsidian. Supported Features[](https://quartz.jzhao.xyz/features/obsidian-compatibility#supported-features) -------------------------------------------------------------------------------------------------- ### Wikilinks[](https://quartz.jzhao.xyz/features/obsidian-compatibility#wikilinks) Internal links using the `[[page]]` syntax are converted to regular links. See [wikilinks](https://quartz.jzhao.xyz/features/wikilinks) for more details. All variations are supported: [[Page]] Link to a page [[Page|Custom text]] Link with alias [[Page#Heading]] Link to a heading [[Page#Heading|Custom text]] Link to a heading with alias [[Page#^block-id]] Link to a block reference ![[Page]] Embed (transclude) a page ![[image.png]] Embed an image ![[image.png|alt 100x200]] Embed with alt text and dimensions Inside tables, pipes in wikilinks can be escaped with a backslash: | Column | | --------------- | | [[page\|alias]] | ### Highlights[](https://quartz.jzhao.xyz/features/obsidian-compatibility#highlights) Wrap text in `==` to highlight it: This is ==highlighted text== in a sentence. This renders as: This is highlighted text in a sentence. ### Comments[](https://quartz.jzhao.xyz/features/obsidian-compatibility#comments) Obsidian-style comments are stripped from the output: This is visible. %%This is a comment and won't appear.%% This renders as: This is visible. Multi-line comments are also supported: %% This entire block is a comment. %% ### Tags[](https://quartz.jzhao.xyz/features/obsidian-compatibility#tags) Tags starting with `#` are parsed and linked to tag pages: #tag #nested/tag #tag-with-dashes For example: [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) > Note > > Pure numeric tags like `#123` are ignored, matching Obsidian behaviour. ### Callouts[](https://quartz.jzhao.xyz/features/obsidian-compatibility#callouts) [Obsidian callouts](https://quartz.jzhao.xyz/features/callouts) are fully supported, including collapsible variants: > [!note] > > This is a note callout. > [!warning]- Collapsed by default > > This content is hidden initially. > [!tip]+ Expanded by default > > This content is visible initially. > Live example > > This is a live callout rendered from Obsidian-flavored Markdown. All built-in callout types are supported: `note`, `abstract`, `info`, `todo`, `tip`, `success`, `question`, `warning`, `failure`, `danger`, `bug`, `example`, and `quote`, along with their aliases. ### Task Lists and Custom Task Characters[](https://quartz.jzhao.xyz/features/obsidian-compatibility#task-lists-and-custom-task-characters) Standard checkboxes work out of the box. With `enableCheckbox: true`, you also get support for custom task characters that are popular in the Obsidian community: - [ ] Unchecked - [x] Checked - [?] Question - [!] Important - [>] Forwarded - [/] In progress - [-] Cancelled - [s] Special Each custom character is preserved as a `data-task` attribute on the rendered element, allowing CSS-based styling per character. * [ ] Unchecked * [x] Checked * [x] Question * [x] Important ### Mermaid Diagrams[](https://quartz.jzhao.xyz/features/obsidian-compatibility#mermaid-diagrams) [Mermaid](https://quartz.jzhao.xyz/features/mermaid-diagrams) code blocks are rendered as diagrams: ```mermaid graph TD A[Start] --> B{Decision} B -->|Yes| C[OK] B -->|No| D[Cancel] ``` `graph TD A[Start] --> B{Decision} B -->|Yes| C[OK] B -->|No| D[Cancel]` ### YouTube Embeds[](https://quartz.jzhao.xyz/features/obsidian-compatibility#youtube-embeds) YouTube videos can be embedded using standard image syntax with a YouTube URL: ![](https://youtu.be/v5LGaczJaf0) ![](https://www.youtube.com/watch?v=v5LGaczJaf0) For example, the following embed is rendered from `![](https://youtu.be/v5LGaczJaf0)`: ### Tweet Embeds[](https://quartz.jzhao.xyz/features/obsidian-compatibility#tweet-embeds) Tweets from Twitter/X are embedded as static blockquotes with a link to the original: ![](https://x.com/kepano/status/1882142872826442145) ![](https://twitter.com/kepano/status/1882142872826442145) For example, the following embed is rendered from `![](https://x.com/kepano/status/1882142872826442145)`: > Priorities for my personal site: > > 1\. I can write and publish directly from Obsidian > 2\. I can preview the site offline > 3\. I can switch hosts easily, all the data is in my control > > For a personal site I find that CMSes like Wordpress, Squarespace, and Webflow, add too much… > > — kepano (@kepano) [January 22, 2025](https://x.com/kepano/status/1882142872826442145?ref_src=twsrc%5Etfw) ### Block References[](https://quartz.jzhao.xyz/features/obsidian-compatibility#block-references) Block references allow linking to specific blocks within a page: Content paragraph. ^my-block [[Page#^my-block]] ### Obsidian URI Links[](https://quartz.jzhao.xyz/features/obsidian-compatibility#obsidian-uri-links) Links using the `obsidian://` protocol are marked with a CSS class (`obsidian-uri`) and a `data-obsidian-uri` attribute, so you can style them differently from regular links. ### Video Embeds[](https://quartz.jzhao.xyz/features/obsidian-compatibility#video-embeds) Video files can be embedded using standard image syntax: ![](video.mp4) ![](video.webm) ### Embed in HTML[](https://quartz.jzhao.xyz/features/obsidian-compatibility#embed-in-html) By default, Obsidian does not render its Markdown syntax inside HTML blocks. Quartz extends this with the `enableInHtmlEmbed` option, which parses wikilinks, highlights, and tags inside raw HTML nodes. ### Footnotes[](https://quartz.jzhao.xyz/features/obsidian-compatibility#footnotes) Footnotes using the `[^1]` syntax are fully supported through the [GitHubFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/githubflavoredmarkdown) plugin: Here is a sentence with a footnote.[^1] [^1]: This is the footnote content. Obsidian Community Plugin Support[](https://quartz.jzhao.xyz/features/obsidian-compatibility#obsidian-community-plugin-support) -------------------------------------------------------------------------------------------------------------------------------- Quartz focuses on supporting Obsidian’s core features. Functionality from Obsidian community plugins is handled by Quartz community plugins: | Obsidian Plugin | Quartz Support | | --- | --- | | Dataview | Supported via [Quartz Syncer](https://community.obsidian.md/plugins/quartz-syncer)
— exports Dataview queries as static content during sync | | Excalidraw | Supported via the `obsidian-plugin-excalidraw` community plugin | | Leaflet Maps | Supported via the `obsidian-plugin-leaflet` community plugin | | Style Settings | Supported via the `quartz-themes` community plugin | > Tip > > As a general rule: Obsidian core features are supported by Quartz directly, while Obsidian community plugin features are supported by corresponding Quartz community plugins. Not all Obsidian community plugins will have Quartz equivalents, but popular ones are likely to be supported by the community. Configuration[](https://quartz.jzhao.xyz/features/obsidian-compatibility#configuration) ---------------------------------------------------------------------------------------- This functionality is provided by the [ObsidianFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/obsidianflavoredmarkdown) , [Frontmatter](https://quartz.jzhao.xyz/plugins/frontmatter) and [CrawlLinks](https://quartz.jzhao.xyz/plugins/crawllinks) plugins. See the plugin pages for customization options. * * * --- # CNAME CNAME ===== Properties2 | | | | --- | --- | | description | Emits a CNAME file for custom domain deployment. | | tags | [plugin/emitter](https://quartz.jzhao.xyz/tags/plugin/emitter) | May 24, 20261 min read This plugin emits a `CNAME` record that points your subdomain to the default domain of your site. If you want to use a custom domain name like `quartz.example.com` for the site, then this is needed. See [Hosting](https://quartz.jzhao.xyz/hosting) for more information. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/cname#api) -------------------------------------------------- * Category: Emitter * Function name: `ExternalPlugin.CNAME()`. * Source: [`quartz-community/cname`](https://github.com/quartz-community/cname) * Install: `npx quartz plugin add github:quartz-community/cname` * * * --- # ContentIndex ContentIndex ============ Properties2 | | | | --- | --- | | description | Generates sitemap, RSS feed, and content index. | | tags | [plugin/emitter](https://quartz.jzhao.xyz/tags/plugin/emitter) | May 24, 20262 min read This plugin emits both RSS and an XML sitemap for your site. The [RSS Feed](https://quartz.jzhao.xyz/features/rss-feed) allows users to subscribe to content on your site and the sitemap allows search engines to better index your site. The plugin also emits a `contentIndex.json` file which is used by dynamic frontend components like search and graph. This plugin emits a comprehensive index of the site’s content, generating additional resources such as a sitemap, an RSS feed, and a > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `enableSiteMap`: If `true` (default), generates a sitemap XML file (`sitemap.xml`) listing all site URLs for search engines in content discovery. * `enableRSS`: If `true` (default), produces an RSS feed (`index.xml`) with recent content updates. * `rssLimit`: Defines the maximum number of entries to include in the RSS feed, helping to focus on the most recent or relevant content. Defaults to `10`. * `rssFullHtml`: If `true`, the RSS feed includes the full rendered HTML content of each page. Defaults to `false`. * `rssSlug`: Slug to the generated RSS feed XML file. Defaults to `"index"`. * `includeEmptyFiles`: If `true` (default), content files with no body text are included in the generated index and resources. API[](https://quartz.jzhao.xyz/plugins/contentindex#api) --------------------------------------------------------- * Category: Emitter * Function name: `ExternalPlugin.ContentIndex()`. * Source: [`quartz-community/content-index`](https://github.com/quartz-community/content-index) * Install: `npx quartz plugin add github:quartz-community/content-index` * * * --- # Citations Citations ========= Properties2 | | | | --- | --- | | description | Academic citation and bibliography support via BibTeX. | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer) | May 24, 20261 min read This plugin adds Citation support to Quartz. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `bibliographyFile`: the path to the bibliography file. Defaults to `./bibliography.bib`. This is relative to git source of your vault. * `suppressBibliography`: whether to suppress the bibliography at the end of the document. Defaults to `false`. * `linkCitations`: whether to link citations to the bibliography. Defaults to `false`. * `csl`: the citation style to use. Defaults to `apa`. Reference [rehype-citation](https://rehype-citation.netlify.app/custom-csl) for more options. API[](https://quartz.jzhao.xyz/plugins/citations#api) ------------------------------------------------------ * Category: Transformer * Function name: `ExternalPlugin.Citations()`. * Source: [`quartz-community/citations`](https://github.com/quartz-community/citations) * Install: `npx quartz plugin add github:quartz-community/citations` * * * --- # ComponentResources ComponentResources ================== Properties1 | | | | --- | --- | | tags | [plugin/emitter](https://quartz.jzhao.xyz/tags/plugin/emitter) | Mar 19, 20261 min read This plugin manages and emits the static resources required for the Quartz framework. This includes CSS stylesheets and JavaScript scripts that enhance the functionality and aesthetics of the generated site. See also the `cdnCaching` option in the `theme` section of the [configuration](https://quartz.jzhao.xyz/configuration) . > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/componentresources#api) --------------------------------------------------------------- * Category: Emitter * Function name: `Plugin.ComponentResources()` (internal plugin). * Source: [`quartz/plugins/emitters/componentResources.ts`](https://github.com/jackyzha0/quartz/blob/v5/quartz/plugins/emitters/componentResources.ts) . * * * --- # ContentPage ContentPage =========== Properties2 | | | | --- | --- | | description | Generates HTML pages for Markdown content. | | tags | [plugin/pagetype](https://quartz.jzhao.xyz/tags/plugin/pagetype) | May 24, 20261 min read This plugin is a page type plugin for the Quartz framework. It generates the HTML pages for each piece of Markdown content. It emits the full-page [layout](https://quartz.jzhao.xyz/layout) , including headers, footers, and body content, among others. It uses the `default` [page frame](https://quartz.jzhao.xyz/layout#page-frames) (three-column layout with sidebars). It is now configured in the `pageTypes` section of `quartz.config.yaml`. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/contentpage#api) -------------------------------------------------------- * Category: Page Type * Function name: `ExternalPlugin.ContentPage()`. * Source: [`quartz-community/content-page`](https://github.com/quartz-community/content-page) * Install: `npx quartz plugin add github:quartz-community/content-page` * * * --- # HardLineBreaks HardLineBreaks ============== Properties2 | | | | --- | --- | | description | Treats single newlines as hard line breaks. | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer) | May 24, 20261 min read This plugin automatically converts single line breaks in Markdown text into hard line breaks in the HTML output. This plugin is not enabled by default as this doesn’t follow the semantics of actual Markdown but you may enable it if you’d like parity with [Obsidian](https://quartz.jzhao.xyz/features/obsidian-compatibility) . > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/hardlinebreaks#api) ----------------------------------------------------------- * Category: Transformer * Function name: `ExternalPlugin.HardLineBreaks()`. * Source: [`quartz-community/hard-line-breaks`](https://github.com/quartz-community/hard-line-breaks) * Install: `npx quartz plugin add github:quartz-community/hard-line-breaks` * * * --- # GitHubFlavoredMarkdown GitHubFlavoredMarkdown ====================== Properties2 | | | | --- | --- | | description | GitHub Flavored Markdown support (tables, task lists, footnotes, strikethrough). | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer) | May 24, 20261 min read This plugin enhances Markdown processing to support GitHub Flavored Markdown (GFM) which adds features like autolink literals, footnotes, strikethrough, tables and tasklists. In addition, this plugin adds optional features for typographic refinement (such as converting straight quotes to curly quotes, dashes to en-dashes/em-dashes, and ellipses) and automatic heading links as a symbol that appears next to the heading on hover. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `enableSmartyPants`: When true, enables typographic enhancements. Default is true. * `linkHeadings`: When true, automatically adds links to headings. Default is true. API[](https://quartz.jzhao.xyz/plugins/githubflavoredmarkdown#api) ------------------------------------------------------------------- * Category: Transformer * Function name: `ExternalPlugin.GitHubFlavoredMarkdown()`. * Source: [`quartz-community/github-flavored-markdown`](https://github.com/quartz-community/github-flavored-markdown) * Install: `npx quartz plugin add github:quartz-community/github-flavored-markdown` * * * --- # NoteProperties NoteProperties ============== Properties2 | | | | --- | --- | | description | Displays frontmatter properties in a collapsible panel. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read The NoteProperties plugin is documented under [Frontmatter](https://quartz.jzhao.xyz/plugins/frontmatter) . > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. API[](https://quartz.jzhao.xyz/plugins/noteproperties#api) ----------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.NoteProperties()`. * Source: [`quartz-community/note-properties`](https://github.com/quartz-community/note-properties) * Install: `npx quartz plugin add github:quartz-community/note-properties` * * * --- # AliasRedirects AliasRedirects ============== Properties2 | | | | --- | --- | | description | Generates redirect pages from frontmatter aliases and case-preserving URLs. | | tags | [plugin/emitter](https://quartz.jzhao.xyz/tags/plugin/emitter) | Jun 09, 20262 min read This plugin emits HTML redirect pages so that old URLs redirect to the canonical page. It handles two types of redirects: 1. **Frontmatter aliases**: Redirect pages for aliases defined in your content’s frontmatter. 2. **Case-preserving redirects**: Automatic redirect pages for URLs that changed due to Quartz v5’s lowercase slug normalization. ### Frontmatter Aliases[](https://quartz.jzhao.xyz/plugins/aliasredirects#frontmatter-aliases) If `foo.md` has the following frontmatter: foo.md --- title: "Foo" aliases: - "bar" --- The target `host.me/bar` will be permanently redirected to `host.me/foo`. The emitter supports the following frontmatter fields: * `aliases` * `alias` ### Case-Preserving Redirects[](https://quartz.jzhao.xyz/plugins/aliasredirects#case-preserving-redirects) Quartz v5 normalizes all URLs to lowercase. If you are migrating from v4 (which preserved the original casing), previously indexed URLs containing uppercase letters (e.g. `/Diary/My-Note`) would return 404 errors. When `enableCaseRedirects` is enabled (the default), this plugin automatically detects files whose original path differs from the lowercased slug and generates redirect pages at the original-case URL. For example, if your content directory contains `Diary/2026-01-01.md`, the plugin generates: * The canonical page at `/diary/2026-01-01` (produced by the normal build) * A redirect page at `/Diary/2026-01-01` (produced by this plugin) The redirect page includes proper SEO signals: * `` for an instant redirect * `` pointing to the lowercase URL * `` to prevent duplicate indexing This preserves search engine rankings and ensures inbound links continue to work. > Note > > Case-preserving redirects have no effect on case-insensitive filesystems (macOS, Windows) where the server already resolves either casing to the same file. The plugin automatically detects the filesystem type and skips redirect generation when unnecessary. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `enableCaseRedirects`: If `true` (default), automatically generates redirect pages for URLs that changed casing due to v5’s lowercase normalization. Set to `false` to disable this behavior. API[](https://quartz.jzhao.xyz/plugins/aliasredirects#api) ----------------------------------------------------------- * Category: Emitter * Function name: `ExternalPlugin.AliasRedirects()`. * Source: [`quartz-community/alias-redirects`](https://github.com/quartz-community/alias-redirects) * Install: `npx quartz plugin add github:quartz-community/alias-redirects` * * * --- # CanvasPage CanvasPage ========== Properties2 | | | | --- | --- | | description | Renders JSON Canvas files as interactive, pannable pages. | | tags | [plugin/pagetype](https://quartz.jzhao.xyz/tags/plugin/pagetype) | May 24, 20263 min read This plugin is a page type plugin that renders [JSON Canvas](https://jsoncanvas.org/) (`.canvas`) files as interactive, pannable and zoomable canvas pages. It uses a custom `"canvas"` [page frame](https://quartz.jzhao.xyz/layout#page-frames) that provides a fullscreen, always-on canvas experience with a togglable left sidebar for navigation. It supports the full [JSON Canvas 1.0 spec](https://jsoncanvas.org/spec/1.0/) , including text nodes with Markdown rendering, file nodes that link to other pages in your vault, link nodes for external URLs, and group nodes for visual organization. Edges between nodes are rendered as SVG paths with optional labels, arrow markers, and colors. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `enableInteraction`: Whether to enable pan and zoom interaction on the canvas. Default: `true`. * `initialZoom`: The initial zoom level when the canvas is first displayed. Default: `1`. * `minZoom`: The minimum zoom level allowed when zooming out. Default: `0.1`. * `maxZoom`: The maximum zoom level allowed when zooming in. Default: `5`. ### Canvas Frame[](https://quartz.jzhao.xyz/plugins/canvaspage#canvas-frame) The canvas-page plugin provides its own `"canvas"` page frame via the [Frame Registry](https://quartz.jzhao.xyz/layout#page-frames) . This frame: * Renders the canvas in **fullscreen mode** by default (100vw × 100vh), giving the canvas maximum screen space — leaning into the “endless canvas” concept of JSON Canvas. * Provides a **togglable left sidebar** that slides in from the left edge. This is the only layout slot available — it renders the same components as the `left` sidebar on content pages (e.g., Explorer, Search, Page Title). * The sidebar toggle button (hamburger/close icon) is positioned in the top-left corner. * Canvas controls (zoom in, zoom out, reset) are positioned on the right side. * On mobile, the sidebar overlays the canvas rather than pushing it aside. Users can override this frame via `quartz.config.yaml` if needed: quartz.config.yaml layout: byPageType: canvas: template: default # Use standard three-column layout instead ### Features[](https://quartz.jzhao.xyz/plugins/canvaspage#features) * **Text nodes**: Render Markdown content including headings, bold, italic, strikethrough, lists, links, and code blocks via [GFM](https://github.github.com/gfm/) support. * **File nodes**: Link to other pages in your vault. Supports popover previews on hover. * **Link nodes**: Reference external URLs. * **Group nodes**: Visual grouping containers with optional labels and background colors. * **Edges**: SVG connections between nodes with optional labels, arrow markers, and colors. Supports all four sides (top, right, bottom, left) and both preset colors (1–6) and custom hex colors. * **Togglable sidebar**: Hamburger button in the top-left corner toggles the left sidebar for navigation. Press `Escape` or click the close button to dismiss. * **Preset colors**: Six preset colors (red, orange, yellow, green, cyan, purple) plus custom hex colors (`#RRGGBB`) for nodes and edges. API[](https://quartz.jzhao.xyz/plugins/canvaspage#api) ------------------------------------------------------- * Category: Page Type * Function name: `ExternalPlugin.CanvasPage()`. * Source: [`quartz-community/canvas-page`](https://github.com/quartz-community/canvas-page) * Install: `npx quartz plugin add github:quartz-community/canvas-page` * * * --- # PageTitle PageTitle ========= Properties2 | | | | --- | --- | | description | Renders the site title as a home link. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin renders the site-wide page title (configured via the `pageTitle` field in [configuration](https://quartz.jzhao.xyz/configuration) ) as a clickable link to the home page. It typically appears in the left sidebar. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. The displayed title is controlled by the `pageTitle` field in `quartz.config.yaml`. API[](https://quartz.jzhao.xyz/plugins/pagetitle#api) ------------------------------------------------------ * Category: Component * Function name: `ExternalPlugin.PageTitle()`. * Source: [`quartz-community/page-title`](https://github.com/quartz-community/page-title) * Install: `npx quartz plugin add github:quartz-community/page-title` * * * --- # ReaderMode ReaderMode ========== Properties2 | | | | --- | --- | | description | Distraction-free reading mode toggle. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Distraction-free reading mode. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [reader mode](https://quartz.jzhao.xyz/features/reader-mode) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/readermode#configuration) --------------------------------------------------------------------------- This plugin accepts the following configuration options: * `enabled`: Whether to enable reader mode. Defaults to `true`. ### Default options[](https://quartz.jzhao.xyz/plugins/readermode#default-options) quartz.config.yaml - source: github:quartz-community/reader-mode enabled: true API[](https://quartz.jzhao.xyz/plugins/readermode#api) ------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.ReaderMode()`. * Source: [`quartz-community/reader-mode`](https://github.com/quartz-community/reader-mode) * Install: `npx quartz plugin add github:quartz-community/reader-mode` * * * --- # RoamFlavoredMarkdown RoamFlavoredMarkdown ==================== Properties2 | | | | --- | --- | | description | Compatibility for Roam Research export format. | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer) | May 24, 20261 min read This plugin provides support for [Roam Research](https://roamresearch.com/) compatibility. See [Roam Research compatibility](https://quartz.jzhao.xyz/features/roam-research-compatibility) for more information. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `orComponent`: If `true` (default), converts Roam `{{ or:ONE|TWO|THREE }}` shortcodes into HTML Dropdown options. * `TODOComponent`: If `true` (default), converts Roam `{{[[TODO]]}}` shortcodes into HTML check boxes. * `DONEComponent`: If `true` (default), converts Roam `{{[[DONE]]}}` shortcodes into checked HTML check boxes. * `videoComponent`: If `true` (default), converts Roam `{{[[video]]:URL}}` shortcodes into embeded HTML video. * `audioComponent`: If `true` (default), converts Roam `{{[[audio]]:URL}}` shortcodes into embeded HTML audio. * `pdfComponent`: If `true` (default), converts Roam `{{[[pdf]]:URL}}` shortcodes into embeded HTML PDF viewer. * `blockquoteComponent`: If `true` (default), converts Roam `{{[[>]]}}` shortcodes into Quartz blockquotes. * `tableComponent`: If `true` (default), converts Roam table syntax into HTML tables. * `attributeComponent`: If `true` (default), converts Roam attribute syntax into rendered attributes. API[](https://quartz.jzhao.xyz/plugins/roamflavoredmarkdown#api) ----------------------------------------------------------------- * Category: Transformer * Function name: `ExternalPlugin.RoamFlavoredMarkdown()`. * Source: [`quartz-community/roam`](https://github.com/quartz-community/roam) * Install: `npx quartz plugin add github:quartz-community/roam` * * * --- # Search Search ====== Properties2 | | | | --- | --- | | description | Full-text search with tag filtering and keyboard navigation. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Full-text search functionality. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [full-text search](https://quartz.jzhao.xyz/features/full-text-search) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/search#configuration) ----------------------------------------------------------------------- This plugin accepts the following configuration options: * `enablePreview`: Whether to show a preview of the page content in search results. Defaults to `true`. * `fieldPriority`: An array specifying the priority order for search fields. Defaults to `["title", "content", "tags"]`. ### Default options[](https://quartz.jzhao.xyz/plugins/search#default-options) quartz.config.yaml - source: github:quartz-community/search enabled: true options: enablePreview: true fieldPriority: - title - content - tags API[](https://quartz.jzhao.xyz/plugins/search#api) --------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Search()`. * Source: [`quartz-community/search`](https://github.com/quartz-community/search) * Install: `npx quartz plugin add github:quartz-community/search` * * * --- # TagList TagList ======= Properties2 | | | | --- | --- | | description | Renders tags as clickable links. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin renders the page’s tags as a list of clickable links. Each tag links to its corresponding [tag page](https://quartz.jzhao.xyz/plugins/tagpage) , making it easy for readers to browse related content by topic. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/taglist#api) ---------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.TagList()`. * Source: [`quartz-community/tag-list`](https://github.com/quartz-community/tag-list) * Install: `npx quartz plugin add github:quartz-community/tag-list` * * * --- # Callouts Callouts ======== Properties1 | | | | --- | --- | | tags | [feature/transformer](https://quartz.jzhao.xyz/tags/feature/transformer) | Feb 23, 20242 min read Quartz supports the same Admonition-callout syntax as Obsidian. This includes * 12 Distinct callout types (each with several aliases) * Collapsable callouts > [!info] Title > > This is a callout! See [documentation on supported types and syntax here](https://help.obsidian.md/Editing+and+formatting/Callouts) . > Warning > > Wondering why callouts may not be showing up even if you have them enabled? You may need to reorder your plugins so that [ObsidianFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/obsidianflavoredmarkdown) > is _after_ [SyntaxHighlighting](https://quartz.jzhao.xyz/plugins/syntaxhighlighting) > . Customization[](https://quartz.jzhao.xyz/features/callouts#customization) -------------------------------------------------------------------------- The callouts are a functionality of the [ObsidianFlavoredMarkdown](https://quartz.jzhao.xyz/plugins/obsidianflavoredmarkdown) plugin. See the plugin page for how to enable or disable them. You can edit the icons by customizing `quartz/styles/callouts.scss`. ### Add custom callouts[](https://quartz.jzhao.xyz/features/callouts#add-custom-callouts) By default, custom callouts are handled by applying the `note` style. To make fancy ones, you have to add these lines to `custom.scss`. quartz/styles/custom.scss .callout { &[data-callout="custom"] { --color: #customcolor; --border: #custombordercolor; --bg: #custombg; --callout-icon: url("data:image/svg+xml; utf8, "); //SVG icon code } } > Warning > > Don’t forget to ensure that the SVG is URL encoded before putting it in the CSS. You can use tools like [this one](https://yoksel.github.io/url-encoder/) > to help you do that. Showcase[](https://quartz.jzhao.xyz/features/callouts#showcase) ---------------------------------------------------------------- > Info > > Default title > Can callouts be _nested_? > > > Yes!, they can. And collapsed! > > > > > You can even use multiple layers of nesting. > Note > > Aliases: “note” > Abstract > > Aliases: “abstract”, “summary”, “tldr” > Info > > Aliases: “info” > Todo > > Aliases: “todo” > Tip > > Aliases: “tip”, “hint”, “important” > Success > > Aliases: “success”, “check”, “done” > Question > > Aliases: “question”, “help”, “faq” > Warning > > Aliases: “warning”, “attention”, “caution” > Failure > > Aliases: “failure”, “missing”, “fail” > Danger > > Aliases: “danger”, “error” > Bug > > Aliases: “bug” > Example > > Aliases: “example” > Quote > > Aliases: “quote”, “cite” * * * --- # Popover Previews Popover Previews ================ Mar 14, 20261 min read Like Wikipedia, when you hover over a link in Quartz, there is a popup of a page preview that you can scroll to see the entire content. Links to headers will also scroll the popup to show that specific header in view. By default, Quartz only fetches previews for pages inside your vault due to [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) . It does this by selecting all HTML elements with the `popover-hint` class. For most pages, this includes the page title, page metadata like words and time to read, tags, and the actual page content. When [creating your own components](https://quartz.jzhao.xyz/advanced/creating-components) , you can include this `popover-hint` class to also include it in the popover. Similar to Obsidian, [images referenced using wikilinks](https://quartz.jzhao.xyz/images/quartz-layout-desktop.png) can also be viewed as popups. Configuration[](https://quartz.jzhao.xyz/features/popover-previews#configuration) ---------------------------------------------------------------------------------- * Remove popovers: set the `enablePopovers` field in `quartz.config.yaml` to be `false`. * Style: `quartz/components/styles/popover.scss` * Script: `quartz/components/scripts/popover.inline.ts` * * * --- # RecentNotes RecentNotes =========== Properties2 | | | | --- | --- | | description | Displays a list of recently modified notes. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read Shows recently modified notes. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [recent notes](https://quartz.jzhao.xyz/features/recent-notes) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/recentnotes#configuration) ---------------------------------------------------------------------------- This plugin accepts the following configuration options: * `title`: The title of the recent notes section. Defaults to `Recent notes`. * `limit`: The maximum number of recent notes to display. Defaults to `3`. * `showTags`: Whether to display the tags for each note. Defaults to `true`. * `linkToMore`: A slug to a page that shows more notes. Defaults to `false`. * `hideTagPages`: Whether to hide tag index pages from the list. Defaults to `false`. * `hideFolderPages`: Whether to hide folder index pages from the list. Defaults to `false`. ### Default options[](https://quartz.jzhao.xyz/plugins/recentnotes#default-options) quartz.config.yaml - source: github:quartz-community/recent-notes enabled: true options: limit: 3 showTags: true hideTagPages: false hideFolderPages: false API[](https://quartz.jzhao.xyz/plugins/recentnotes#api) -------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.RecentNotes()`. * Source: [`quartz-community/recent-notes`](https://github.com/quartz-community/recent-notes) * Install: `npx quartz plugin add github:quartz-community/recent-notes` * * * --- # StackedPages StackedPages ============ Properties2 | | | | --- | --- | | description | Andy Matuschak-style stacked sliding panes. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20262 min read Andy Matuschak-style stacked pages (sliding panes). Clicking internal links opens pages side by side in a horizontal stack, allowing you to trace your path through your notes. Each pane shows a full page and can be individually scrolled or closed. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. Usage[](https://quartz.jzhao.xyz/plugins/stackedpages#usage) ------------------------------------------------------------- Once enabled, clicking any internal link on a page opens the linked page as a new pane to the right instead of navigating away. The URL updates with a `#stacked=slug1,slug2` hash encoding your current stack, so you can share or bookmark a specific trail of pages. Stacked pages are disabled on mobile by default (below 800px) since horizontal panning doesn’t work well on small screens. On mobile, links navigate normally. ### Interactions[](https://quartz.jzhao.xyz/plugins/stackedpages#interactions) * **Click a link**: Opens the target page in a new pane to the right. If the maximum number of panes is reached, the leftmost pane is removed. * **Close a pane**: Click the × button in the pane header to remove it from the stack. * **Collapsed spines**: When panes overflow the viewport, earlier panes collapse to a thin vertical spine showing the page title. Click a spine to bring that pane back into focus. * **Browser back/forward**: The full stack state is stored in the URL hash and integrated with browser history, so back/forward navigation works as expected. Configuration[](https://quartz.jzhao.xyz/plugins/stackedpages#configuration) ----------------------------------------------------------------------------- This plugin accepts the following configuration options: * `maxTabs`: Maximum number of stacked panes visible at once. Defaults to `8`. * `mobileBreakpoint`: Viewport width (in pixels) below which stacked pages are disabled and links navigate normally. Defaults to `800`. * `showSpines`: Whether to show collapsed spine headers when panes overflow the viewport. Defaults to `true`. * `animateTransitions`: Whether to animate pane open/close transitions. Defaults to `true`. ### Default options[](https://quartz.jzhao.xyz/plugins/stackedpages#default-options) quartz.config.yaml - source: github:quartz-community/stacked-pages enabled: true layout: position: afterBody priority: 50 display: all options: maxTabs: 8 mobileBreakpoint: 800 showSpines: true animateTransitions: true API[](https://quartz.jzhao.xyz/plugins/stackedpages#api) --------------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.StackedPages()`. * Source: [`quartz-community/stacked-pages`](https://github.com/quartz-community/stacked-pages) * Install: `npx quartz plugin add github:quartz-community/stacked-pages` * * * --- # Graph Graph ===== Properties2 | | | | --- | --- | | description | Interactive link graph visualization. | | tags | [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20262 min read Interactive graph visualization. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. See [graph view](https://quartz.jzhao.xyz/features/graph-view) for detailed usage information. Configuration[](https://quartz.jzhao.xyz/plugins/graph#configuration) ---------------------------------------------------------------------- This plugin accepts the following configuration options: * `localGraph`: Options for the local graph view. * `globalGraph`: Options for the global graph view. Both `localGraph` and `globalGraph` accept the following options: * `drag`: Enable dragging nodes. Defaults to `true`. * `zoom`: Enable zooming. Defaults to `true`. * `depth`: The depth of the graph. Defaults to `1` for local and `-1` for global. * `scale`: The initial scale of the graph. Defaults to `1.1` for local and `0.9` for global. * `repelForce`: The force that pushes nodes apart. Defaults to `0.5`. * `centerForce`: The force that pulls nodes to the center. Defaults to `0.3` for local and `0.2` for global. * `linkDistance`: The distance between linked nodes. Defaults to `30`. * `fontSize`: The font size of node labels. Defaults to `0.6`. * `opacityScale`: The scale of node opacity. Defaults to `1`. * `removeTags`: Tags to exclude from the graph. Defaults to `[]`. * `showTags`: Whether to show tags in the graph. Defaults to `true`. * `enableRadial`: Whether to enable radial layout. Defaults to `false` for local and `true` for global. * `focusOnHover`: Whether to focus on the hovered node. Defaults to `false` for local and `true` for global. ### Default options[](https://quartz.jzhao.xyz/plugins/graph#default-options) quartz.config.yaml - source: github:quartz-community/graph enabled: true options: localGraph: drag: true zoom: true depth: 1 scale: 1.1 repelForce: 0.5 centerForce: 0.3 linkDistance: 30 fontSize: 0.6 opacityScale: 1 removeTags: [] showTags: true focusOnHover: false enableRadial: false globalGraph: drag: true zoom: true depth: -1 scale: 0.9 repelForce: 0.5 centerForce: 0.3 linkDistance: 30 fontSize: 0.6 opacityScale: 1 removeTags: [] showTags: true focusOnHover: true enableRadial: true API[](https://quartz.jzhao.xyz/plugins/graph#api) -------------------------------------------------- * Category: Component * Function name: `ExternalPlugin.Graph()`. * Source: [`quartz-community/graph`](https://github.com/quartz-community/graph) * Install: `npx quartz plugin add github:quartz-community/graph` * * * --- # CreatedModifiedDate CreatedModifiedDate =================== Properties2 | | | | --- | --- | | description | Determines creation and modification dates from frontmatter, git, or filesystem. | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer) | May 24, 20261 min read This plugin determines the created, modified, and published dates for a document using three potential data sources: frontmatter metadata, Git history, and the filesystem. See [authoring content > Syntax](https://quartz.jzhao.xyz/authoring-content#syntax) for more information. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `priority`: The data sources to consult for date information. Highest priority first. Possible values are `"frontmatter"`, `"git"`, and `"filesystem"`. Defaults to `["frontmatter", "git", "filesystem"]`. * `defaultDateType`: Which date type to use when displaying dates. Can be `"created"`, `"modified"`, or `"published"`. Defaults to `"modified"`. When loading the frontmatter, the value of [Frontmatter > List](https://quartz.jzhao.xyz/plugins/frontmatter#list) is used. > Warning > > If you rely on `git` for dates, make sure `defaultDateType` is set to `modified` in the plugin’s options. > > Depending on how you [host](https://quartz.jzhao.xyz/hosting) > your Quartz, the `filesystem` dates of your local files may not match the final dates. In these cases, it may be better to use `git` or `frontmatter` to guarantee correct dates. API[](https://quartz.jzhao.xyz/plugins/createdmodifieddate#api) ---------------------------------------------------------------- * Category: Transformer * Function name: `ExternalPlugin.CreatedModifiedDate()`. * Source: [`quartz-community/created-modified-date`](https://github.com/quartz-community/created-modified-date) * Install: `npx quartz plugin add github:quartz-community/created-modified-date` * * * --- # FolderPage FolderPage ========== Properties2 | | | | --- | --- | | description | Generates listing pages for folders. | | tags | [plugin/pagetype](https://quartz.jzhao.xyz/tags/plugin/pagetype) | May 24, 20261 min read This plugin is a page type plugin that generates index pages for folders, creating a listing page for each folder that contains multiple content files. It uses the `default` [page frame](https://quartz.jzhao.xyz/layout#page-frames) (three-column layout with sidebars). See [folder and tag listings](https://quartz.jzhao.xyz/features/folder-and-tag-listings) for more information. Example: [Advanced](https://quartz.jzhao.xyz/advanced/) > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `showFolderCount`: Whether to display the number of pages in the folder. Defaults to `true`. * `showSubfolders`: Whether to include pages from subfolders in the listing. Defaults to `true`. * `sort`: A function of type `(f1: QuartzPluginData, f2: QuartzPluginData) => number` used to sort entries. Defaults to sorting by date and tie-breaking on lexographical order. Requires a TS override. * `prefixFolders`: If `true`, generated folder page titles are prefixed with “Folder: ” (e.g. “Folder: notes”). Defaults to `false`. API[](https://quartz.jzhao.xyz/plugins/folderpage#api) ------------------------------------------------------- * Category: Page Type * Function name: `ExternalPlugin.FolderPage()`. * Source: [`quartz-community/folder-page`](https://github.com/quartz-community/folder-page) * Install: `npx quartz plugin add github:quartz-community/folder-page` * * * --- # Static Static ====== Properties1 | | | | --- | --- | | tags | [plugin/emitter](https://quartz.jzhao.xyz/tags/plugin/emitter) | Mar 19, 20261 min read This plugin emits all static resources needed by Quartz. This is used, for example, for fonts and images that need a stable position, such as banners and icons. The plugin respects the `ignorePatterns` in the global [configuration](https://quartz.jzhao.xyz/configuration) . > Important > > This is different from [Assets](https://quartz.jzhao.xyz/plugins/assets) > . The resources from the [Static](https://quartz.jzhao.xyz/plugins/static) > plugin are located under `quartz/static`, whereas [Assets](https://quartz.jzhao.xyz/plugins/assets) > renders all static resources under `content` and is used for images, videos, audio, etc. that are directly referenced by your markdown content. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin has no configuration options. API[](https://quartz.jzhao.xyz/plugins/static#api) --------------------------------------------------- * Category: Emitter * Function name: `Plugin.Static()` (internal plugin). * Source: [`quartz/plugins/emitters/static.ts`](https://github.com/jackyzha0/quartz/blob/v5/quartz/plugins/emitters/static.ts) . * * * --- # Latex Latex ===== Properties2 | | | | --- | --- | | description | Renders LaTeX math expressions via KaTeX or Typst. | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer) | May 24, 20261 min read This plugin adds LaTeX support to Quartz. See [Latex](https://quartz.jzhao.xyz/features/latex) for more information. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `renderEngine`: the engine to use to render LaTeX equations. Can be `"katex"` for [KaTeX](https://katex.org/) , `"mathjax"` for [MathJax](https://www.mathjax.org/) [SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html) , or `"typst"` for [Typst](https://typst.app/) (a new way to compose LaTeX equation). Defaults to KaTeX. * `customMacros`: custom macros for all LaTeX blocks. It takes the form of a key-value pair where the key is a new command name and the value is the expansion of the macro. For example: `{"\\R": "\\mathbb{R}"}` * `katexOptions`: Additional options passed to the KaTeX renderer. See the [KaTeX docs](https://katex.org/docs/options) for available options. * `mathJaxOptions`: Additional options passed to the MathJax renderer. See the [MathJax docs](https://docs.mathjax.org/en/latest/options/) for available options. * `typstOptions`: Additional options passed to the Typst renderer. API[](https://quartz.jzhao.xyz/plugins/latex#api) -------------------------------------------------- * Category: Transformer * Function name: `ExternalPlugin.Latex()`. * Source: [`quartz-community/latex`](https://github.com/quartz-community/latex) * Install: `npx quartz plugin add github:quartz-community/latex` * * * --- # TagPage TagPage ======= Properties2 | | | | --- | --- | | description | Generates listing pages for tags. | | tags | [plugin/pagetype](https://quartz.jzhao.xyz/tags/plugin/pagetype) | May 24, 20261 min read This plugin is a page type plugin that emits dedicated pages for each tag used in the content. It uses the `default` [page frame](https://quartz.jzhao.xyz/layout#page-frames) (three-column layout with sidebars). See [folder and tag listings](https://quartz.jzhao.xyz/features/folder-and-tag-listings) for more information. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `numPages`: The maximum number of pages to display per tag before showing a “see more” link. Defaults to `10`. * `sort`: A function of type `(f1: QuartzPluginData, f2: QuartzPluginData) => number` used to sort entries. Defaults to sorting by date and tie-breaking on lexographical order. Requires a TS override. * `prefixTags`: If `true`, generated tag page titles are prefixed with “Tag: ” (e.g. “Tag: recipes”). Defaults to `false`. API[](https://quartz.jzhao.xyz/plugins/tagpage#api) ---------------------------------------------------- * Category: Page Type * Function name: `ExternalPlugin.TagPage()`. * Source: [`quartz-community/tag-page`](https://github.com/quartz-community/tag-page) * Install: `npx quartz plugin add github:quartz-community/tag-page` * * * --- # TableOfContents TableOfContents =============== Properties2 | | | | --- | --- | | description | Generates and renders a table of contents from headings. | | tags | [plugin/transformer](https://quartz.jzhao.xyz/tags/plugin/transformer)
, [plugin/component](https://quartz.jzhao.xyz/tags/plugin/component) | May 24, 20261 min read This plugin generates a table of contents (TOC) for Markdown documents. See [table of contents](https://quartz.jzhao.xyz/features/table-of-contents) for more information. > Note > > For information on how to add, remove or configure plugins, see the [Configuration](https://quartz.jzhao.xyz/configuration#plugins) > page. This plugin accepts the following configuration options: * `maxDepth`: Limits the depth of headings included in the TOC, ranging from `1` (top level headings only) to `6` (all heading levels). Default is `3`. * `minEntries`: The minimum number of heading entries required for the TOC to be displayed. Default is `1`. * `showByDefault`: If `true` (default), the TOC should be displayed by default. Can be overridden by frontmatter settings. * `collapseByDefault`: If `true`, the TOC will start in a collapsed state. Default is `false`. * `layout`: The visual layout of the TOC component. Can be `"modern"` or `"legacy"`. Default is `"modern"`. > Warning > > This plugin needs the `Plugin.TableOfContents` component in `quartz.config.yaml` to determine where to display the TOC. Without it, nothing will be displayed. They should always be added or removed together. API[](https://quartz.jzhao.xyz/plugins/tableofcontents#api) ------------------------------------------------------------ * Category: Transformer, Component * Function name: `ExternalPlugin.TableOfContentsTransformer()`. * Source: [`quartz-community/table-of-contents`](https://github.com/quartz-community/table-of-contents) * Install: `npx quartz plugin add github:quartz-community/table-of-contents` * * * --- # Upgrading Quartz Upgrading Quartz ================ Properties1 | | | | --- | --- | | aliases | upgrading | Jul 27, 20264 min read > Note > > This is specifically a guide for upgrading your Quartz to a more recent update. If you are coming from Quartz 4 or Quartz 3, check out the [migration guide](https://quartz.jzhao.xyz/getting-started/migrating) > for more info. To fetch the latest Quartz updates, simply run npx quartz upgrade As Quartz uses [git](https://git-scm.com/) under the hood for versioning, upgrading effectively ‘pulls’ in the updates from the official Quartz GitHub repository. Merge conflicts in `quartz.lock.json` are handled automatically — Quartz backs up your lockfile before pulling and restores it afterward. For other files with local changes that conflict with the updates, you may need to resolve these manually yourself (or, pull manually using `git pull origin upstream`). > Hint > > Quartz will try to cache your content before upgrading to try and prevent merge conflicts. If you get a conflict mid-merge, you can stop the merge and then run `npx quartz restore` to restore your content from the cache. If you have the [GitHub desktop app](https://desktop.github.com/) , this will automatically open to help you resolve the conflicts. Otherwise, you will need to resolve this in a text editor like VSCode. For more help on resolving conflicts manually, check out the [GitHub guide on resolving merge conflicts](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line#competing-line-change-merge-conflicts) . To update your installed plugins separately, use: npx quartz plugin install --latest See the [CLI reference for upgrade](https://quartz.jzhao.xyz/cli/upgrade) for more details on available flags. ### Layout System Changes[](https://quartz.jzhao.xyz/getting-started/upgrading#layout-system-changes) The `footer` layout slot is now an array of components, consistent with other layout slots like `header`, `left`, and `right`. Additionally, `header` and `footer` are now configurable layout positions — plugins can declare `layout: { position: header }` or `layout: { position: footer }` in their YAML config. **If you override layouts in `quartz.ts`**, update any `footer` assignments to use arrays: quartz.ts // Before export const layout = await loadQuartzLayout({ defaults: { footer: MyFooterComponent }, }) // After export const layout = await loadQuartzLayout({ defaults: { footer: [MyFooterComponent] }, }) **If you have a custom page frame**, update the `render` function to iterate the footer array: // Before render({ footer: Footer, ...rest }: PageFrameProps) { return