Orientation
Content Processing
Search Infrastructure
Rendering and UI
Developer Tooling
How It Works
The following files were used as context for generating this wiki page:
Obsidian Vaults in the context of Fumadocs serves as a bridging mechanism to transform standard, local Obsidian note repositories into structured, web-ready documentation content. It solves the compatibility problem between the idiosyncratic markdown features used by Obsidian users (such as Wikilinks, callouts, block IDs, and comments) and the standard MDX/Remark pipeline used by modern static site generators.
The component architecture is designed to intercept raw file collections, parse them into a virtualized storage model, and apply specialized Remark transformations. By centralizing the resolution of cross-file references and syntax conversion, Obsidian Vaults allows developers to maintain their documentation in a personal knowledge management tool while publishing high-quality, linked web documentation with minimal friction.
The system is built on a modular, pipeline-oriented architecture. It uses a custom VaultStorage to index files, a VaultResolver for path and name-based lookups, and a specific suite of Remark plugins to handle the Obsidian dialect. This decoupling ensures that the core documentation engine remains agnostic of the input source, while the vault-specific logic resides within isolated plugins that translate complex Obsidian markdown into standard MDX elements like custom components or standardized HTML structures.
The storage model is anchored by buildStorage, which ingests raw files and classifies them into one of three formats: content, media, or data. This classification drives how the downstream conversion pipeline treats each entry: content files undergo Remark processing and frontmatter parsing, media files are treated as assets, and data files are preserved as-is.
Sources: packages/obsidian/src/build-storage.ts:39-68
The VaultResolver provides the lookup service for cross-linking. It creates internal mapping tables (pathToFile, nameToFile) to facilitate lookups by file name, relative path, or absolute vault path.
When resolveAny(name, fromPath) is called, the system evaluates the target name:
./ or ../ to resolve relative to the current file's directory.pathToFile.nameToFile if the initial resolution fails, providing robust handling for links that omit directory depth.// Example usage of VaultResolver
const resolver = buildResolver(storage);
const target = resolver.resolveAny('my-note', '/path/to/current/note.md');Sources: packages/obsidian/src/build-resolver.ts:5-26, packages/obsidian/src/build-resolver.ts:58-69
The Wikilink plugin (remarkWikilinks) is responsible for converting Obsidian's link syntax into valid Markdown links or embedded components. The process follows a specific order of operations:
paragraph nodes containing wikilinks.resolveParagraphText function executes a regex match for the ... pattern.isEmbed = false), it calls resolver.resolveAny and generates a standard markdown link node.isEmbed = true), it attempts to resolve the file and returns a mdxJsxFlowElement (typically an <include /> component) for content or an image node for media assets.Note
Wikilinks that resolve to heading-only targets are handled specifically by getHeadingHash, which processes the hash segment without slugifying block IDs (strings starting with ^).
Sources: packages/obsidian/src/remark/remark-wikilinks.ts:15-17, packages/obsidian/src/remark/remark-wikilinks.ts:84-137
Obsidian's blockquote syntax [!type] is converted into standard Fumadocs-compatible callout components. The remarkConvert plugin handles this:
blockquote nodes in the MDAST.resolveCallout extracts the type (e.g., info, warning) from the first line using RegexCalloutHead.mdast-separate to split the title from the rest of the node content.createCallout to transform the structure into a clean JSX/component-ready tree.Sources: packages/obsidian/src/remark/remark-convert.ts:10-46
Obsidian uses ^block-id as an anchor. The remarkBlockId plugin parses these IDs by visiting paragraph nodes, scanning for the regex /(?<!\\)\^(?<block_id>\w+)$/m, and replacing the paragraph with a <section> element featuring the corresponding id attribute.
Sources: packages/obsidian/src/remark/remark-block-id.ts:7-50
The fromVault entry point orchestrates the lifecycle:
readVaultFiles: Fetches raw file contents from the disk.convertVaultFiles:
remark-parse → remark-gfm → remark-math → remarkWikilinks → remarkConvert → remarkObsidianComment → remarkBlockId).remark-mdx and remark-stringify.writeVaultFiles: Maps the resulting output types (asset, content, data) to their respective destinations.Sources: packages/obsidian/src/index.ts:15-20, packages/obsidian/src/convert.ts:57-119