A Docusaurus plugin that seamlessly integrates your Obsidian vault with Docusaurus, automatically transforming Obsidian-specific syntax (wikilinks, callouts) into Docusaurus-compatible markdown.
A live demo is available at: gl0bal01.com/intel-codex
- Automatic Content Sync: Sync your Obsidian vault to Docusaurus docs at build time
- Wikilink Conversion: Transforms
[[wikilinks]]into proper Docusaurus markdown links - Callout Support: Converts Obsidian callouts to Docusaurus admonitions
- Asset Management: Automatically copies images and other assets
- Category Generation: Auto-generates
_category_.jsonfiles for sidebar navigation - Frontmatter Enhancement: Preserves and enhances YAML frontmatter
- Custom Banners: Add informational banners at top/bottom of synced files
- MDX Compatibility: Escapes problematic characters for MDX
- Local & GitHub Sources: Support for both local vaults and GitHub repositories
- Customizable Transformations: Configure which transformations to apply
- Hot Reload: Watch mode for local development
npm install docusaurus-plugin-obsidian-vaultor
yarn add docusaurus-plugin-obsidian-vaultAdd the plugin to your docusaurus.config.ts:
import type { Config } from '@docusaurus/types';
const config: Config = {
// ... other config
plugins: [
[
'docusaurus-plugin-obsidian-vault',
{
vaultSource: {
type: 'local',
path: '/path/to/your/obsidian-vault',
},
docsPath: 'docs/vault',
assetsPath: 'static/img/vault',
},
],
],
};
export default config;interface ObsidianPluginOptions {
// Source vault configuration (required)
vaultSource: VaultSource;
// Destination in Docusaurus docs (required)
docsPath: string;
// Multi-instance docs support (optional)
docsPluginId?: string; // ID of the docs plugin instance for multi-instance setups
// Transformation options
transformations?: TransformationOptions;
// Asset handling
assetsPath?: string; // Default: 'static/img/obsidian-vault'
// Filtering
exclude?: string[]; // Default: ['**/.obsidian/**', '**/.git/**', '**/node_modules/**']
include?: string[]; // Default: ['**/*.md']
// Category generation
generateCategories?: boolean; // Default: true
categoryLabels?: Record<string, string>;
categoryIndexFile?: string; // Filename to use as category index (e.g., 'START.md', 'README.md')
// Banners
bannerTop?: string; // Optional banner at top of each file (after frontmatter)
bannerBottom?: string; // Optional banner at bottom of each file
// Debug
debug?: boolean; // Default: false
}{
vaultSource: {
type: 'local',
path: '/absolute/path/to/vault',
}
}{
vaultSource: {
type: 'github',
repository: 'username/repo-name',
branch: 'main', // Optional, default: 'main'
token: process.env.GITHUB_TOKEN, // For private repos
path: '.temp-vault', // Where to expect the cloned repo
}
}For GitHub sources, you'll need to clone the repository in your CI/CD pipeline:
# .github/workflows/deploy.yml
- name: Checkout vault
uses: actions/checkout@v4
with:
repository: username/vault-repo
path: .temp-vault
token: ${{ secrets.GITHUB_TOKEN }}{
transformations: {
convertWikilinks: true, // Default: true
wikilinkStyle: 'relative', // 'relative' | 'absolute'
convertCallouts: true, // Default: true
preserveFrontmatter: true, // Default: true
}
}// docusaurus.config.ts
export default {
plugins: [
[
'docusaurus-plugin-obsidian-vault',
{
vaultSource: {
type: 'local',
path: '/Users/username/Documents/MyVault',
},
docsPath: 'docs/knowledge-base',
},
],
],
};// docusaurus.config.ts
export default {
plugins: [
[
'docusaurus-plugin-obsidian-vault',
{
vaultSource: {
type: 'local',
path: '/Users/username/Documents/MyVault',
},
docsPath: 'docs/knowledge-base',
assetsPath: 'static/img/kb',
// Exclude certain paths
exclude: [
'**/.obsidian/**',
'**/Private/**',
'**/Templates/**',
],
// Custom category labels
categoryLabels: {
'osint': 'Open Source Intelligence',
'cyber': 'Cybersecurity',
'tools': 'Tool Documentation',
},
// Transformation options
transformations: {
convertWikilinks: true,
convertCallouts: true,
preserveFrontmatter: true,
},
// Custom banners
bannerTop: ':::info\nThis file is synced from the Obsidian vault. [Edit on GitHub](https://github.com/username/vault)\n:::',
bannerBottom: '*Last synced from Obsidian vault*',
generateCategories: true,
categoryIndexFile: 'START.md', // Use START.md as category index page
debug: true,
},
],
],
};// docusaurus.config.ts
export default {
plugins: [
[
'docusaurus-plugin-obsidian-vault',
{
vaultSource: {
type: 'github',
repository: 'username/my-vault',
branch: 'main',
path: '.temp-vault',
},
docsPath: 'docs/vault',
},
],
],
};Docusaurus supports multiple docs instances, allowing you to have separate documentation sections with their own sidebars and routes. This is perfect for integrating an Obsidian vault as a separate docs instance alongside your main documentation.
Why use multi-instance?
- Keep your Obsidian vault content separate from your main docs
- Different route base paths (e.g.,
/docsfor main docs,/intel-codexfor vault) - Separate sidebars and navigation
- Different edit URLs and configurations
Complete multi-instance setup requires TWO plugin configurations:
- A
@docusaurus/plugin-content-docsinstance for the vault - The
docusaurus-plugin-obsidian-vaultto sync content to that instance
// docusaurus.config.ts
import type { Config } from '@docusaurus/types';
const config: Config = {
// ... other config
plugins: [
// 1. Create a separate docs instance for your Obsidian vault
[
'@docusaurus/plugin-content-docs',
{
id: 'intel-codex', // Unique ID for this docs instance
path: 'intel-codex', // Folder where vault content will be synced
routeBasePath: 'intel-codex', // URL route: /intel-codex
sidebarPath: './sidebars-intel-codex.ts', // Separate sidebar config
editUrl: 'https://github.com/username/vault-repo/tree/main/',
showLastUpdateAuthor: false,
showLastUpdateTime: true,
sidebarCollapsed: true,
},
],
// 2. Obsidian vault sync plugin (syncs TO the intel-codex folder)
[
'docusaurus-plugin-obsidian-vault',
{
vaultSource: {
type: 'github',
repository: 'username/vault-repo',
branch: 'main',
path: '.temp-vault', // GitHub Actions clones here
},
docsPath: 'intel-codex', // Syncs to the docs instance folder
docsPluginId: 'intel-codex', // Optional: specify which docs instance
assetsPath: 'static/img/intel-codex',
exclude: ['**/.obsidian/**', '**/.git/**', '**/Templates/**'],
transformations: {
convertWikilinks: true,
convertCallouts: true,
preserveFrontmatter: true,
},
generateCategories: true,
categoryLabels: {
'intel-codex': 'Intel Codex',
'Investigations': 'Investigations',
'Security': 'Security Research',
},
categoryIndexFile: 'START.md', // Use START.md as category index
bannerTop: ':::danger[Synced from **Obsidian vault**]\\nFor full graph and advanced features, download the **[Vault](https://github.com/username/vault-repo)** and open in **Obsidian**.\\n:::',
debug: true,
},
],
],
// Configure the navbar to link to the vault docs instance
themeConfig: {
navbar: {
items: [
// Regular docs link
{
type: 'docSidebar',
sidebarId: 'mainSidebar',
position: 'left',
label: 'Docs',
},
// Link to vault docs instance
{
type: 'docSidebar',
sidebarId: 'intelCodexSidebar', // Must match sidebar ID in sidebars-intel-codex.ts
position: 'left',
label: 'Intel Codex',
docsPluginId: 'intel-codex', // Must match docs instance ID
},
],
},
},
};
export default config;Create a separate sidebar file for the vault (sidebars-intel-codex.ts):
import type { SidebarsConfig } from '@docusaurus/plugin-content-docs';
const sidebars: SidebarsConfig = {
// This ID must match the sidebarId in navbar config
intelCodexSidebar: [
{
type: 'autogenerated',
dirName: '.', // Generate sidebar from intel-codex folder
},
],
};
export default sidebars;GitHub Actions workflow (for GitHub vault sources):
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout main repo
uses: actions/checkout@v4
# Clone the vault BEFORE building Docusaurus
- name: Checkout Obsidian vault
uses: actions/checkout@v4
with:
repository: username/vault-repo
path: .temp-vault
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Build site
run: npm run build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./buildImportant notes for multi-instance:
-
Folder Structure:
- The
intel-codex/folder should be git-ignored (it's auto-generated) - The plugin automatically creates this folder with a placeholder during initialization
- The plugin syncs vault content to this folder at build time
- The placeholder is removed after vault sync completes
- The
-
Route Paths:
- Main docs:
/(configured in preset) - Vault docs:
/intel-codex(configured in docs instance)
- Main docs:
-
Sidebar Configuration:
- Main docs use
sidebars.ts - Vault docs use
sidebars-intel-codex.ts - Each has independent navigation
- Main docs use
-
Edit URLs:
- Main docs can link to main repo
- Vault docs link to vault repo (configured in docs instance
editUrl)
-
docsPluginId Usage:
- The
docsPluginIdoption is informational and helps with debugging - The actual multi-instance setup is done via the
@docusaurus/plugin-content-docsplugin - Both plugins must use matching paths (e.g., both use
intel-codex)
- The
-
Plugin Initialization Order:
- The obsidian-vault-plugin creates the destination folder immediately during plugin initialization
- This ensures the folder exists BEFORE the
@docusaurus/plugin-content-docsplugin tries to load it - A temporary placeholder file is created to prevent errors, then removed after sync completes
Obsidian wikilinks are automatically converted to Docusaurus-compatible links. The plugin intelligently calculates relative paths based on file location.
Obsidian wikilinks are vault-root-relative:
[[Page Name]]
[[folder/Page Name|Custom Label]]
[[Page Name#Section]]Converted paths depend on file depth:
From a root-level file:
[Page Name](./Page Name.md)
[Custom Label](./folder/Page Name.md)From a nested file (e.g., Cases/Example/README.md):
[Page Name](../../Page Name.md)
[Custom Label](../../folder/Page Name.md)The plugin automatically:
- Calculates the correct
../prefix based on file depth - Preserves custom display labels
- Handles section anchors (
#heading) - Works with both root-level and nested files
Obsidian callouts are converted to Docusaurus admonitions:
Obsidian:
> [!note] Important Note
> This is the content of the callout.
> It can span multiple lines.Docusaurus:
:::note Important Note
This is the content of the callout.
It can span multiple lines.
:::Supported callout types: note, tip, info, warning, danger, caution
The plugin preserves and enhances YAML frontmatter:
---
title: My Page
tags: [osint, research]
sidebar_position: 1
---If no title or sidebar_label is present, one is automatically generated from the filename.
By default, Docusaurus generates an index page for each category. You can specify a custom file to use as the category index:
Configuration:
{
categoryIndexFile: 'START.md'
}How it works:
- Root category: If
START.mdexists in the vault root, the main category link points to it - Subdirectories: If a subdirectory contains
START.md, that directory's category link points to it - Fallback: If the file doesn't exist in a directory, Docusaurus generates a default index
Example:
vault/
├── START.md ← Root category index
├── Investigations/
│ ├── START.md ← Investigations category index
│ └── Platforms/
│ └── twitter.md
└── Security/
└── malware.md ← No START.md, uses generated index
With this setup:
- Clicking "Vault" in sidebar → Opens root
START.md - Clicking "Investigations" → Opens
Investigations/START.md - Clicking "Security" → Shows auto-generated index
Add custom banners to all synced files to provide context or attribution:
Configuration:
{
bannerTop: ':::info\nThis file is part of the Obsidian vault. [Edit on GitHub](https://github.com/user/repo)\n:::',
bannerBottom: '*Last synced from Obsidian vault*'
}Result:
bannerTopis inserted after frontmatter (or at the top if no frontmatter exists)bannerBottomis appended at the end of the file- Banners support full markdown including Docusaurus admonitions
Example use cases:
- Attribution and edit links:
:::note\nEdit this file in the [Obsidian vault](https://github.com/user/vault)\n::: - Sync warnings:
:::caution\nDo not edit this file directly - changes will be overwritten\n::: - Metadata:
*Last synced: {new Date().toISOString()}*
When using a local vault, the plugin watches for changes and hot-reloads:
npm startThe plugin will automatically re-sync when you modify files in your vault.
npm run buildThe plugin runs during the build process and syncs all vault content.
The plugin generates vault statistics accessible in your Docusaurus site:
{
"totalFiles": 150,
"totalSize": 2048000,
"categoriesGenerated": 12,
"wikilinsConverted": 450,
"calloutsConverted": 89,
"assetscopied": 73
}Access via: .docusaurus/docusaurus-plugin-obsidian-vault/default/vault-stats.json
- Check that your
vaultSource.pathis correct - Verify files aren't being excluded by your
excludepatterns - Enable
debug: trueto see detailed logs
- Ensure
transformations.convertWikilinksistrue - Check that the linked files exist in your vault
- Relative paths should work automatically
- Verify callout syntax matches Obsidian format:
> [!type] Title - Supported types: note, tip, info, warning, danger, caution
- Enable
debug: trueto check transformation logs
- Ensure the repository is cloned before Docusaurus build
- Check that
vaultSource.pathmatches your checkout path - Verify GitHub token has correct permissions for private repos
Contributions are welcome! Please feel free to submit a Pull Request.
See MIT License file for details