---
title: FAQ
description: >-
  Perguntas comuns sobre o Blume — como ele se compara a outras ferramentas de documentação e por que um formatador de Markdown pode colapsar as suas diretivas de callout.
sidebar:
  label: FAQ
---

Respostas para perguntas que aparecem com frequência. Falta alguma? [Abra uma issue](https://github.com/haydenbleasel/blume/issues) ou pergunte ao assistente na própria página.

## Em que o Blume é diferente do Mintlify, do Fumadocs e de outros? [#how-is-blume-different-from-mintlify-fumadocs-and-others]

A maioria das ferramentas de documentação fica em um de dois extremos. **Plataformas gerenciadas** como o Mintlify entregam um resultado polido rápido, mas o build e a hospedagem são o serviço deles — você escreve dentro do sistema deles e publica na infraestrutura deles. **Bibliotecas de componentes e starters** como o Fumadocs, o Nextra ou o Docusaurus são open-source e flexíveis, mas entregam para você um aplicativo (um projeto Next.js ou React) que você precisa montar, conectar e manter antes e depois de escrever uma única palavra.

O Blume segue um terceiro caminho: **o framework é o template.** Você aponta ele para uma pasta de Markdown e ele gera e conduz o site inteiro — navegação, busca, temas, imagens Open Graph, SEO e endpoints de IA — sem nenhum app para cuidar. É totalmente open-source e você mesmo pode hospedar, então não existe serviço gerenciado nem dependência de fornecedor, mas também não existe boilerplate para manter.

|  | Blume | Mintlify | Fumadocs / Nextra / Docusaurus |
| --- | --- | --- | --- |
| **Modelo** | Framework de configuração zero; só conteúdo | Plataforma hospedada | Biblioteca + app que você monta |
| **Código-fonte** | Open-source (MIT) | Núcleo fechado | Open-source |
| **Hospedagem** | Em qualquer lugar — estático ou uma função de servidor | A infraestrutura gerenciada deles | Em qualquer lugar; você faz o build e publica |
| **Você mantém** | O seu Markdown | O seu Markdown + configuração da plataforma | O seu Markdown + o app em volta dele |
| **Renderização** | Astro; o tema principal não envia nenhum JS de cliente | O runtime deles | Runtime React/Next.js |
| **Recursos de IA** | `llms.txt`, Markdown bruto, Ask AI, MCP — integrados, sem serviço hospedado | Integrados (hospedados) | Traga os seus |

Vale destacar algumas consequências:

- **O resultado é seu.** O `blume build` gera um site simples que você hospeda na Vercel, Netlify, Cloudflare, S3 ou na sua própria máquina. Nada manda dados para casa.
- **Sem dependência de fornecedor, com duas saídas.** O seu conteúdo é Markdown portátil, e o `blume eject` transforma o projeto em um app Astro autônomo que continua usando o pacote `blume` quando você quiser controle total.
- **Rápido por padrão.** O tema principal não usa React e renderiza HTML estático, então as páginas vão bem nos Core Web Vitals sem ajuste nenhum. Você adota os recursos de servidor (Ask AI, MCP) só quando precisa deles.
- **Configuração com segurança de tipos.** O `blume.config.ts` e cada `meta.ts` são TypeScript real validado por um schema — e não YAML de tipagem frouxa.

:::note
Isso não é "melhor que tudo" — plataformas gerenciadas e frameworks completos são a escolha certa quando você quer um produto hospedado ou controle máximo sobre o app. O Blume é para times que querem um site de documentação de nível de produção sem ter que cuidar nem da plataforma nem da encanação.
:::

Veja [Por que o Blume existe](/docs) para a versão mais longa.

## O Blume é gratuito e open-source? [#is-blume-free-and-open-source]

Sim — o Blume tem licença MIT e é gratuito. Você instala o pacote `blume`, mantém o seu conteúdo no seu próprio repositório e hospeda o build onde quiser. Não tem plano pago, nem preço por usuário, nem conta para criar. O código-fonte está no [GitHub](https://github.com/haydenbleasel/blume).

## Preciso saber Astro, React ou Tailwind? [#do-i-need-to-know-astro-react-or-tailwind]

Não. Uma pasta de Markdown é um site completo — navegação, busca e temas são inferidos ou definidos com um punhado de tokens. Você só recorre à stack por baixo quando quiser personalizar: [ilhas interativas](/docs/content/islands) (React), [substituições de componentes](/docs/configuration/customization) ou [tokens de tema](/docs/configuration/theming) (Tailwind). E, mesmo assim, o [`blume.config.ts`](/docs/configuration) é tipado, então o seu editor te guia.

## Posso usar componentes React e MDX? [#can-i-use-react-components-and-mdx]

Sim. Qualquer página pode ser `.md` ou `.mdx`, e o MDX permite inserir os [componentes integrados](/docs/content/components) sem imports. Você também pode adicionar as suas próprias [ilhas](/docs/content/islands) `.tsx`/`.jsx` — o Blume ativa o React automaticamente só nas páginas que as usam, então o tema principal continua livre de JavaScript em todo o resto.

## Onde posso publicar? [#where-can-i-deploy-it]

Em qualquer lugar. O `blume build` gera HTML estático por padrão, que você pode servir de qualquer hospedagem estática ou CDN — Vercel, Netlify, Cloudflare Pages, GitHub Pages, S3 ou o seu próprio servidor. Os recursos exclusivos de servidor (Ask AI, o servidor MCP, renderização sob demanda) mudam o build para uma função de servidor por meio de um adaptador para Vercel, Node, Netlify ou Cloudflare. Veja [Publicação](/docs/deployment).

## A busca precisa de um serviço hospedado? [#does-search-need-a-hosted-service]

Não. O [Orama](/docs/configuration/search) monta um índice local que funciona tanto em desenvolvimento quanto em produção, sem nada para hospedar ou pagar. Para sites muito grandes, o [Pagefind](/docs/configuration/search) está a uma flag de distância. De qualquer forma, o índice vai junto como parte do seu site.

## Como personalizo a aparência? [#how-do-i-customize-the-look]

Comece pelos [tokens de tema](/docs/configuration/theming) — cor de destaque, fontes, raio e um `theme.css` para tudo o mais que o Tailwind conseguir expressar. Vá além [substituindo componentes integrados](/docs/configuration/customization) ou adicionando [páginas personalizadas](/docs/configuration/customization#custom-pages). Quando você quiser o projeto Astro em si, o [`blume eject`](/docs/reference/cli) entrega um app autônomo que continua usando o pacote `blume`.

## Por que o oxfmt / Ultracite está colapsando as minhas diretivas? [#why-is-oxfmt--ultracite-collapsing-my-directives]

Se você formata o seu Markdown com o [Ultracite](https://www.ultracite.ai) (que roda oxlint + [oxfmt](https://oxc.rs)) — como o próprio Blume faz — talvez note que as diretivas de contêiner ficam achatadas em uma única linha depois de uma passagem de formatação:

```md
:::note
Regenerate the project with blume dev.
:::
```

vira

```md
:::note Regenerate the project with blume dev. :::
```

Uma vez que a linha de abertura `:::note` é unida à prosa, ela deixa de ser uma diretiva, então é renderizada como texto literal em vez de um [callout](/docs/content/syntax#callouts).

### Por que isso acontece [#why-it-happens]

Isso é um bug no formatador de Markdown do oxfmt (herdado do printer de Markdown do Prettier — veja [prettier/prettier#19040](https://github.com/prettier/prettier/pull/19040)). Quando ele quebra a prosa, trata as linhas de fence `:::` como texto comum e junta com a linha vizinha, quebrando a diretiva. Isso afeta todos os tipos de diretiva de contêiner — `:::note`, `:::tip`, `:::info`, `:::warning`, `:::danger`, `:::success`.

Reportamos o problema upstream em [oxc-project/oxc#24096](https://github.com/oxc-project/oxc/issues/24096); até ser corrigido lá, o patch abaixo é a solução alternativa.

### A correção [#the-fix]

Aplique um patch no oxfmt para que ele preserve a quebra de linha que fica encostada em um fence `:::`. O Blume inclui a mesma correção no próprio repositório, e você pode aplicá-la em qualquer projeto.

1. Salve o patch como `patches/oxfmt@0.67.0.patch`:

   ```diff patches/oxfmt@0.67.0.patch
   diff --git a/dist/markdown-BMigo7Hm.js b/dist/markdown-BMigo7Hm.js
   index bc9037f6c0de5516b139d8cdb195b1e25cd33bc0..a02c284e28bb535f9964a8a086ebb6549657e416 100644
   --- a/dist/markdown-BMigo7Hm.js
   +++ b/dist/markdown-BMigo7Hm.js
   @@ -4872,7 +4872,43 @@ function lu(e, t, r) {
    		case "sentence": return Oh(e, r);
    		case "word": return t.parser !== "mdx" ? zh(e, t) : Uh(e);
    		case "whitespace": {
   -			let { next: a } = e, u = a && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(a.value) && !NE(e) && !(t.proseWrap === "preserve" && RE(e)) ? "never" : t.proseWrap;
   +			let { next: a, 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(":::");
   +			// A titled directive (`:::warning[Heads up]`) parses its `[title]` as a
   +			// linkReference between two sentence nodes at the paragraph level: the
   +			// fence word ends the sentence before the reference, and the body's
   +			// leading newline opens the sentence after it. So when this whitespace
   +			// starts its sentence, climb to the paragraph and check whether the two
   +			// preceding siblings are a (link) reference and a sentence ending in a
   +			// `:::` fence word.
   +			let oxfmtPrevIsTitledFence = !1;
   +			if (oxfmtFencePrev == null && e.index === 0 && e.grandparent != null && Array.isArray(e.grandparent.children)) {
   +				let oxfmtSibs = e.grandparent.children, oxfmtSentIdx = oxfmtSibs.indexOf(e.parent);
   +				if (oxfmtSentIdx >= 2) {
   +					let oxfmtLink = oxfmtSibs[oxfmtSentIdx - 1], oxfmtBefore = oxfmtSibs[oxfmtSentIdx - 2];
   +					let oxfmtLastWord = oxfmtBefore && oxfmtBefore.type === "sentence" && Array.isArray(oxfmtBefore.children) ? oxfmtBefore.children[oxfmtBefore.children.length - 1] : null;
   +					oxfmtPrevIsTitledFence = oxfmtLink != null && (oxfmtLink.type === "linkReference" || oxfmtLink.type === "link") && oxfmtIsFence(oxfmtLastWord);
   +				}
   +			}
   +			// The plain-markdown parser keeps a titled fence's `[title]` as literal
   +			// words, so the whole directive is one sentence. For a newline
   +			// whitespace, walk back to the start of its visual line within the
   +			// sentence; a line led by a `:::` word is a fence whose break must stay.
   +			if (!oxfmtPrevIsTitledFence && e.node.value.includes("\n") && e.parent != null && Array.isArray(e.parent.children)) {
   +				let oxfmtLineFirst = null;
   +				for (let oxfmtJ = e.index - 1; oxfmtJ >= 0; oxfmtJ--) {
   +					let oxfmtSib = e.parent.children[oxfmtJ];
   +					if (oxfmtSib.type === "whitespace" && typeof oxfmtSib.value === "string" && oxfmtSib.value.includes("\n")) break;
   +					oxfmtLineFirst = oxfmtSib;
   +				}
   +				oxfmtPrevIsTitledFence = oxfmtIsFence(oxfmtLineFirst);
   +			}
   +			let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtPrevIsTitledFence || oxfmtIsFence(a) ? "preserve" : a && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(a.value) && !NE(e) && !(t.proseWrap === "preserve" && RE(e)) ? "never" : t.proseWrap;
    			return ou(e, n.value, u, !1, t);
    		}
    		case "emphasis": {
   ```

2. Registre ele no `patchedDependencies` do seu gerenciador de pacotes. Com o Bun ou o pnpm, adicione ao `package.json`:

   ```json package.json
   {
     "patchedDependencies": {
       "oxfmt@0.67.0": "patches/oxfmt@0.67.0.patch"
     }
   }
   ```

3. Reinstale para que o patch seja aplicado:

   ```package-install
   bun install
   ```

:::warning[Fixado em uma versão]
O patch tem como alvo um build específico do oxfmt — o diff dele referencia um arquivo cujo nome tem um hash por release (`dist/markdown-*.js`). Quando você atualizar o oxfmt, regere o patch (por exemplo, `bun patch oxfmt`) ou verifique se a correção upstream já chegou e o patch não é mais necessário.
:::

## Por que o Knip reporta as dependências do meu `blume.config.ts` como não usadas? [#why-does-knip-report-my-blumeconfigts-dependencies-as-unused]

O [Knip](https://knip.dev) só segue imports a partir de arquivos que ele sabe que são pontos de entrada, e ele descobre isso pelos plugins integrados dele. Ainda não existe um plugin do Blume, e o plugin do Astro do Knip também não é ativado: ele procura `astro` no seu próprio `package.json`, mas um projeto Blume depende do `blume`, e o projeto Astro gerado em `.blume/` está no gitignore, então o Knip nunca o enxerga. Nada referencia o `blume.config.ts`, então qualquer pacote que ele importa acaba sendo reportado como não usado.

Registre como entries os arquivos que o Blume carrega a partir da raiz do seu projeto. No `knip.json`:

```json knip.json
{
  "entry": [
    "blume.config.{ts,mjs,js}",
    "components.{ts,tsx}",
    "islands/**/*.{ts,tsx}",
    "pages/**/*"
  ]
}
```

Em um monorepo, coloque essa mesma lista de `entry` sob o workspace da documentação em `workspaces`. Remova qualquer linha de uma convenção que você não usa — `components.ts` para [substituições de componentes](/docs/configuration/customization#component-overrides), `islands/` para [ilhas interativas](/docs/configuration/customization#interactive-islands) e `pages/` para [páginas personalizadas](/docs/configuration/customization#custom-pages) (ajuste a última se você tiver mudado o `content.pages`).

O Knip só consegue seguir imports reais. Um pacote que só aparece nomeado dentro de uma string — digamos, uma [integração do Astro](/docs/configuration/customization#astro-integrations) que chama `injectScript("page", "import('some-package')")` — ainda precisa de uma entrada em `ignoreDependencies`.
