Minor Changes
-
eab5c53: A page type's
generatecan be async: it may return a promise of its virtual pages, which Quartz awaits in full and incremental builds. Page types that create pages from other files (like.mdx) can now run content through the site's transformers or bundle with esbuild; previously a promise crashed the build with "virtualPages is not iterable" (#2575). -
f768b1e: Clearer failures: errors thrown by a plugin now name the plugin (package, version, source), what it was doing (transforming, filtering, emitting, rendering a component) and the file or page, list problems Quartz already knew about that plugin (incompatible Quartz version, missing required data), and say how to check and where to report it. Plugin stack frames are no longer hidden. Warnings from loading the config are repeated in a summary at the end of the build, together with a note about links to pages that are not on the site (including links to drafts).
-
f768b1e: Frontmatter parsing, link resolution, draft handling and the content index are now built into Quartz (configured under
configuration.frontmatter,.links,.draftsand.contentIndex) instead of being plugins that silently broke the site when disabled. Existingcrawl-links,remove-draftandcontent-indexentries keep working and are applied automatically;npx quartz plugin doctor --fixmoves them intoconfiguration. Plugins can add fields tocontentIndex.jsonwith anextendContentIndexhook, and emitters can reuse the built index viactx.contentIndex. The effective built-in settings are available to plugins asctx.cfg.configuration.<key>. -
e4ebd28: Markdown renders as it does in Obsidian: Quartz now uses ObsidianFlavoredMarkdown 2.0 and GitHubFlavoredMarkdown 2.0.
- Line breaks changed: a single line break inside a paragraph now renders as
<br>, as in Obsidian's default reading view. SetstrictLineBreaks: truein the ObsidianFlavoredMarkdown options to keep the previous behaviour (a space). - ObsidianFlavoredMarkdown parses with Obsidian's own parser by default (
preset: "legacy"). Usepreset: "strict"for TOML (+++) frontmatter or plugins that add their own Markdown syntax. - Also new: callouts parsed like Obsidian, inline footnotes (
text^[note]), image sizes on Markdown images (), Mermaid diagrams that follow the theme's colours, an opt-in Pele diagram renderer, and inline tags that only link to tag pages that exist. - GitHubFlavoredMarkdown now runs before ObsidianFlavoredMarkdown (order 30, then 40), as in the default config. If your config sets
orderfor either plugin, keep GitHubFlavoredMarkdown first.
See the plugins' changelogs for details.
- Line breaks changed: a single line break inside a paragraph now renders as
-
15b8eef: Permalinks work as in Obsidian Publish (#2572):
permalink: pagemakes the note's address/page, whatever its file path. Links to the note, backlinks, the graph, the sitemap, RSS and the content index use the permalink, links inside the note still resolve relative to its file, and the note's file path redirects to the permalink (with AliasRedirects, likealiases). Previously the permalink was only a redirect to the file path. A permalink that is already another page's address is ignored with a warning. In navigation (the content trie used by the explorer, folder pages and breadcrumbs) the note stays in its folder, as on Obsidian Publish sites. -
c84f348: Plugins can ship files with the site:
externalResources()may returnassets: [{ from, to }](a file or directory on disk), which Quartz copies tostatic/plugins/<to>. Client scripts find them through the newwindow.quartzStaticUrlpage global (absolute URL ofstatic/, valid across SPA navigations). Use it for large or optional client code loaded withimport()only where needed, instead of inline scripts, which every page downloads. Invalid or conflicting targets and missing sources are build warnings. -
f768b1e: Plugins can declare what they provide and need (
quartz.capabilities); the build warns when a required provider is missing (e.g. "backlinks requires datalinks… enable configuration.links"). Rendered plugin components carrydata-quartz-plugin/data-quartz-componentattributes, so you can tell which plugin rendered which part of a page.fetchDataresolves to{}instead of rejecting whencontentIndex.jsonis missing. -
f768b1e: Plugin management: plugin commands exit non-zero on failure, CLI edits keep comments in
quartz.config.yaml, lockfile pins are honoured everywhere, npm plugins show up inplugin list,plugin adddetects duplicates, misspelled plugin options are flagged, and the newplugin doctorchecks config, lockfile and installs.plugin config --set a.b=1now writes a nested key. -
3c18be8: Plugins can be marked deprecated with
quartz.deprecated(a message naming the replacement) in theirpackage.json. Quartz warns while a deprecated plugin is enabled — when the build loads the config (repeated in the end-of-build summary), inplugin addand inplugin doctor— and suggests removing it. The deprecatedhard-line-breaksplugin is no longer listed in the default config and templates: ObsidianFlavoredMarkdown 2.0+ handles line breaks (strictLineBreaks). -
50203da: Plugins that fail to load (can't be installed, throw on import, export no plugin) are reported as errors with the plugin, version, the file and line where it failed and what to do, and listed again at the top of the end-of-build summary. Previously some of these failures were silent. Locally the build continues without them; in CI (
CIset) it fails unlessQUARTZ_ALLOW_BROKEN_PLUGINS=1. Fixed along the way: the entry module of npm-installed component plugins (and npm plugins without a declared category) was never imported, so theirinit(options)never ran. -
01bc5c5: Updated default plugins (see each plugin's changelog for details):
- Changed heading ids: with SmartyPants on (the default), a heading containing
--now gets the same id as on GitHub:## Range 1--2→range-1--2(wasrange-12). Links that hard-code the old id need updating;[[page#Range 1--2]]keeps working. Links Obsidian writes to headings containing:,|,^,#or\now reach them, and heading ids, links and table-of-contents entries share one slug function (GitHubFlavoredMarkdown 2.1.0, TableOfContents, CrawlLinks, utils 1.1). - Bases now behave as in Obsidian 1.15 (BasesPage 1.2.0, checked against real Obsidian): subtracting dates gives a duration (
(due - today()).days); nested tags match (file.tags.contains("#project")finds#project/x); dates display in the site's locale, anddate()reads only ISO-style dates; cards use Obsidian's aspect ratio and layout;link()with any URL is an external link; a base opens on its first view; tags link to their tag pages;this.<property>in an embedded base reads the embedding note. Base views also list canvas and Excalidraw files with their real paths,thisin an embedded base is that note, and Quartz's generated pages (404, tags, folder listings) no longer appear as notes. Changed:file.tagskeeps#and the original case, thetagsproperty no longer includes inline tags,hasTag(a, b)matches any of its tags, the root folder is/, textcontainsignores case, and the cards'imageAspectRatiois height/width (it was inverted).file.nameis as in Obsidian: a Markdown note's name has no extension (Note), other files keep theirs (Plan.canvas);file.fullnamehas the extension for every file. - Folder pages list folders that hold only canvas, base or other generated pages (FolderPage).
- A footnote referenced only from a table cell past the table's columns is rendered, as in Obsidian (ObsidianFlavoredMarkdown 2.1.1, with remark-obsidian 2.2.0: faster parsing and fixes for deeply nested notes and
\r\nline endings). - A short wikilink to an Obsidian folder note (
[[Topic]]forTopic/Topic.md) reaches the folder page (utils 1.1). - NoteProperties no longer bundles gray-matter's
eval-based JavaScript frontmatter engine.
- Changed heading ids: with SmartyPants on (the default), a heading containing
-
f768b1e: Plugin version requirements are enforced: a plugin's
quartzVersion(package.json→quartz.quartzVersion, any semver range) is checked when the config loads (a warning locally; in CI —CIset — the build fails unlessQUARTZ_ALLOW_INCOMPATIBLE_PLUGINS=1), byplugin addand byplugin doctor, with a message saying whether to update Quartz or the plugin. Previously the field was never checked (and the old checker read">=5.0.0"as0.0.0). -
6210f28: Prefetching: Quartz fetches pages before their links are followed, so they open at once. New
configuration.prefetch:viewport(default): links on screen are fetched while the browser is idle, and links pointed at, focused or touched first.hover: only links pointed at, focused or touched."off".
It works with and without SPA routing. With SPA routing, fetched pages are kept in memory (five minutes, at most 40) and used by both page navigation and popover previews, which no longer download a page the router fetches again. Without it, Quartz asks the browser to prefetch (Speculation Rules, or
<link rel="prefetch">). Nothing is prefetched from links to other sites, files, or pages the site doesn't have, or with Data Saver on or a 2G connection (except links pointed at). Adddata-no-prefetchto a link to exclude it. See https://quartz.jzhao.xyz/features/prefetching -
3103485: New
configuration.publishAssets: "referenced"(default"all"): only copy the images, PDFs and other files that published pages link to or embed, so files that only unpublished notes use stay private (#2531, from #2544). References are matched against published (slugified) file names and include<object data>SVG embeds,srcsetand URL-encoded paths. -
f768b1e: New
npx quartz infocommand: prints the Quartz version (withgit describe, branch and local changes), Node/npm/OS, the config file, built-ins and every plugin with its version, source and compatibility, as a Markdown block to paste into bug reports. The build banner andplugin doctoralso show where the checkout is relative to the last release. -
08c2857: New
layout.sidebarsoption: how each sidebar handles content taller than the screen.fit(default, unchanged) keeps it one screen tall and components shrink and scroll themselves;scrollkeeps it in view and lets it scroll itself, so nothing is ever out of reach;staticlets it scroll away with the page. Page types can override it (byPageType.<type>.sidebars). Newdocstemplate for documentation sites (npx quartz create --template docs), with both sidebarsstatic. Frames receive the modes as asidebarsprop and put them on sidebar elements asdata-sidebar-mode, which plugins can target in CSS. -
d66968c:
npx quartz upgradeshows the release notes of every version between the old and the new Quartz, then runs the new version's checks: it restores plugins, runsplugin doctor(plugin entries that are built in now, plugins that don't support the new version …) and lists plugin updates. Plugin install/restore skip plugins that are built into Quartz now, andplugin doctor --fixalso removes their unused lockfile pins. -
fe6a3ea: Pages that page types render from files (
.base,.canvas, Excalidraw drawings) follow the site's publish filters (quartz-community/bases-page#11). The filters see each such page with the frontmatter its page type gives it andfile.data.referencedBy, the published pages that link to it; a page a filter rejects is not emitted and not listed in the content index, sitemap, RSS, folder pages, explorer or search, and can't be embedded. Before, every such file was published regardless of the filters. New transformer hookvirtualPageHtmlPlugins(ctx, content): rehype plugins run on these pages' rendered bodies, which never pass throughhtmlPlugins, so plugins such as encrypted-pages can encrypt them.
Patch Changes
-
ac8cc4b: A plugin that lists a plugin now built into Quartz in
quartz.dependencies(@quartz-community/crawl-links,remove-draftorcontent-index) no longer fails the build with "requires crawl-links. Run: npx quartz plugin add …". The built-in (configuration.links,drafts,contentIndex) satisfies the dependency. A warning is shown when that built-in is disabled.npx quartz plugin doctorchecks the same way. -
f8b5638: More build errors caused by content are reported as content problems ("fix the file and build again") instead of "This is likely a Quartz bug": frontmatter that holds a single value or a list instead of properties, Markdown files and assets Quartz can't read (permission denied, a directory named like a note). Plugins can do the same by throwing an
ErrorwithquartzContent = true(see "Reporting problems in content" in Making plugins). -
e8a192e: Plugins can declare
window.quartzStaticUrlandwindow.quartzPrefetchincapabilities.requires.globalswithout a false "nothing provides it" warning: Quartz counts both as its own (quartzPrefetchunlessprefetchis off). -
85d2b43: A plugin whose dependency is configured but disabled gets the intended warning ("depends on X which is disabled") instead of failing the build with "requires X. Run: npx quartz plugin add X".
-
28fcb64: When a build stops on an error, Quartz stops esbuild before exiting. esbuild runs as a child process that writes to the same terminal; left running, it could print "fatal error: all goroutines are asleep - deadlock!" and a long goroutine dump after Quartz's error message.
-
913f9f0: The folder hierarchy used by folder pages and breadcrumbs now includes the pages of page types that render files (
.base,.canvas, …): folder pages list them, and a folder that holds only such pages has its own node (#2576). Documented that while a page type'sgenerateruns,ctx.virtualPagesholds the virtual pages of the page types before it. -
5507de3: French: "Vue graphique" and "Table des matières" use French capitalisation (#2513).
-
2ef60c6: Embedding a section by the anchor Obsidian writes for its heading works when that anchor differs from the heading's id:
![[note#Colon unspaced]]now embeds## Colon:unspaced. It needs GitHubFlavoredMarkdown 2.1.0 or later, which marks those anchors on the heading. -
f768b1e: Layout fixes: the
has-backlinkscondition works,byPageType.excludematches npm plugin sources, layout overrides inquartz.tsare applied,position: footeris accepted by the config schema, and typos in positions, display values, frames or page types produce a "did you mean" warning instead of silently dropping the component.quartz build --verboseprints a layout report. -
fc86552: Links as in Obsidian 1.15.0:
- A Markdown link whose path is not valid percent-encoding (
[x](%zz),[x](a%E2.md)) is an external link. Before, it failed the whole build withURI malformed. - Link paths are NFC-normalized, so a link spelled with a decomposed
éreachescafé.md. - An image of a file the site doesn't have, with no alt text, gets its percent-decoded path as alt (
→ “missing pic.png”), as Obsidian names a missing embed. - A wikilink's
^idno longer names its paragraph (Obsidian Flavored Markdown).
- A Markdown link whose path is not valid percent-encoding (
-
671ca49: Security updates:
sharp0.35.5 (GHSA-wq5f-xc86-pv6w),toml5 (prototype pollution and DoS advisories; TOML frontmatter parses the same, and now also accepts plain dates likedate = 2024-01-02) andminimatch11 (GHSA-g3hv-75wg-m249;ignorePatternsmatch the same). The plugin schema'slayout.positionlists exactly the positions Quartz renders.npx quartz tuipoints to the right install command (github:quartz-community/tui). -
b7998bb: Pages no longer scroll sideways on phones: long words (a page title such as "ObsidianFlavoredMarkdown", long inline code) break where they don't fit, and the right sidebar's components (graph, backlinks) wrap below each other when they don't fit side by side.
-
b2da325: Security: frontmatter can no longer run code during a build. A note starting with
---jsor---javascripthad its frontmatter evaluated as JavaScript (gray-matter's default engine), so building content you didn't write, such as a pull request preview, could run arbitrary code. Quartz now refuses JavaScript frontmatter with an error naming the file. Invalid frontmatter (for example broken YAML) is now reported as a problem in that file instead of as a Quartz bug. -
e41ebf6: Plugin option checks use the option schemas first-party plugins ship. These are declared as
quartz.optionSchema, the name Quartz Syncer reads, as a flat map of option to type ({ limit: { type: "number" }, mode: { type: "enum", values: [...] } }). Before, only a JSON Schema inquartz.configSchemawas read, so no first-party plugin's schema was used. A misspelled option, an enum value that isn't allowed, or a value of the wrong type is now a warning. A value of the same type as the plugin's default is always accepted, even where a schema disagrees. -
edb64cd: Tag pages honour the TagPage
numPagesandsortoptions (tag-page 1.0.4); before, they always used the defaults. Plugin client scripts can usewindow.quartzStaticUrlandwindow.quartzPrefetchwith the declarations in@quartz-community/types1.3.1. -
28581e8: On Windows, regenerating
.quartz/plugins/index.ts(npm run install-plugins, which also runs before everynpm run build) no longer skips npm plugins with "dist/index.d.ts could not be found", which left exports such asExternalPlugin.Explorerundefined. The package's location was read from afile:URL's pathname (/C:/…), which is not a Windows path. -
ba5a9a6: Option checks know the FolderPage
prefixFolders, TagPageprefixTagsand LatexcustomMacrosoptions (folder-page 1.0.4, tag-page 1.0.5, latex 1.0.2 declare them in their manifests). -
e44f2b0:
quartz.tsoverrides (ExternalPlugin.Explorer({...})) work for npm plugins and for component plugins (#2565): overrides are registered under the key the loader looks up (the package name for npm plugins), and component constructors re-exported from a plugin's sub-module are wrapped too. The wrapper also returns what the plugin returns, soExternalPlugin.X(...)works both as a statement and as a component in a layout override.quartz plugin …commands no longer drop npm plugins from the generated.quartz/plugins/index.ts; both generators are now one shared module. -
8854aeb: Plugin fixes for Windows: relative aliases (
./old,../x) redirect to the right page (alias-redirects 1.0.3), and git dates of notes in folders are read from git instead of the filesystem (created-modified-date 1.0.3). -
f687f58: Plugins use Quartz's own Preact instead of each bundling a copy, so components can use hooks and context across plugins (a component using
preact/hookswith another plugin's components crashed), and plugin bundles are smaller. The default plugins and the plugin template are updated; a third-party plugin made from the template before this release still bundles its own copy until it updates itstsup.config.ts(see quartz-community/plugin-template). -
2a4ab67: The Roam Research plugin works with ObsidianFlavoredMarkdown on, as in the default config (roam 1.1.0).
{{[[TODO]]}},{{[[DONE]]}},[[>]]and{{[[video]]: …}}no longer render as wikilinks, and__italic__is italic instead of bold. -
8eae6a1: Security: plugin commands no longer build shell commands from plugin sources, refs, pinned commits or package names (#2571). git and npm run with argument arrays, refs/commits/URLs/package names are validated (so a value can't be read as an option either), and generated plugin imports are written as escaped string literals. A source such as
github:owner/repo#main;cmdused to runcmd; it is now rejected as an invalid ref. Also fixes restoring a plugin pinned inquartz.lock.jsonto a commit that is no longer its branch tip (#2559). -
3e4a50a: SPA navigation follows redirects correctly: when a link is redirected (for example
folder→folder/on static hosts, or an alias page's canonical link), relative links on the new page resolve against where it really is, and the address bar shows that URL. Links on folder pages and parent tag pages reached through slash-less links pointed one level too high. -
7236249: Sites created with
npx quartz createfrom a template (default, blog, docs, obsidian, ttrpg) run GitHubFlavoredMarkdown before ObsidianFlavoredMarkdown (orders 30 and 40), as the default config does and 5.1 requires. The templates still had the old orders (40 and 30). The obsidian and ttrpg templates no longer setparseArrows, which ObsidianFlavoredMarkdown doesn't have. -
f5ef8f7: A local plugin source written with Windows separators (
.\my-plugin,..\plugins\my-plugin) is recognised as a local path. Before, it was read as a git or npm source and failed with "Expected github:owner/repo".Pretty links shorten link text the same way on every OS: Windows also cut it at a backslash (
[[a\b]]showedb).