Skip to Content
OtherDevelop Obsidian Plugins

Develop a Local Obsidian Plugin

Creating a custom Obsidian plugin allows you to extend the functionality of your vault, interact with the file system, or integrate external tools.

While there are starter templates available, building a plugin from scratch helps you understand the lifecycle, the event system, and the build process required to make TypeScript run inside Obsidian.


This guide covers the manual creation of a plugin that:

  1. Adds an entry to the File Explorer context menu (Right-click on file).
  2. Adds an entry to the Editor menu (The “3 dots” top-right).
  3. Executes a custom command.
Caution

Obsidian plugins run simply as JavaScript files. Because Obsidian relies on the Electron framework, your plugin has full Node.js access. Be careful when executing system commands (Child Process) or deleting files.

Prerequisites

  • Node.js  installed
  • A code editor (VS Code recommended)
  • An existing Obsidian Vault for testing
Note

TypeScript is mandatory. Obsidian’s API is typed, and trying to write a plugin in raw JavaScript is prone to errors and lacks autocomplete support.


Project Setup

We will initialize a minimal TypeScript project without relying on heavy bundlers first, to understand the core components.

Initialize the project

Create a new folder and initialize package.json.

Terminal
mkdir my-obsidian-plugin cd my-obsidian-plugin npm init -y

Install dependencies

We need the Obsidian API types and TypeScript.

Terminal
npm install obsidian npm install -D typescript tslib @types/node

Configure TypeScript (tsconfig.json)

Create a tsconfig.json file. This configuration is crucial to ensure the compiled code is compatible with Obsidian’s environment.

tsconfig.json
{ "compilerOptions": { "module": "CommonJS", "target": "ES6", "noImplicitAny": true, "moduleResolution": "node", "importHelpers": false, "lib": ["DOM", "ES5", "ES6", "ES7"] }, "include": ["**/*.ts"] }
  • importHelpers: false prevents the “tslib not found” error during runtime.
  • module: CommonJS is required for Obsidian to load the main.js.

Configure Build Script

Open package.json and add the build command under scripts.

package.json
"scripts": { "build": "tsc" }

The Plugin Logic

An Obsidian plugin consists of two main files: manifest.json (metadata) and main.ts (logic).

1. Create the Manifest

Create a file named manifest.json. The id must be unique and will be the folder name later.

manifest.json
{ "id": "my-custom-plugin", "name": "My Custom Plugin", "version": "1.0.0", "minAppVersion": "0.15.0", "description": "A demo plugin showing menu integrations.", "author": "Your Name", "isDesktopOnly": false }

2. Implement the Logic (main.ts)

This is where we hook into the UI. We will register events for both the File Menu and the Editor Menu.

main.ts
import { Plugin, TFile, Menu, Editor, MarkdownView, Notice } from 'obsidian'; export default class MyCustomPlugin extends Plugin { async onload() { console.log('Loading My Custom Plugin...'); // 1. Add item to the File Explorer Context Menu (Right-click on file) this.registerEvent( this.app.workspace.on("file-menu", (menu: Menu, file) => { // Ensure we only show this on Markdown files if (file instanceof TFile && file.extension === 'md') { menu.addItem((item) => { item .setTitle("My Custom Action") .setIcon("star") .onClick(async () => { new Notice(`Action triggered on file: ${file.basename}`); // Your custom logic here... }); }); } }) ); // 2. Add item to the Editor Menu ("3 dots" top-right) this.registerEvent( this.app.workspace.on("editor-menu", (menu: Menu, editor: Editor, view: MarkdownView) => { menu.addItem((item) => { item .setTitle("My Editor Action") .setIcon("pencil") .onClick(async () => { const currentFile = view.file; new Notice(`Action triggered in editor for: ${currentFile?.basename}`); }); }); }) ); } async onunload() { console.log('Unloading plugin'); } }

Build and Install

Since we are developing locally, we need to manually compile the TypeScript code and place it into the Obsidian vault’s hidden system folder.

1. Compile the code

Terminal
npm run build

This generates a main.js file in your project root.

2. Locate Plugin Folder

Navigate to your Obsidian Vault directory. You need to access the hidden .obsidian folder.

  • Path: YourVault/.obsidian/plugins/

3. Create Plugin Directory

Create a new folder inside plugins/ that matches exactly the id from your manifest.json.

  • Target: YourVault/.obsidian/plugins/my-custom-plugin/

4. Deploy Files

Copy the following files into that new folder:

  • main.js (The compiled code)
  • manifest.json (The metadata)
  • styles.css (Optional, if you have one)

Activation and Debugging

Enable the Plugin

  1. Open Obsidian Settings.
  2. Go to Community Plugins.
  3. Toggle “Restricted mode” OFF.
  4. Click Refresh installed plugins.
  5. Find “My Custom Plugin” and toggle it ON.

Debugging (Console)

If your plugin doesn’t load or throws errors, you need to look under the hood. Obsidian has Chrome DevTools built-in.

  • Windows/Linux: Ctrl + Shift + I
  • macOS: Cmd + Opt + I

Go to the Console tab to see your console.log outputs or error messages (red text).

Tip

Hot Reloading: While developing, repeating the “Build -> Copy -> Reload” cycle is tedious. You can install the “Hot Reload” community plugin in Obsidian. Then, you can simply run tsc -w (watch mode) in your terminal, and output directly to the vault folder.

Created: 25.01.2026

Last Updated: 25.01.2026