--- title: Creating Component Plugins --- > [!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: ```html

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 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 To create a new component plugin, you can use the official plugin template: ```shell git clone https://github.com/quartz-community/plugin-template.git my-component cd my-component npm install ``` ### 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. ```tsx title="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 All Quartz components accept the same set of props: ```tsx 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 In community plugins, styles are bundled with the plugin. You can define styles using the `.css` property on the component: ```tsx 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: ```tsx 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. ### 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: ```ts 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. It is best practice to track any event handlers via `window.addCleanup` to prevent memory leaks during SPA navigation. #### 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: ```tsx title="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 Once your component is published (e.g., to GitHub or npm), users can install it using the Quartz CLI: ```shell npx quartz plugin add github:your-username/my-component ``` Then, they can add it to their `quartz.config.yaml`: ```yaml title="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`: ```ts title="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 })], }, }, }) ``` ## 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]] 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.