---
title: "Quartz Layout: quartz.layout.ts"
slug: "quartz-layout"
category: "tooling"
tags: ["tooling", "quartz", "layout", "components", "ssg", "typescript", "ui"]
status: "stable"
last_updated: 2026-05-14
summary: "How quartz.layout.ts maps slot names to component arrays, defines per-page-type overrides, and uses ConditionalRender to show or hide components."
related: ["[[tooling/quartz]]", "[[tooling/quartz-components]]", "[[tooling/quartz-config]]", "[[tooling/quartz-plugins]]", "[[tooling/quartz-debugging]]", "[[howto/deploy-quartz-site]]", "[[ops/github-pages]]"]
---

## Overview

`quartz.layout.ts` controls where components appear on every page. It exports a `PageLayout` object that maps eight named slots to arrays of Quartz components. Components within a slot render in order; empty arrays remove the slot entirely. Per-page-type overrides let content pages, folder pages, and tag pages differ without a custom template.

## Understand the eight slots before touching the file

The slot names map directly to HTML regions in the default template.

| Slot | Location |
|---|---|
| `head` | Inside `<head>`: meta tags, styles, scripts |
| `header` | Top bar visible on all pages |
| `beforeBody` | Between the header and the main content column |
| `left` | Left sidebar |
| `right` | Right sidebar |
| `pageBody` | Main content area (usually `Component.Content()` alone) |
| `afterBody` | Below the main content, above the footer |
| `footer` | Bottom of every page |

Do not move `Component.Content()` out of `pageBody`. Every other component is optional. See [[tooling/quartz-components]] for the built-in component catalog.

## Assign `sharedLayout` for elements that appear on every page

`sharedLayout` applies to all page types unless overridden. Put navigation, search, and the footer here.

```ts
export const sharedLayout: SharedLayout = {
  head: Component.Head(),
  header: [Component.PageTitle(), Component.Search(), Component.Darkmode()],
  afterBody: [],
  footer: Component.Footer({ links: { GitHub: "https://github.com/…" } }),
}
```

`Component.Head()` must be the sole element in `head`; it owns `<title>`, canonical tags, and Open Graph metadata. Adding a second head component breaks deduplication.

## Use `defaultContentPageLayout` for article pages

`defaultContentPageLayout` controls the most common page type: a single markdown file rendered as an article.

```ts
export const defaultContentPageLayout: PageLayout = {
  beforeBody: [
    Component.Breadcrumbs(),
    Component.ArticleTitle(),
    Component.ContentMeta(),
    Component.TagList(),
  ],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
    Component.Explorer(),
  ],
  right: [
    Component.Graph(),
    Component.DesktopOnly(Component.TableOfContents()),
    Component.Backlinks(),
  ],
}
```

`TableOfContents` belongs in the right sidebar on desktop; wrapping it in `Component.DesktopOnly()` hides it on narrow viewports where it would overlap content.

## Use `defaultListPageLayout` for index pages

Folder pages and tag pages use `defaultListPageLayout`. They rarely need a table of contents or backlinks, so the right sidebar is simpler.

```ts
export const defaultListPageLayout: PageLayout = {
  beforeBody: [Component.Breadcrumbs(), Component.ArticleTitle(), Component.ContentMeta()],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
    Component.Explorer(),
  ],
  right: [],
}
```

Leaving `right` as an empty array collapses the right column. Quartz uses a CSS grid that reflows to two columns when the right array is empty, giving list pages a wider body.

## Wrap responsive components with `MobileOnly` and `DesktopOnly`

Quartz ships two helper components that toggle visibility via CSS class injection.

```ts
Component.MobileOnly(Component.Spacer())   // renders only on viewports < 768 px
Component.DesktopOnly(Component.TableOfContents())  // hidden on mobile
```

These are thin wrappers, not media-query logic in the component itself. The child component still mounts; the CSS class controls visibility. Avoid wrapping expensive components in `MobileOnly` expecting a performance benefit; the work still runs server-side.

## Use `ConditionalRender` to show a component only on specific slugs

`ConditionalRender` takes a component and a predicate function that receives the current page's `FileData`. Use it when a component belongs on some content pages but not others.

```ts
Component.ConditionalRender({
  component: Component.Graph(),
  condition: (page) => !page.slug?.startsWith("cheatsheets/"),
})
```

The predicate runs at build time per page, so there is no runtime cost. Use it sparingly; if more than three conditionals accumulate, consider a dedicated page type instead. See [[tooling/quartz-components]] for writing custom components that implement the same logic internally.

## Keep layout changes in sync with the theme

The layout file consumes the font and color values from `quartz.config.ts`. Changing `theme.colors` takes effect globally without touching the layout. Custom per-slot styles go in component `.css` properties, not as inline overrides in the layout file. See [[tooling/quartz-config]] for the `theme` object shape.

## Related

- [[tooling/quartz]]
- [[tooling/quartz-components]]
- [[tooling/quartz-config]]
- [[tooling/quartz-plugins]]
- [[tooling/quartz-debugging]]
- [[howto/deploy-quartz-site]]
- [[ops/github-pages]]
