Transclude
Embeds another Markdown or MDX file via :::include{file="…"} before the rest of the Markdown pipeline runs.
The transclude feature embeds another Markdown or MDX file through a : directive. It is not the Obsidian ![[path]] wikilink syntax, and it does not render an <include> component. Instead, transclusion is the first mdast visitor: it parses the target file, splices that AST into the source document, and lets the inserted content continue through the normal Markdown pipeline.
Enable transclusion before authoring an include
transclude is off by default. When it is off, zfb leaves the parsed paragraph—Text(":::include") followed by an MdxTextExpression—untouched. The expression still contains the raw attribute string file=, which is not valid JavaScript, so the compiled JSX module fails and SSR returns 500. This is a feature-gating failure, not a missing renderer: no <include> component exists or is expected.
Set transclude: true in zudoDoc({...}) before adding a live directive. Do not configure the package-owned markdown.features block directly.
Configuration
Enable transclusion with the zudo-doc setting:
import { defineConfig } from "zfb/config";
import { zudoDoc } from "@takazudo/zudo-doc/config";
export default defineConfig(
zudoDoc({
transclude: true,
}),
);The public zudo-doc setting is boolean and defaults to false. Upstream zfb defines transclude as an object-typed Markdown feature and rejects the true shorthand, so zudoDoc() translates the enabled setting to markdown.features.transclude: {} internally. This showcase enables the setting so the example below runs during every site build.
Live include
The sentence below comes from a dedicated, non-routed Markdown fixture. Its sentinel makes the AST splice visible in the built HTML rather than merely proving that the build did not fail.
ZUDO_DOC_TRANSCLUDE_LIVE_SENTINEL_3890 — This sentence was spliced from the English non-routed include fixture.
Directive syntax
Basic inclusion—paths resolve relative to the source file containing the directive:
:::include{file="./snippets/intro.md"}Include a file as a plain fenced code block. code=true does not add a title bar:
:::include{file="./examples/hello.rs" code=true lang="rust"}Select a 1-based, inclusive line range. The lines value must be exactly N-M:
:::include{file="./src/lib.rs" code=true lang="rust" lines="10-30"}Do not add a directive label. : does not match the transclusion syntax and causes the same invalid-expression SSR failure described above.
Filesystem and failure constraints
The target must exist, and its path must be relative to the source file. Absolute paths and aliases are not supported.
After canonicalization, the target must remain under the project root. Traversal and symlinks cannot escape that boundary.
Every transclusion failure is build-fatal, including invalid paths, invalid
linesranges, excessive recursion, and cycles. There is no warning-only mode such aslinkValidationprovides.Includes may contain further includes, but recursion is capped at depth 5. Cycles are always detected, even before that limit is reached.
The directive needs blank lines above and below so Markdown recognizes it as block-level content.
Build-context requirement
Transclusion needs the source path and project filesystem. The context-free @takazudo/ pipeline therefore leaves it silently inert; it cannot test this feature. Use a real zfb build or zfb dev run, then verify that distinctive included content appears in the rendered page.