---
title: "How to run a Quartz static site locally"
slug: "run-a-quartz-static-site-locally"
category: "howto"
tags: ["howto", "quartz", "local-dev", "tutorial", "tooling"]
status: "stable"
last_updated: 2026-05-14
summary: "Clone the repo, install Node 22, run npx quartz build --serve, and fix the port, theme, and plugin-order errors that bite new users."
related: ["[[tooling/quartz]]", "[[tooling/obsidian]]", "[[howto/deploy-quartz-site]]", "[[ops/github-pages]]", "[[howto/ship-llms-txt]]"]
---

## Overview

Quartz 4 builds to `public/` and serves a hot-reload preview on port 8080. This guide takes a fresh checkout to a running local site in under five minutes. It covers the install path, the dev server, and the four errors most new users hit. The umbrella rules and `quartz.config.ts` patterns live in [[tooling/quartz]].

## Prerequisites

- Node 22. Older Node versions throw cryptic ESM errors at build. Check with `node -v`.
- `npm` 10 or newer. Comes with Node 22.
- Git, to clone the repo.
- 200 MB of free disk for `node_modules`.

## Steps

### 1. Clone the repo

```bash
git clone https://github.com/<you>/<your-quartz-repo>.git
cd <your-quartz-repo>
```

If you are starting from upstream Quartz, clone `https://github.com/jackyzha0/quartz.git` and rename the directory to your site name.

### 2. Pin Node 22

Node version drift causes silent build differences. Use `nvm` or a similar manager to pin Node 22 per-project.

```bash
nvm install 22
nvm use 22
node -v   # expected: v22.x.x
```

If the repo has a `.nvmrc`, `nvm use` picks the right version automatically.

### 3. Install dependencies

```bash
npm ci
```

Use `npm ci`, not `npm install`. `ci` honors `package-lock.json` exactly; `install` may update transitive deps and break the build.

### 4. Run the dev server

```bash
npx quartz build --serve
```

The first build takes 20 to 60 seconds depending on vault size. The server then listens on `http://localhost:8080` and rebuilds on file changes. Open the URL in a browser; the master MOC at `content/index.md` is the landing page.

### 5. Edit and watch the rebuild

Edit any file under `content/`. The terminal shows `Detected change` and rebuilds. The browser auto-reloads via the dev server's websocket.

If the rebuild fails, the terminal prints the offending file and line. Fix and save; the next save triggers a clean rebuild.

### 6. Build for production

Before opening a PR, run a clean production build to catch errors the dev server forgives.

```bash
rm -rf public .quartz-cache
npx quartz build
```

If this exits 0, the build is shippable. See [[howto/deploy-quartz-site]] for the GitHub Pages deploy.

## Verify it worked

Three checks.

```bash
# 1. Dev server returns 200 on the homepage.
curl -sI http://localhost:8080/ | head -1
# expected: HTTP/1.1 200 OK

# 2. The build produced public/index.html.
ls public/index.html
# expected: file exists

# 3. A known page renders.
curl -s http://localhost:8080/ | grep -o '<title>[^<]*</title>'
# expected: the configured pageTitle
```

## Common errors

- **`EADDRINUSE: address already in use :::8080`**. Another dev server is running. Kill it with `lsof -i :8080` then `kill <pid>`, or pass a different port: `npx quartz build --serve --port 8081`.
- **`Cannot find module '@myriaddreamin/rehype-typst'`** or similar. Dependencies are stale. Delete `node_modules` and `.quartz-cache`, then `npm ci`.
- **Build hangs at "Parsing files"**. Quartz is processing a corrupted markdown file. Run `npx quartz build --verbose` to find the file, then check for unclosed code fences or invalid frontmatter.
- **Theme renders broken**. The `theme` block in `quartz.config.ts` references a font or color that does not exist. Compare against the default config in the upstream Quartz repo; restore the missing field.
- **Plugins emit in the wrong order**. `Plugin.ContentIndex` must run before `Plugin.Static`, or the sitemap is empty. Keep the emitter list in the order from [[tooling/quartz]]; do not alphabetize it.
- **`fatal: not a git repository`** when Quartz computes dates. The vault must be a git repo for `CreatedModifiedDate({ priority: ["git"] })`. Either `git init` the project, or set the priority to `["frontmatter"]` only.
- **Wikilinks render as plain text**. The link target does not exist or the slug is wrong. Quartz only resolves wikilinks to existing pages; create the target page or fix the slug.

## Related

- [[tooling/quartz]]
- [[tooling/obsidian]]
- [[howto/deploy-quartz-site]]
- [[ops/github-pages]]
- [[howto/ship-llms-txt]]
- [[howto/write-llm-friendly-content]]
