obsidian-plugin-development
SKILL.md
Obsidian Plugin Development
Comprehensive guidance for building Obsidian community plugins using TypeScript.
Plugin Architecture
Obsidian plugins are TypeScript bundles that extend the app's functionality. Every plugin:
- Extends the
Pluginclass fromobsidian - Implements
onload()for initialization - Implements
onunload()for cleanup - Bundles to a single
main.jsfile via esbuild
Plugin Lifecycle
import { Plugin } from 'obsidian';
export default class MyPlugin extends Plugin {
async onload() {
// Configure resources, register commands, views, etc.
}
async onunload() {
// Release resources (automatic for register* methods)
}
}
Project Structure
Organize code across multiple files:
src/
main.ts # Plugin entry point, lifecycle only
settings.ts # Settings interface and tab
commands/ # Command implementations
ui/ # Modals, views, components
utils/ # Helpers and constants
types.ts # TypeScript interfaces
Keep main.ts minimal - delegate logic to modules.
Core Capabilities
Commands
Register user-invocable actions:
this.addCommand({
id: 'my-command', // Stable ID, never change after release
name: 'My Command', // User-visible name
callback: () => { ... }, // Or editorCallback, checkCallback
});
Use editorCallback for editor access, checkCallback for conditional availability.
Settings
Persist user configuration:
interface MySettings { enabled: boolean; }
const DEFAULT_SETTINGS: Partial<MySettings> = { enabled: true };
// In onload:
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
this.addSettingTab(new MySettingTab(this.app, this));
// Save changes:
await this.saveData(this.settings);
UI Components
- Ribbon icons:
this.addRibbonIcon('icon', 'tooltip', callback) - Status bar:
this.addStatusBarItem().setText('text')(desktop only) - Modals: Extend
ModalorSuggestModal/FuzzySuggestModal - Views: Extend
ItemView, register withregisterView() - Notices:
new Notice('message')
Events and Cleanup
Always use register* methods for automatic cleanup:
this.registerEvent(this.app.vault.on('create', callback));
this.registerDomEvent(document, 'click', callback);
this.registerInterval(window.setInterval(callback, 1000));
Development Workflow
Setup
# Clone sample plugin or create from template
npm install
npm run dev # Watch mode
npm run build # Production build
Testing
Copy build artifacts to vault:
<Vault>/.obsidian/plugins/<plugin-id>/
main.js
manifest.json
styles.css (optional)
Reload Obsidian and enable in Settings → Community plugins.
Debugging
- Open DevTools: Ctrl+Shift+I (Windows/Linux) or Cmd+Option+I (macOS)
- Use
console.log()for debugging - Check Console tab for errors
Manifest (manifest.json)
Required fields:
{
"id": "plugin-id",
"name": "Plugin Name",
"version": "1.0.0",
"minAppVersion": "1.0.0",
"description": "What it does",
"author": "Your Name",
"isDesktopOnly": false
}
Rules:
- Never change
idafter release - Use semantic versioning (x.y.z)
- Keep
minAppVersionaccurate - Set
isDesktopOnly: trueonly if using desktop-only APIs
Best Practices
Performance
- Keep startup light - defer heavy work
- Lazy-initialize expensive resources
- Batch disk access, debounce file events
Security & Privacy
- Default to local/offline operation
- No hidden telemetry - require explicit opt-in
- Never execute remote code
- Disclose external services used
- Minimize vault access scope
Code Quality
- TypeScript with
"strict": true - Bundle everything into main.js
- Use async/await, handle errors gracefully
- Keep main.ts minimal, split into modules
Mobile Compatibility
- Test on iOS and Android if
isDesktopOnly: false - Avoid desktop-only APIs (Node, Electron)
- Mind memory constraints
Common Patterns
File Operations
// Read file
const content = await this.app.vault.read(file);
// Write file
await this.app.vault.modify(file, newContent);
// Create file
await this.app.vault.create(path, content);
// Get active file
const file = this.app.workspace.getActiveFile();
Editor Operations
// In editorCallback:
const selection = editor.getSelection();
editor.replaceSelection(newText);
editor.getCursor(); // { line, ch }
editor.setLine(lineNum, text);
Workspace
// Get active view
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
// Open file
await this.app.workspace.openLinkText(path, '');
// Get leaves of type
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE);
Additional Resources
Reference Files
For detailed API patterns and code examples, consult:
Core Development:
references/agents-guide.md- Comprehensive AI-oriented development guide with coding conventions, file structure, common tasks, and do/don't guidelinesreferences/ui-components.md- Detailed UI component examples (commands, views, modals, settings)references/publishing.md- Publishing workflow and community submission process
Vault & Files:
references/vault-api.md- File operations, TFile/TFolder, read/cachedRead, process(), vault events
Editor & Markdown:
references/editor-extensions.md- CodeMirror 6 state fields, view plugins, decorations, widgetsreferences/markdown-processing.md- Post processors, code block processors for rendering content
UI & Styling:
references/views.md- Complete views guide: ItemView, workspace, leaves, deferred views, state serializationreferences/icons.md- Lucide icons, setIcon(), addIcon() for custom SVGs, sizingreferences/status-bar.md- Status bar items, icons, dynamic updates (desktop only)references/context-menus.md- Menu class, file-menu/editor-menu events, submenusreferences/html-styling.md- createEl() helpers, CSS variables, dynamic stylingreferences/rtl-support.md- Right-to-left language support, logical CSS properties, internationalization
Frameworks:
references/frameworks.md- React integration overview, mounting in viewsreferences/svelte.md- Comprehensive Svelte 5 guide with runes, components, and patterns
Advanced:
references/performance.md- onLayoutReady, startup optimization, deferred views, pitfallsreferences/multi-window.md- Pop-out window support, element.doc/win, instanceOf()references/secrets.md- SecretStorage API for secure API key/token storage
Testing & Development:
references/testing-guide.md- Testing workflow, debugging, mobile testing, pre-release checklist
Example Files
Working examples in examples/:
examples/sample-main.ts- Complete main.ts with all common patternsexamples/sample-settings.ts- Settings interface and tab implementationexamples/sample-view.ts- Custom ItemView with state persistence, actions, and filteringexamples/sample-editor-extension.ts- CodeMirror 6 state fields, view plugins, decorationsexamples/sample-markdown-processor.ts- Post processors, code block processors, widgets