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 to publish personal notes, websites, and digital gardens to the web.
🪴 Get Started
Quartz requires at least Node v22 and npm v10.9.2 to function correctly. Ensure you have this installed on your machine before continuing.
Then, in your terminal of choice, enter the following commands line by line:
Fully-customizable parsing, filtering, and page generation through plugins
For a comprehensive list of features, visit the features page. You can read more about the why behind these features on the philosophy page and a technical overview on the architecture page.
🚧 Troubleshooting + Updating
Having trouble with Quartz? Try searching for your issue using the search feature. If you haven’t already, upgrade 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 if you feel you found a bug or ask for help in our Discord Community.
Full-text search in Quartz is powered by Flexsearch. It’s fast enough to return search results in under 10ms for Quartzs as large as half a million words.
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.
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
Removing search: delete all usages of Component.Search() from quartz.layout.ts.
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.
An Example Component
Constructor
Component files are written in .tsx files that live in the quartz/components folder. These are re-exported in quartz/components/index.ts so you can use them in layouts and other components more easily.
Each component file should have a default export that satisfies the QuartzComponentConstructor function signature. It’s a function that takes in a single optional parameter opts and returns a Quartz Component. The type of the parameters opts is defined by the interface Options which you as the component creator also decide.
In your component, you can use the values from the configuration option to change the rendering behaviour inside of your component. For example, the component in the code snippet below will not render if the favouriteNumber option is below 0.
The Quartz component itself (lines 11-17 highlighted above) looks like a React component. It takes in properties (sometimes called props) and returns JSX.
All Quartz components accept the same set of props:
quartz/components/types.ts
// simplified for sake of demonstrationexport type QuartzComponentProps = { fileData: QuartzPluginData cfg: GlobalConfiguration tree: Node<QuartzPluginData> 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.ts.
tree: the resulting HTML AST after processing and transforming the file. This is useful if you’d like to render the content using hast-util-to-jsx-runtime (you can find an example of this in quartz/components/pages/Content.tsx).
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. Helpful if you want to conditionally hide a component on mobile or desktop.
Styling
Quartz components can also define a .css property on the actual function component which will get picked up by Quartz. This is expected to be a CSS string which can either be inlined or imported from a .scss file.
Note that inlined styles must be plain vanilla CSS:
// assuming your stylesheet is in quartz/components/styles/YourComponent.scssimport styles from "./styles/YourComponent.scss"export default (() => { function YourComponent() { return <p>Example Component</p> } YourComponent.css = styles return YourComponent}) satisfies QuartzComponentConstructor
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.
Scripts and Interactivity
What about interactivity? Suppose you want to add an-click handler for example. Like the .css property on the component, you can also declare .beforeDOMLoaded and .afterDOMLoaded properties that are strings that contain the script.
quartz/components/YourComponent.tsx
export default (() => { function YourComponent() { return <button id="btn">Click me</button> } YourComponent.beforeDOMLoaded = ` console.log("hello from before the page loads!") ` YourComponent.afterDOMLoaded = ` document.getElementById('btn').onclick = () => { alert('button clicked!') } ` return YourComponent}) satisfies QuartzComponentConstructor
Hint
For those coming from React, Quartz components are different from React components in that it only uses JSX for templating and layout. Hooks like useEffect, useState, etc. are not rendered and other properties that accept functions like onClick handlers will not work. Instead, do it using a regular JS script that modifies the DOM element directly.
As the names suggest, the .beforeDOMLoaded scripts are executed before the page is done loading so it doesn’t have access to any elements on the page. This is mostly used to prefetch any critical data.
The .afterDOMLoaded script executes once the page has been completely loaded. This is a good place to setup anything that should last for the duration of a site visit (e.g. getting something saved from local storage).
If you need to create an afterDOMLoaded script that depends on page specific elements that may change when navigating to a new page, you can listen for the "nav" event that gets fired whenever a page loads (which may happen on navigation if SPA Routing is enabled).
document.addEventListener("nav", () => { // do page specific logic here // e.g. attach event listeners const toggleSwitch = document.querySelector("#switch") as HTMLInputElement toggleSwitch.addEventListener("change", switchTheme) window.addCleanup(() => toggleSwitch.removeEventListener("change", switchTheme))})
You can also add the equivalent of a beforeunload event for SPA Routing via the prenav event.
document.addEventListener("prenav", () => { // executed after an SPA navigation is triggered but // before the page is replaced // one usage pattern is to store things in sessionStorage // in the prenav and then conditionally load then in the consequent // nav})
It is best practice to track any event handlers via window.addCleanup to prevent memory leaks.
This will get called on page navigation.
Importing Code
Of course, it isn’t always practical (nor desired!) to write your code as a string literal in the component.
Quartz supports importing component code through .inline.ts files.
quartz/components/YourComponent.tsx
// @ts-ignore: typescript doesn't know about our inline bundling system// so we need to silence the errorimport script from "./scripts/graph.inline"export default (() => { function YourComponent() { return <button id="btn">Click me</button> } YourComponent.afterDOMLoaded = script return YourComponent}) satisfies QuartzComponentConstructor
quartz/components/scripts/graph.inline.ts
// any imports here are bundled for the browserimport * as d3 from "d3"document.getElementById("btn").onclick = () => { alert("button clicked!")}
Additionally, like what is shown in the example above, you can import packages in .inline.ts files. This will be bundled by Quartz and included in the actual script.
Using a Component
After creating your custom component, re-export it in quartz/components/index.ts:
quartz/components/index.ts
import ArticleTitle from "./ArticleTitle"import Content from "./pages/Content"import Darkmode from "./Darkmode"import YourComponent from "./YourComponent"export { ArticleTitle, Content, Darkmode, YourComponent }
Then, you can use it like any other component in quartz.layout.ts via Component.YourComponent(). See the layout section for more details.
As Quartz components are just functions that return React components, you can compositionally use them in other Quartz components.
Look in quartz/components for more examples of components in Quartz as reference for your own components!
SPA Routing
1分钟阅读
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. 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
Disable SPA Routing: set the enableSPA field of the configuration in quartz.config.ts to be false.