Obsidian Plugin Development Cheat Sheet #
A comprehensive guide to developing plugins for Obsidian, from setup to release.
π Getting Started #
Prerequisites #
- Git: https://git-scm.com/
- Node.js: https://nodejs.org/
- Code Editor: VS Code is recommended.
Development Vault #
- ALWAYS use a separate, dedicated vault for plugin development to avoid data loss in your main vault.
- Create a new, empty vault for development purposes.
Sample Plugin Setup #
-
Clone the sample plugin into your development vault’s
.obsidian/pluginsdirectory:cd /path/to/your/dev-vault/.obsidian/plugins git clone https://github.com/obsidianmd/obsidian-sample-plugin.git your-plugin-name -
Install dependencies:
cd your-plugin-name npm install -
Start the development server:
npm run devThis command watches for changes in your
.tsfiles and automatically compiles them intomain.js. -
Enable the plugin in Obsidian:
- Go to
Settings>Community plugins. - Turn on community plugins.
- Enable your new plugin under
Installed plugins.
- Go to
ποΈ Core Concepts #
manifest.json
#
This file contains metadata about your plugin. Restart Obsidian after making changes to this file.
id: A unique identifier for your plugin (e.g.,hello-world).name: The human-friendly name of your plugin.author: Your name.version: The current version of your plugin.minAppVersion: The minimum version of Obsidian required.description: A short description of your plugin.isDesktopOnly: Set totrueif your plugin uses Node.js or Electron APIs.
main.ts - The Plugin Class
#
This is the main entry point for your plugin. It must export a default class that extends Plugin.
import { Plugin } from 'obsidian';
export default class MyPlugin extends Plugin {
async onload() {
// Plugin startup logic here
}
onunload() {
// Plugin cleanup logic here
}
}
Plugin Lifecycle #
onload(): This method is called when your plugin is enabled. Use it to set up UI elements, register event listeners, and add commands.onunload(): This method is called when your plugin is disabled. Use it to clean up any resources created inonload()to prevent memory leaks.
π§© API Fundamentals #
Access the Obsidian API via this.app from within your plugin class.
this.app.vault: Interact with files and folders in the vault.getFiles(): Get all files in the vault.getAbstractFileByPath(path): Get a file or folder by its path.create(path, content): Create a new file.modify(file, content): Modify an existing file.process(file, (data) => newData): Atomically modify a file.
this.app.workspace: Manage the visual layout of Obsidian.getActiveViewOfType(viewType): Get the active view of a specific type (e.g.,MarkdownView).getLeaf(): Get a new workspace leaf.updateOptions(): Force a refresh of editor options.
this.app.metadataCache: Access cached metadata for Markdown files.getFileCache(file): Get cached information like headings, links, and tags.
π¨ UI Development #
Ribbon Icons #
Add an icon to the left-hand ribbon.
this.addRibbonIcon('dice', 'My Plugin Action', (evt: MouseEvent) => {
new Notice('Hello, world!');
});
Status Bar Items #
Add an element to the status bar at the bottom.
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status Bar Text');
Commands #
Add a command to the command palette.
this.addCommand({
id: 'my-command',
name: 'My Command',
callback: () => {
// Command logic here
},
// Use checkCallback for conditional commands
checkCallback: (checking: boolean) => {
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (markdownView) {
if (!checking) {
// Execute the command
}
return true;
}
return false;
}
});
Settings Tab #
Create a settings tab for your plugin.
import { App, PluginSettingTab, Setting } from 'obsidian';
interface MyPluginSettings {
mySetting: string;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
mySetting: 'default'
}
class MySettingTab extends PluginSettingTab {
// ... (implementation in main.ts)
}
πΎ Data Persistence #
Save and load plugin data using loadData() and saveData().
interface MyPluginSettings {
mySetting: string;
}
const DEFAULT_SETTINGS: Partial<MyPluginSettings> = {
mySetting: 'default'
};
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
await this.loadSettings();
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
β Best Practices #
- Resource Management: Use
this.register...methods (registerEvent,registerDomEvent,registerInterval) to automatically clean up resources when the plugin is unloaded. - Security: Avoid using
innerHTMLorouterHTML. UsecreateEl,createDiv, etc., to build DOM elements programmatically. - Styling: Use CSS classes instead of inline styles. Use Obsidian’s CSS variables for consistency.
- File Operations: Prefer the Vault API (
app.vault) over the Adapter API (app.vault.adapter). UseVault.process()for background file modifications. - Async/Await: Prefer
async/awaitover Promises for cleaner asynchronous code.
π¦ Releasing Your Plugin #
- Update
manifest.json: Increment theversionnumber. - Update
versions.json: Add the new version and the minimum required Obsidian version. - Create a GitHub Release: Tag the release with the exact version number (e.g.,
1.0.1). - Upload Assets: Attach
main.js,styles.css(if any), andmanifest.jsonto the release. - Submit to Community List: Create a pull request to the obsidian-releases repository.