Production-ready template for building, testing, and publishing Quartz community plugins. It mirrors
Quartz's native plugin patterns and uses a factory-function API similar to Astro integrations:
plugins are created by functions that return objects with name and lifecycle hooks.
- ✅ Quartz-compatible transformer/filter/emitter examples
- ✅ TypeScript-first with exported types for consumers
- ✅
tsupbundling + declaration output - ✅ Vitest testing setup with example tests
- ✅ Linting/formatting with ESLint + Prettier
- ✅ CI workflow for checks and npm publishing
- ✅ Demonstrates CSS/JS resource injection and remark/rehype usage
npm install
npm run buildInstall your plugin into a Quartz site and register it in quartz.config.ts:
import {
ExampleTransformer,
ExampleFilter,
ExampleEmitter,
} from "@quartz-community/plugin-template";
export default {
configuration: {
pageTitle: "My Garden",
},
plugins: {
transformers: [ExampleTransformer({ highlightToken: "==" })],
filters: [ExampleFilter({ allowDrafts: false })],
emitters: [ExampleEmitter({ manifestSlug: "plugin-manifest" })],
},
};Quartz plugins are factory functions that return an object with a name and hook implementations.
This mirrors Astro's integration pattern (a function returning an object of hooks), which makes
composition and configuration explicit and predictable.
import type { QuartzTransformerPlugin } from "@jackyzha0/quartz/plugins/types";
export const MyTransformer: QuartzTransformerPlugin<{ enabled: boolean }> = (opts) => {
return {
name: "MyTransformer",
markdownPlugins() {
return [];
},
};
};ExampleTransformer shows how to:
- apply a custom remark plugin
- run a rehype plugin
- inject CSS/JS resources
- perform a text transform hook
import { ExampleTransformer } from "@quartz-community/plugin-template";
ExampleTransformer({
highlightToken: "==",
headingClass: "example-plugin-heading",
enableGfm: true,
addHeadingSlugs: true,
});The transformer uses a custom remark plugin to convert ==highlight== into bold text and a rehype
plugin to attach a class to all headings. It also injects a small inline CSS/JS snippet.
ExampleFilter demonstrates frontmatter-driven filtering:
ExampleFilter({
allowDrafts: false,
excludeTags: ["private", "wip"],
excludePathPrefixes: ["_drafts/", "_private/"],
});ExampleEmitter emits a JSON manifest of all pages:
ExampleEmitter({
manifestSlug: "plugin-manifest",
includeFrontmatter: true,
metadata: { project: "My Garden" },
transformManifest: (json) => json.replace("My Garden", "Quartz"),
});| Option | Type | Default | Description |
|---|---|---|---|
highlightToken |
string |
"==" |
Token used to highlight text. |
headingClass |
string |
"example-plugin-heading" |
Class added to headings. |
enableGfm |
boolean |
true |
Enables remark-gfm. |
addHeadingSlugs |
boolean |
true |
Enables rehype-slug. |
| Option | Type | Default | Description |
|---|---|---|---|
allowDrafts |
boolean |
false |
Publish draft pages. |
excludeTags |
string[] |
["private"] |
Tags to exclude. |
excludePathPrefixes |
string[] |
["_drafts/", "_private/"] |
Path prefixes to exclude. |
| Option | Type | Default | Description |
|---|---|---|---|
manifestSlug |
string |
"plugin-manifest" |
Output filename (without extension). |
includeFrontmatter |
boolean |
true |
Include frontmatter in output. |
metadata |
Record<string, unknown> |
{ generator: "Quartz Plugin Template" } |
Extra metadata in manifest. |
transformManifest |
(json: string) => string |
undefined |
Custom transformer for emitted JSON. |
manifestScriptClass |
string |
undefined |
Optional CSS class if rendered into HTML. |
npm testnpm run build
npm run lint
npm run formatTags matching v* trigger the GitHub Actions publish workflow. Ensure NPM_TOKEN is set in the
repository secrets.
MIT