Share a bookdown book

bookdown is what R users reach for when a report grows past one document: numbered chapters in separate .Rmd files, cross-references that resolve (\@ref(fig:coverage-plot)), citations from a .bib, and a rendered site with a sidebar and full-text search.

bookdown::render_book("index.Rmd", "bookdown::bs4_book")   # → _book/

And the output is a directory:

_book/
├─ index.html            ← entry point
├─ methods.html  results.html  appendix.html
├─ search.json           ← fetched at load
├─ libs/                 ← jQuery, Bootstrap, MathJax
└─ figures/  *_files/    ← plots

Every failure people hit with this comes from treating that directory as a file. Sending index.html alone gives the reader chapter one with dead links to every other chapter. Zipping it makes them unpack and hunt for the entry point. And opened from file://, the search index fetch is blocked by the browser, so search silently returns nothing.

The published fallback is usually GitHub Pages or Netlify — which means a repo, a build step, a deploy, and a public URL whether or not the content should be public.

Publish the folder

Drag _book/ (or a zip of it) into the app:

  • index.html becomes the report body.
  • Every chapter page, search.json, the libs/ bundle and the figure directories upload alongside it, and their relative references are rewritten to the uploaded copies.
  • Scripts run inside a sandboxed iframe (allow-scripts, no allow-same-origin), so the sidebar, the search box and MathJax all work — including the search index fetch that file:// blocks.

Or PATCH the entry file from the render script:

Rscript -e 'bookdown::render_book("index.Rmd", "bookdown::bs4_book")'

curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html _book/index.html \
        --arg t "Methods handbook — 2026 edition" '{title: $t, html: $html}')"

When one file is the better answer

If what you have is a long report rather than a book, the folder question goes away:

output:
  bookdown::html_document2:
    self_contained: true
    toc: true
    toc_float: true

That inlines the figures and the libraries into one file. You lose per-chapter URLs and the sidebar's cross-document navigation; you gain an artifact that is trivially portable. For anything under about thirty pages it is usually the right trade.

Draft rounds

A book in review gets re-rendered constantly. PATCH one report id and the link in the reviewers' calendar invitation stays correct:

Rscript -e 'bookdown::render_book("index.Rmd")'
# …PATCH as above…

One URL, a revision per render. Reviewers reading revision 4 can see what changed since revision 3, which is the part an emailed PDF cannot do.

What review adds

  • Anchored threads on a paragraph or a figure, so a correction sits where the error is — see commenting on HTML.
  • Revisions, so the review round has a record.
  • Access per report — private, team, domain-gated or named reviewers, which is how a draft stays a draft. See the sharing model.

Limits

  • Entry HTML: 5 MB. Assets: 25 MB per file, 250 MB and 500 files total. A long book with many high-resolution plots is the case that approaches the file count — render figures at a sensible DPI rather than publishing 600-dpi PNGs nobody will zoom into.
  • 60 requests/minute per token.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Publish a book →

Related