Skip to main content
Get up and running with your first Obsidian plugin using the official API.

Prerequisites

Node.js

Install Node.js 16 or higher

TypeScript

Basic TypeScript knowledge recommended

Obsidian

Obsidian app installed on your system

Code Editor

VS Code or your preferred editor

Quick Start

1

Clone the Sample Plugin

Start with the official Obsidian plugin template:
git clone https://github.com/obsidianmd/obsidian-sample-plugin.git my-plugin
cd my-plugin
npm install
2

Install the API Types

The sample plugin already includes the Obsidian API types in its dependencies:
package.json
{
  "devDependencies": {
    "obsidian": "latest",
    "@types/node": "^16.11.6",
    "typescript": "4.4.4"
  }
}
The types are automatically installed when you run npm install.
3

Understand the Plugin Structure

The sample plugin includes these key files:
main.ts
import { App, Plugin, PluginSettingTab } from 'obsidian';

export default class MyPlugin extends Plugin {
    async onload() {
        console.log('Loading plugin');
        
        // Add a ribbon icon
        this.addRibbonIcon('dice', 'Sample Plugin', () => {
            console.log('Ribbon icon clicked');
        });

        // Add a command
        this.addCommand({
            id: 'open-sample-modal',
            name: 'Open Sample Modal',
            callback: () => {
                console.log('Command executed');
            }
        });
    }

    async onunload() {
        console.log('Unloading plugin');
    }
}
manifest.json
{
    "id": "sample-plugin",
    "name": "Sample Plugin",
    "version": "1.0.0",
    "minAppVersion": "0.15.0",
    "description": "This is a sample plugin for Obsidian.",
    "author": "Your Name",
    "authorUrl": "https://yourwebsite.com",
    "isDesktopOnly": false
}
4

Build the Plugin

Compile your TypeScript code to JavaScript:
npm run build
This creates main.js in your project directory.
5

Test in Obsidian

Copy your plugin to Obsidian’s plugin directory:
cp -r . "%APPDATA%\Obsidian\plugins\my-plugin"
Then in Obsidian:
  1. Open Settings
  2. Navigate to Community Plugins
  3. Enable your plugin

Add Your First Feature

Let’s add a command that creates a new note:
1

Import Required APIs

main.ts
import { App, Plugin, TFile, Notice } from 'obsidian';
2

Add the Command

main.ts
export default class MyPlugin extends Plugin {
    async onload() {
        this.addCommand({
            id: 'create-note',
            name: 'Create New Note',
            callback: async () => {
                await this.createNote();
            }
        });
    }

    async createNote() {
        const fileName = `Note ${Date.now()}.md`;
        const content = `# New Note\n\nCreated at ${new Date().toLocaleString()}`;
        
        try {
            const file = await this.app.vault.create(fileName, content);
            new Notice(`Created ${fileName}`);
            
            // Open the new file
            const leaf = this.app.workspace.getLeaf(false);
            await leaf.openFile(file);
        } catch (error) {
            new Notice('Failed to create note');
            console.error(error);
        }
    }
}
3

Rebuild and Test

npm run build
Reload your plugin in Obsidian (Settings → Community Plugins → Reload) and test the command!

Development Workflow

Watch Mode

npm run dev
Automatically rebuilds on file changes

Hot Reload

Use the Hot Reload plugin to automatically reload your plugin during development

Console Logs

Open Developer Tools (Ctrl+Shift+I) to see console output

Debugging

Use debugger statements and browser DevTools for debugging

Common Patterns

Access the Vault

// Read a file
const file = this.app.vault.getAbstractFileByPath('path/to/file.md');
if (file instanceof TFile) {
    const content = await this.app.vault.read(file);
}

// Create a file
await this.app.vault.create('new-file.md', 'Content here');

// Modify a file
await this.app.vault.modify(file, 'New content');

Work with the Editor

this.addCommand({
    id: 'insert-text',
    name: 'Insert Text',
    editorCallback: (editor, view) => {
        editor.replaceSelection('Inserted text');
    }
});

Add Settings

interface MyPluginSettings {
    settingValue: string;
}

const DEFAULT_SETTINGS: MyPluginSettings = {
    settingValue: 'default'
}

export default class MyPlugin extends Plugin {
    settings: MyPluginSettings;

    async onload() {
        await this.loadSettings();
        this.addSettingTab(new MySettingTab(this.app, this));
    }

    async loadSettings() {
        this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
    }

    async saveSettings() {
        await this.saveData(this.settings);
    }
}

Next Steps

Core Concepts

Learn about the plugin lifecycle and architecture

API Reference

Explore the full Plugin API documentation

Guides

Follow step-by-step guides for common tasks

Examples

See real-world plugin examples

Get Help

Join the Community

Connect with other plugin developers on the Obsidian forum

Build docs developers (and LLMs) love