mdbook-content-loader
Zero-runtime-fetch content collections for mdBook
Injects your content-collections.json (generated by
mdbook-content-collections)
directly into your HTML as window.CONTENT_COLLECTIONS — no network request,
no path hacks, works offline.
By default, the data is injected on your landing page (index.md) only. You can
optionally enable the old behavior and inject it on every page via a config
flag.
This gives you true Astro/Zola-style content collections inside mdBook: instant, reliable access to posts, blog entries, notes, changelogs — anywhere in your theme.
Why this exists
The original mdbook-content-collections example used
fetch('content-collections.json') in the browser.
That works… until it doesn’t:
| Problem | With fetch() |
With mdbook-content-loader |
|---|---|---|
| Extra HTTP request | Yes | No |
Broken in file:// / offline mode |
Yes | Works perfectly |
| Fragile URL construction | Yes | No path logic needed |
| Flash of empty content | Yes | Instant |
| Fails silently on 404 | Yes | Build fails early |
| Works on GitHub Pages / custom domains | Sometimes | Always |
This crate fixes all of that permanently.
What you get
A single, ready-to-query global on every page (or just the landing page, by default)
-
window.CONTENT_COLLECTIONS.entries: all published items, sorted newest → oldest -
window.CONTENT_COLLECTIONS.collections: per‑collection buckets (blog, notes, changelog, etc.) -
window.CONTENT_COLLECTIONS.generated_at: build timestamp for cache busting or debugging
Each entry has the full shape from mdbook-content-collections (path, title,
date, description, tags, optional collection, preview_html, and draft
stripped out), so your theme code can immediately render lists, sidebars, tag
clouds, and “related posts” without any extra parsing or network calls.
Installation
# If not installed, install mdbook-content-collections
# cargo install mdbook-content-collections
Setup
Add the following to your book.toml:
[]
# Generates content-collections.json
[]
= "mdbook-content-loader"
= ["content-collections"] # make sure JSON is generated first
# Behavior:
# - default: inject window.CONTENT_COLLECTIONS only into index.md
# - set inject_all = true to inject into every chapter
# inject_all = true
The after key ensures the JSON exists before this loader runs, and
inject_all is an optional boolean that restores the old “every page” behavior.
mdBook exposes this table under [preprocessor.content-loader] to the
preprocessor via PreprocessorContext, which is where this flag is read.
Works best with an empty index.md, create one by adding this as the first line
of your SUMMARY.md:
()[index.md]
Then run mdbook build to create it.
Usage in your theme
Drop this anywhere in theme/index.hbs (or any .hbs file).
For example, placing it directly after this block works, and you'll have a landing page of your "Latest Edited Posts" placed right above the prev/next buttons:
Add the following block:
Want a sidebar? Tag cloud? Search index? Related posts? Just read from
window.CONTENT_COLLECTIONS.
Common patterns
// Latest 5 blog posts
window.CONTENT_COLLECTIONS..?.;
// All posts (any collection)
window.CONTENT_COLLECTIONS.;
// Posts with tag "rust"
window.CONTENT_COLLECTIONS..;
Data Shape of content-collections.json
Same as mdbook-content-collections, plus:
-
entries: all entries from content-collections.json, sorted newest → oldest -
collections: grouped views of the same entries, for example: -
collections.blog: all entries where collection === "blog" -
collections.posts: all entries (fallback/all posts) -
generated_at: ISO 8601 timestamp string of when the data was injected intowindow.CONTENT_COLLECTIONS -
All
draft: trueentries removed
See: mdbook-content-collections