Local Development

Local development is a short loop: build the site, look at it on localhost, edit a template, watch it rebuild. Everything on this page assumes the site is already set up on this machine — if it is not, start with leed site init, which the installer normally runs for you.

Where to run commands

Every leed site command finds its configuration by looking for leed.config.json in the current directory and then in its parent. That gives you exactly two places to stand:

~/leed/
└── example.com/                 ← the site folder: run commands here
    ├── leed.config.json
    ├── example.com.code-workspace
    └── raw-content/             ← or here: the git repository
        ├── src/                 ← everything you edit
        │   ├── _data/
        │   ├── _includes/
        │   ├── _layouts/
        │   ├── docs/
        │   └── static/
        ├── tailwind/
        └── .build/              ← build output, gitignored

Two levels, and no others. From ~/leed — one level too high — or from raw-content/src/ — one level too deep — the CLI stops with “Could not locate leed.config.json. Run this application from the directory where your repo exists.”

A plain build

leed site build

This renders the whole site: your templates, the published CMS content, the Tailwind stylesheet, the search index. Source comes from raw-content/src, intermediate output lands in raw-content/.build, and the finished site is written to raw-content/.build/site. The entire .build/ directory is gitignored — it is a product, never a source, and nothing in it is ever committed.

A plain build is also the build that counts before a commit. See the one trap below for why that sentence needs the word “plain”.

A page’s folder is its URL

The first time you open src/ you will find pages nested several folders deep and wonder whether that structure is safe to rearrange. It is not.

A page’s file lives at src/<page type>/<folder path>/<slug>.md, and the site generator turns exactly that path into the URL. src/docs/leed-cli/local-development.md becomes /docs/leed-cli/local-development/. Nothing in the file’s front matter sets its address; the path does.

You do not move a page by moving its file. The folder path is derived from the documentation menu — each folder item’s name, slugified — so a file you relocate by hand is moved back the next time that menu is published. Read the tree; do not restructure it. The full rule is in page paths and folder structure, and the menu side of it in folders set your URLs.

The preview loop

leed site build --serve

After the initial build finishes, the site is served at http://localhost:8080:

Initial build complete, starting webserver...
Serving /home/you/leed/example.com/raw-content/.build/site at http://localhost:8080

Edit any template, partial, page or Tailwind file and the affected pages rebuild incrementally — refresh the browser to see the change. Ctrl+C stops the server, and on the way out it removes the leed-* templates the build copies into src/ so your working tree is left as it was found.

--port <number> moves the server somewhere else:

leed site build --serve --port 4000

Which variant you are looking at

Your site has two published forms — a preview site and a live site — and a local build produces one of them, chosen by the git branch you are on. Anything other than main builds the preview variant.

leed site init leaves you on staging, and staging is the only branch you may commit from, so in ordinary use you are always previewing exactly what your preview site will render. You can see the decision with -v:

$ leed site build -v
Branch 'staging' detected — preview build

There are flags that force it either way, but they are hidden and meant for CI. The behavior is the part worth knowing: change branch and you change which variant you are looking at. What differs between the two once published is covered in preview site vs live site.

Faster iteration

Three flags trade completeness for speed while you are working.

FlagDefaultWhat it doesRecorded as your pre-commit build?
--serveoffBuilds, then serves and watchesYes — at the moment the initial build completes
--port <number>8080Port for the dev servern/a
-d, --debugoffSkips minification; output HTML and CSS stay readableNo
--skip-searchoffSkips building the search indexYes
--skip-aioffSkips the AI support pluginYes
--redirectsoffEnables the redirect pluginYes

-d, --debug is the one you will reach for most: skipping minification takes roughly 40% off the build on a typical site — measured at about 4.2 seconds against 7.4 on one real profile — and leaves output you can actually read in the browser’s element inspector.

Every flag, hidden ones included, is enumerated on leed site build.

The one trap

If commit keeps refusing with “You must run the following command before changes can committed: leed site build” and you are certain you built, check whether that build had -d on it. That is nearly always the answer.

Seeing more output

The CLI is quiet by default: only error channels are enabled, so most of its own commentary is suppressed.

leed site build -v            # everything the CLI logs
leed site build -n leed:site:build:info   # one channel

-v is exactly equivalent to -n leed:*. -n <filter> takes a debug-style namespace pattern, so you can narrow it to one command’s channels.

The site generator underneath has its own channels:

DEBUG=Eleventy* leed site build

Two behaviors here are worth knowing before they surprise you. An inherited DEBUG is merged with whatever -v or -n asked for, rather than replaced. And when no generator channel ends up enabled, the CLI deletes DEBUG from the environment after registering its own namespaces — a deliberate performance workaround, since the generator re-reads that variable to decide whether to run an expensive per-template check. So DEBUG alone does not always do what you expect; combine it with -v when you want both.

A worked session

cd ~/leed/example.com

leed site build --serve       # preview at http://localhost:8080, rebuilds as you edit
# ... edit templates and styles until the site looks right ...
# Ctrl+C to stop the server

leed site validate            # check every change is allowed
leed site build               # a plain build — no -d, this is the one commit checks
leed site commit -m "Refresh the footer layout"
leed site push                # push to staging and tell the CMS

The second half of that — validate, build, commit, push — has rules of its own and its own page: validate, commit and push. The files you are editing in between are described folder by folder in your site repository, and anything under tailwind/ recompiles through the pipeline described in Tailwind build.

When the build fails

A failed build prints a summary rather than the whole log. The CLI scans the output for failure markers — ✘, [ERROR], a leading Error:, Failed:, fatal: — and extracts those blocks with the indented lines that follow them, capped at 2000 characters. When it finds no marker at all it falls back to the last 2000 characters, on the reasoning that the cause is usually near the end.

That is worth knowing because the generator prints a great deal before it fails, and the extract is designed to skip past it to the part that matters. If the extract is not enough, re-run with -v.

Every message a failed command can produce, and the exit code beside it, is in CLI troubleshooting and exit codes. A build that fails after you push — on Cloudflare rather than on your machine — produces the same [site-build] extract, shown in the CMS and decoded in when a deployment fails.

The build and serve options named here are a subset; every command and every flag is indexed at CLI command reference.

ESC