# mdbook-content-loader

**Zero-runtime-fetch content collections for mdBook**

Injects your `content-collections.json` (generated by
[`mdbook-content-collections`](https://crates.io/crates/mdbook-content-collections))
directly into every HTML page as `window.CONTENT_COLLECTIONS` — **no network
request, no path hacks, works offline**.

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 (and why you want it)

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 global, available **immediately** on every page:

```html
<script>
  window.CONTENT_COLLECTIONS = { ...ready to use... }
</script>
```

```js
window.CONTENT_COLLECTIONS = {
  entries: [
    /* all published posts, sorted newest first */
  ],
  collections: {
    posts: [
      /* fallback collection */
    ],
    blog: [
      /* frontmatter: collection: blog */
    ],
    notes: [
      /* ... */
    ],
    // ... any collection name you use
  },
  generated_at: "2025-11-28T10:00:00Z",
};
```

Each entry has the full shape from `mdbook-content-collections`:

```ts
{
  path: "blog/my-post.md",
  title: "My Great Post",
  date: "2025-11-27T00:00:00Z",
  author?: string,
  description?: string,
  collection?: string,
  tags: string[],
  draft: false,
  preview_html: "<p>First paragraph <strong>rendered</strong>...</p>"
}
```

- Drafts are filtered out automatically

- Everything is sorted newest -> oldest

- No runtime parsing, fetching, or error handling needed

## Installation

```bash
cargo install mdbook-content-loader
# If not installed, install mdbook-content-collections
# cargo install mdbook-content-collections
```

**Setup**

Add the following to your `book.toml`:

```toml
[preprocessor.content-collections]
# Generates content-collections.json

[preprocessor.content-loader]
command = "mdbook-content-loader"
after = ["content-collections"]   # ← important!
```

- The `after` key ensures the JSON exists before this loader runs.

## Usage in your theme

Drop this anywhere in `theme/index.hbs` (or any `.hbs` file):

```html
<div id="latest-posts"></div>

<script>
  if (!window.CONTENT_COLLECTIONS) {
    console.warn("mdbook-content-loader: data not loaded");
  } else {
    const posts =
      window.CONTENT_COLLECTIONS.collections.blog ||
      window.CONTENT_COLLECTIONS.collections.posts ||
      [];

    const list = document.getElementById("latest-posts");
    const ul = document.createElement("ul");

    posts.slice(0, 5).forEach((post) => {
      const li = document.createElement("li");
      li.innerHTML = `
        <h3><a href="$$ {post.path.replace(/\.md $$/, '.html')}">${post.title}</a></h3>
        ${post.date ? `<time>${new Date(post.date).toISOString().slice(0, 10)}</time>` : ""}
        <div class="preview">${post.preview_html}</div>
      `;
      ul.appendChild(li);
    });

    list.appendChild(ul);
  }
</script>
```

Want a sidebar? Tag cloud? Search index? Related posts? Just read from
`window.CONTENT_COLLECTIONS`.

## Common patterns

```js
// Latest 5 blog posts
window.CONTENT_COLLECTIONS.collections.blog?.slice(0, 5);

// All posts (any collection)
window.CONTENT_COLLECTIONS.entries;

// Posts with tag "rust"
window.CONTENT_COLLECTIONS.entries.filter((p) => p.tags.includes("rust"));
```

## Data Shape

Same as `mdbook-content-collections`, plus:

- `collections`: grouped + sorted by collection name

- `generated_at`: when the data was build

- All `draft: true` entries removed

See:
[mdbook-content-collections](https://crates.io/crates/mdbook-content-collections)

### License

Apache 2.0
