FAQ
Common questions about Blume — how it compares to other documentation tools, and why a Markdown formatter might collapse your callout directives.
Answers to questions that come up often. Missing one? Open an issue or ask the in-page assistant.
How is Blume different from Mintlify, Fumadocs, and others?
Most documentation tools sit at one of two extremes. Managed platforms like Mintlify give you a polished result fast, but the build and hosting are their service — you author inside their system and deploy to their infrastructure. Component libraries and starters like Fumadocs, Nextra, or Docusaurus are open-source and flexible, but they hand you an application (a Next.js or React project) that you scaffold, wire up, and maintain before and after you write a word.
Blume takes a third path: the framework is the template. You point it at a folder of Markdown and it generates and drives the whole site — navigation, search, theming, Open Graph images, SEO, and AI endpoints — with no app to own. It’s fully open-source and self-hostable, so there’s no managed service and no vendor lock-in, but there’s also no boilerplate to maintain.
| Blume | Mintlify | Fumadocs / Nextra / Docusaurus | |
|---|---|---|---|
| Model | Zero-config framework; content only | Hosted platform | Library + app you scaffold |
| Source | Open-source (MIT) | Closed core | Open-source |
| Hosting | Anywhere — static or a server function | Their managed infrastructure | Anywhere; you build and deploy |
| You maintain | Your Markdown | Your Markdown + platform config | Your Markdown + the app around it |
| Rendering | Astro; core theme ships zero client JS | Their runtime | React/Next.js runtime |
| AI features | llms.txt, raw Markdown, Ask AI, MCP — built in, no hosted service |
Built in (hosted) | Bring your own |
A few consequences worth calling out:
- You own the output.
blume buildproduces a plain site you host on Vercel, Netlify, Cloudflare, S3, or your own box. Nothing phones home. - No lock-in, two ways out. Your content is portable Markdown, and
blume ejectturns the project into a standalone Astro app that still uses theblumepackage when you want full control. - Fast by default. The core theme is React-free and renders static HTML, so pages score well on Core Web Vitals without tuning. You opt into server features (Ask AI, MCP) only when you need them.
- Type-safe configuration.
blume.config.tsand everymeta.tsare real TypeScript validated by a schema — not loosely-typed YAML.
See Why Blume exists for the longer version.
Is Blume free and open-source?
Yes — Blume is MIT-licensed and free. You install the blume package, keep your content in your own repository, and host the build wherever you like. There’s no paid tier, no per-seat pricing, and no account to sign up for. The source lives on GitHub.
Do I need to know Astro, React, or Tailwind?
No. A folder of Markdown is a complete site — navigation, search, and theming are inferred or set with a handful of tokens. You only reach for the underlying stack when you want to customize: interactive islands (React), component overrides, or theme tokens (Tailwind). Even then, blume.config.ts is typed, so your editor guides you.
Can I use React components and MDX?
Yes. Any page can be .md or .mdx, and MDX lets you drop in the built-in components with no imports. You can also add your own .tsx/.jsx islands — Blume auto-enables React only for the pages that use them, so the core theme stays JavaScript-free everywhere else.
Where can I deploy it?
Anywhere. blume build outputs static HTML by default, which you can serve from any static host or CDN — Vercel, Netlify, Cloudflare Pages, GitHub Pages, S3, or your own server. Server-only features (Ask AI, the MCP server, on-demand rendering) switch the build to a server function through an adapter for Vercel, Node, Netlify, or Cloudflare. See Deployment.
Does search need a hosted service?
No. Orama builds a local index that works in both dev and production with nothing to host or pay for. For very large sites, Pagefind is one flag away. Either way the index ships as part of your site.
How do I customize the look?
Start with theme tokens — accent color, fonts, radius, and a theme.css for anything else Tailwind can express. Go further by overriding built-in components or adding custom pages. When you want the Astro project itself, blume eject hands you a standalone app that still uses the blume package.
Why is oxfmt / Ultracite collapsing my directives?
If you format your Markdown with Ultracite (which runs oxlint + oxfmt) — as Blume itself does — you may notice that container directives get flattened onto a single line after a format pass:
:::note
Regenerate the project with blume dev.
:::
becomes
:::note Regenerate the project with blume dev. :::
Once the opening :::note fence is joined to the prose, it’s no longer a directive, so it renders as literal text instead of a callout.
Why it happens
This is a bug in oxfmt’s Markdown formatter (inherited from Prettier’s Markdown printer — see prettier/prettier#19040). When it wraps prose, it treats the ::: fence lines as ordinary text and joins them with the adjacent line, breaking the directive. It affects every container directive — :::note, :::tip, :::warning, :::danger, :::success.
We reported it upstream in oxc-project/oxc#24096; until it’s fixed there, the patch below is the workaround.
The fix
Patch oxfmt so it preserves the line break that sits directly against a ::: fence. Blume ships exactly this patch in its own repo, and you can apply the same one in any project.
-
Save the patch as
patches/oxfmt@0.57.0.patch:diff --git a/dist/markdown-B5hFVJKQ.js b/dist/markdown-B5hFVJKQ.js index 58322b247b263f87975c2a97eecfe0b97d7143c4..0f39f1901603026a391c6d343cb838dfaccbfd03 100644 --- a/dist/markdown-B5hFVJKQ.js +++ b/dist/markdown-B5hFVJKQ.js @@ -1141,7 +1141,14 @@ function Yf(e, r, t) { case "sentence": return Yi(e, t); case "word": return Li(e); case "whitespace": { - let { next: i } = e, u = i && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/u.test(i.value) ? "never" : r.proseWrap; + let { next: i, previous: oxfmtFencePrev } = e; + // Preserve line breaks that sit directly against a `:::` container + // directive fence, so `proseWrap: "never"` keeps the opening/closing + // fence on their own lines instead of joining them into the prose (which + // breaks the directive). Ordinary prose still wraps per proseWrap. + // See prettier/prettier#19040. + let oxfmtIsFence = (w) => w != null && typeof w.value === "string" && w.value.startsWith(":::"); + let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtIsFence(i) ? "preserve" : i && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/u.test(i.value) ? "never" : r.proseWrap; return qt(e, n.value, u); } case "emphasis": { -
Register it with your package manager’s
patchedDependencies. With Bun or pnpm, add topackage.json:{ "patchedDependencies": { "oxfmt@0.57.0": "patches/oxfmt@0.57.0.patch" } } -
Reinstall so the patch is applied:
npm installpnpm installyarn installbun install