posit-dev / posit-dev/great-docs

Quarto render time scales ~quadratically with API page count due to the generated per-object sidebar (19.5 min → 31 s for a 90-page site)

Open
#261 0 comments 0 reactions 1 assignee View on GitHub

@rich-iannone is already working on this.

Since Jul 15, 2026.

Difficulty: [2] Intermediate Effort: [2] Medium Priority: [3] High Type: ★ Enhancement
Dominant language
Python
Stars
262
Forks
18
Avg merge
1d 4h
Merged PRs (30d)
14

Description

Summary

For packages with non-trivial APIs, great-docs build spends nearly all of its time in the
Quarto render phase, and that time scales roughly quadratically with the number of API pages.
The cause is the generated _quarto.yml sidebar: one entry per documented object, as a bare
file path. Quarto re-resolves the full sidebar for every page it renders (apparently reading
each target file's metadata to get its title), so an API with N objects pays ~N² metadata reads.

On a real package (86 API objects, 90 pages total, zero executable code cells), the
measured difference is 19m 29s as shipped vs 30.8s with the sidebar removed — everything else
identical.

This is distinct from #172, which covered generation-phase costs (introspection, source-link
generation). Here generation is fast; it's the quarto render step that blows up.

Measurements

All runs on the same 90-page site (macOS, Quarto 1.9.38, great-docs 0.14.0). The site's API
pages contain no executable cells — pure signature/docstring pages.

Configuration quarto render time
As generated by great-docs 19m 29s
As generated, minus the jupyter: python3 line 18m 41s
Same 90 pages, bare project: type: website 14.6s
+ the great-docs SCSS theme (9,167 lines) 15.1s
+ the three Lua filters (output-title, details, gd-lightbox) 15.6s
Full generated config, render list cut to 10 pages 4.3s (~0.4s/page)
Full generated config, sidebar block deleted 30.8s
Full generated config, sidebar entries rewritten to explicit text:/href: 6m 07s

Two observations from the table:

  1. The theme, filters, and engine binding are all essentially free. The sidebar accounts for
    ~97% of the render time.
  2. Per-page cost is not constant: ~0.4s/page when 10 pages render, ~12.4s/page when 90 render.
    Cost grows with (pages rendered × sidebar entries), i.e. quadratically in API size, so this
    gets dramatically worse for larger packages.

Suggested fixes

  1. Emit sidebar entries as explicit text: + href: pairs instead of bare paths.
    great-docs already knows every page's title at generation time. Measured effect: 3.1×
    (19.5m → 6m). Low-risk, no UX change.
  2. Offer a compact-sidebar option (sections link to per-section index pages instead of
    listing every method). Measured upper bound: ~36× (19.5m → ~31s). Changes navigation UX,
    so presumably opt-in.

Even with explicit titles Quarto still pays substantial per-page × per-entry cost (6m is much
better than 19.5m but far from 31s), so the residual may be worth raising with quarto-cli —
happy to file there too if useful.

Side note

jupyter: python3 is written into _quarto.yml unconditionally, binding every page to the
Jupyter engine even when no page in the site has executable cells. At project scale it's minor
(48s here), but it adds ~20s of kernel startup to every single-page render, which hurts
iterating on one page. Only writing the key when executable content exists (or letting users
set jupyter: false) would avoid it.

Environment

  • great-docs 0.14.0
  • Quarto 1.9.38
  • macOS (Darwin 25.5), Python 3.13
  • Package: internal library, 86 documented objects / 90 site pages, no executable code cells

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.