Quartz:

  • static site generator
  • npx quartz build execution path:

Server

entrypoint:

  • command: npx quartz build
  • target: package.json bin./quartz/bootstrap-cli.mjs

execution:

  • Node executes bootstrap-cli.mjs via shebang

bootstrap-cli.mjs:

  • CLI:
    • parses arguments with yargs
    • handles plugin subcommand
  • bundling:
    • transpiles/bundles TypeScript → JavaScript with esbuild
    • imports .scss as plain text through esbuild-sass-plugin v2
    • bundles browser-targeted *.inline.ts scripts through a custom esbuild plugin
    • imports inline-script modules as plain text
  • --serve:
    • starts WebSocket server on port 3001 for hot-reload signals
    • starts HTTP file server on default port 8080
    • watches .ts, .tsx, .scss, package files
    • rebuilds through esbuild rebuild API
  • build-module handoff:
    • writes transpiled build module to .quartz-cache/transpiled-build.mjs
    • dynamically imports it with a random query string to bypass Node import cache
    • leak: ~350 kB per hot reload
    • invokes module with parsed CLI arguments and client-refresh callback

build.ts:

  • setup:
    • installs source-map support
    • cleans output directory
    • globs content/ files
    • respects .gitignore
  • Markdown parsing:
    • concurrency:
      • if content count > 128:
        • transpiles quartz/worker.ts
        • spawns workerpool
        • assigns 128-file batches to workers
      • else:
        • parses on main thread
    • parser:
    • steps:
      • read file into vfile
      • apply plugin text transformations
      • slugify file path; see paths
      • parse Markdown → mdast with remark-parse
      • apply plugin Markdown-to-Markdown transformations
      • convert mdast → hast with remark-rehype
      • apply plugin HTML-to-HTML transformations
  • filtering:
    • filters content through plugins
  • emitting:
    • gathers static resources
    • transforms hast → JSX with hast-util-to-jsx-runtime and Preact
    • renders HTML with preact-render-to-string; interactive hooks ignored
    • assembles layout, client scripts, transpiled styles in quartz/components/renderPage.tsx
    • minifies/transforms CSS with Lightning CSS
    • splits scripts:
      • beforeDOMLoaded: inserts into <head>
      • afterDOMLoaded: inserts into <body>
    • writes emitted files to disk
  • --serve:
    • watches .md content files with 250 ms debounce
    • updates parsed AST/plugin-data content map for changed/new slugs
    • reruns filters/emitters
    • triggers client refresh

Client

initial_load:

  • browser loads:
    • HTML
    • public/index.css
    • critical JavaScript: public/prescript.js in <head>

deferred_load:

  • browser loads non-critical JavaScript: public/postscript.js after body

lifecycle_events:

  • "nav":
    • dispatched synthetically on page load
    • if enableSPA option active: fires on client-side navigation to cycle event handlers/state
    • if SPA disabled: fires once after initial load
  • "render":
    • dispatched after in-place DOM updates, such as decryption
    • content-bound elements should listen for both "nav" and "render"

Community Package Layering

packages:

  • @quartz-community/types:
    • type definitions
    • interfaces
    • vfile DataMap augmentation
    • no runtime dependencies
  • @quartz-community/utils:
    • shared path, DOM, sorting, date, JSX utilities
    • depends on @quartz-community/types
  • @quartz-community/runtime:
    • browser events, navigation, storage, script-loading utilities
    • depends on @quartz-community/types and @quartz-community/utils

dependency_graph:

types (no deps)
  ↑
utils (depends on types)
  ↑
runtime (depends on types + utils)
  ↑
plugins (depend on any combination)

Plugin System

configuration:

  • source: pageTypes in quartz.config.yaml

community_plugins:

  • clone into .quartz/plugins/
  • export through .quartz/plugins/index.ts

Plugin Types

types:

  • transformers:
    • map over content
    • examples: frontmatter, syntax highlighting
  • filters:
    • filter content
    • examples: drafts, publication flags
  • emitters:
    • reduce over content
    • examples: RSS, sitemaps, redirects, OG images
  • page_types:
    • render specific page categories
    • examples: content notes, listings, 404
    • routed by PageTypeDispatcher
  • Bases_views:
  • composition:
    • plugins may span multiple types, such as transformer plus component provider

Plugin Resolution

npx quartz plugin add github:quartz-community/explorer:

  • clones plugin to .quartz/plugins/explorer/
  • builds with tsup via tsup.config.ts
  • re-exports through .quartz/plugins/index.ts
  • locks commit hash in quartz.lock.json

Plugin CLI Commands

commands:

  • npx quartz plugin add github:quartz-community/<name>: install plugin
  • npx quartz plugin install --latest: update all plugins
  • npx quartz plugin install --clean: restore plugins from quartz.lock.json
  • npx quartz plugin remove <name>: remove plugin

Plugin Structure

files:

  • src/index.ts: plugin entry point/export
  • tsup.config.ts: build config
  • package.json: dependencies on @quartz-community/types and @quartz-community/utils

details:

Page Frames

scope:

  • frames define inner HTML structure inside static outer shell:
    • <html>
    • <head>
    • <body>
    • #quartz-root

location:

  • quartz/components/frames/

files:

  • types.ts: defines PageFrame and PageFrameProps
  • DefaultFrame.tsx: three-column layout; left sidebar, center, right sidebar, footer
  • FullWidthFrame.tsx: single center column
  • MinimalFrame.tsx: content and footer only
  • registry.ts: FrameRegistry singleton
  • index.ts: built-in registry and resolveFrame()

Frame Registry

registry:

  • FrameRegistry stores plugin-registered frames
  • plugins declare frames under "quartz"."frames" in package.json
  • loader: quartz/plugins/loader/frameLoader.ts

Frame Resolution

rendering:

  • quartz/components/renderPage.tsx calls frame render()
  • PageTypeDispatcher (quartz/plugins/pageTypes/dispatcher.ts) resolves frames

resolution_order:

  • YAML config: layout.byPageType.<name>.template
  • plugin-registered frame in FrameRegistry
  • builtinFrames map
  • fallback: "default"

output:

  • active frame name writes to data-frame on .page

Plugin-Provided Frames

plugin_frames:

related: