Includes
Reuse content across pages — splice shared Markdown, MDX, or code files into any page with the include syntax.
Write a snippet once and splice it into any page. An <include> statement on its own line embeds another file at build time, as if its content were written inline — headings join the page’s table of contents, text is indexed by search, and the content appears in the page’s .md mirror and llms-full.txt. A statement a formatter wraps across lines still splices, and one Blume can’t parse raises a BLUME_INCLUDE_MALFORMED warning at its line instead of rendering as a raw tag.
<include>./_snippets/prerequisites.mdx</include>
The path is resolved relative to the including file. Paths starting with / resolve from your content root, so deeply nested pages can reference shared snippets without ../../.. chains:
<include>/_snippets/prerequisites.mdx</include>
The syntax matches Fumadocs’ include syntax, so migrated content works unchanged.
Here it is live — this next callout is spliced from a shared snippet:
Partials
Any file whose name (or folder) starts with an underscore is excluded from routing, navigation, search, and sitemaps — that convention is the natural home for shared snippets. Patterns you add to content.exclude add to this rule rather than replacing it; to publish underscore files after all, add "!**/_*" to exclude:
docs/
_snippets/
prerequisites.mdx
cli-flags.md
guides/
quickstart.mdx ← <include>../_snippets/prerequisites.mdx</include>
index.mdx
A partial is a normal Markdown or MDX file. Its front matter is stripped when spliced (the including page’s front matter wins), and everything else — callouts, code fences, components, math — renders exactly as it would inline. Partials can include other partials; a circular include is reported as an error.
Link reference definitions ([pep 508]: https://peps.python.org/pep-0508/) cross includes too, so a partial can hold a site’s shared link definitions: the page and its other partials can cite a partial’s ([PEP 508]), and a partial can cite the page’s. When the page and a partial both define a label, each uses its own.
Relative image references inside a partial keep working: they’re rebased onto the including page, so a colocated  or <Card img="./cover.png"> next to the partial resolves wherever the partial is spliced. An image’s path is rebased in whatever form it’s written: in angle brackets (), with escaped parentheses, or followed by a title in any of its quotes. A link to another file next to the partial ([spec](./spec.pdf), or an <img src> or <a href> naming one) moves with it too. A link to a page resolves from the including page, as if it were written there.
Editing a partial while blume dev is running reloads every page that includes it.
Props
Pass values into a partial as attributes, and read them in the partial with {{name}}, the same syntax as variables. One partial then serves every page that needs a variation of it:
Upgrade to the **{{plan}}** plan to use {{feature}}.
<include plan="Enterprise" feature="SSO">
./_snippets/upgrade.mdx
</include>
A prop applies to the included file and to anything it includes in turn, and takes precedence over a site-wide variable with the same name. Every attribute except lang and meta is a prop; attribute names are lowercase, and values are quoted plain text. Props reach Markdown and MDX partials only: a {{name}} in an included code file isn’t replaced by a prop.
Including code files
A target that isn’t .md/.mdx is embedded as a fenced code block, with the language inferred from the extension. Use lang to override the language (or to show a Markdown file as source rather than splicing it), and meta to pass a fence meta string such as a title:
<include>./examples/config.ts</include>
<include meta='title="config.ts"'>./examples/config.ts</include>
<include lang="mdx">./_snippets/prerequisites.mdx</include>
Rules and diagnostics
Include statements must occupy their own line — they’re block-level, not inline. Statements inside fenced code blocks are left alone, so you can document the syntax itself (like this page does).
Targets must live inside your content root: a file outside it would be silently missing from version snapshots and ejected projects, so blume reports BLUME_INCLUDE_OUTSIDE_ROOT instead of splicing it. A target that doesn’t exist is BLUME_INCLUDE_NOT_FOUND, and a loop of includes is BLUME_INCLUDE_CYCLE — all three fail blume build (pass --no-strict to build anyway) and appear in blume validate.
Broken links inside a partial are reported against the partial file, not the pages that splice it, so you fix them where they live.
A partial is read in the including page’s format, so a .md partial that an .mdx page includes is read as MDX. MDX has no HTML comments, so the partial’s <!-- … --> comments are spliced as MDX comments ({/* … */}) and stay hidden. Other HTML there has to be valid JSX: an <img …> or <br> without a closing slash takes in everything after it in the partial, with no error. Blume warns about it as BLUME_MDX_UNCLOSED_ELEMENT at the partial’s line; close it as <br />. Anything else in the partial that MDX can’t parse once it’s spliced in, like a stray {, is a BLUME_MDX_SYNTAX warning at the partial’s line, and when it fails the build, the build reports it there too, not at the page that includes it.