Provides development guidelines for creating Obsidian plugins, including boilerplate generation and API best practices.

Install

mkdir -p .claude/skills/obsidian && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/248" && unzip -o skill.zip -d .claude/skills/obsidian && rm skill.zip

Installs to .claude/skills/obsidian

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Comprehensive guidelines for Obsidian.md plugin development including ESLint rules from eslint-plugin-obsidianmd v0.4.1, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI/UX standards, popout window compatibility, community.obsidian.md submission process, and Scorecard optimization. Use when working with Obsidian plugins, main.ts files, manifest.json, Plugin class, MarkdownView, TFile, vault operations, or any Obsidian API development.
471 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Generate new Obsidian plugin project boilerplate
  • Validate plugin IDs and names against community rules
  • Manage event listeners for automatic cleanup
  • Use Editor API for active file edits
  • Check `minAppVersion` for API availability
  • Ensure popout window compatibility with `activeWindow`

How it works

The skill provides guidelines derived from official Obsidian ESLint plugin rules and submission requirements. It offers an interactive boilerplate generator for new plugin projects.

Inputs & outputs

You give it
User request to create a new plugin or a plugin code snippet
You get back
Generated boilerplate code or suggested code improvements based on guidelines

When to use obsidian

  • Generate a new Obsidian plugin project boilerplate
  • Validate plugin naming against community rules
  • Optimize plugin code for memory and performance
  • Prepare plugin for submission to community.obsidian.md

About this skill

Obsidian Plugin Development Guidelines

Follow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices.

Getting Started

Quick Start Tool

For new plugin projects, an interactive boilerplate generator is available:

  • Script: tools/create-plugin.js in the skill repository
  • Command: Invoke create-plugin using your agent's method (/create-plugin, $create-plugin, or @create-plugin)
  • Generates minimal, best-practice boilerplate with no sample code
  • Detects existing projects and only adds missing files

Recommend the boilerplate generator when users ask how to create a new plugin, want to start a new project, or need help setting up the basic structure.


Rules Reference (eslint-plugin-obsidianmd v0.4.1)

Submission & Naming

#Rule✅ Do❌ Don't
1Plugin IDOmit "obsidian"; don't end with "plugin"Include "obsidian" or end with "plugin"
2Plugin nameOmit "Obsidian"; don't end with "Plugin"Include "Obsidian" or end with "Plugin"
3Plugin nameDon't start with "Obsi" or end with "dian"Start with "Obsi" or end with "dian"
4DescriptionOmit "Obsidian", "This plugin", etc.Use "Obsidian" or "This plugin"
5DescriptionEnd with .?!) punctuationLeave description without terminal punctuation

Memory & Lifecycle

#Rule✅ Do❌ Don't
6Event cleanupUse registerEvent() for automatic cleanupRegister events without cleanup
6aDOM eventsUse registerDomEvent() on the plugin or owning componentPair addEventListener with manual removeEventListener cleanup
7View referencesReturn views/components directlyStore view references in plugin properties or pass plugin as component to MarkdownRenderer
8Leaf detachmentLet Obsidian handle leaf cleanupCall detachLeavesOfType() in onunload

Type Safety

#Rule✅ Do❌ Don't
9TFile/TFolderUse instanceof for type checkingCast to TFile/TFolder; use any; use var
10DOM instanceofUse .instanceOf(T) for DOM Nodes/UIEventsUse instanceof for cross-window DOM checks

UI/UX

#Rule✅ Do❌ Don't
11UI textSentence case — "Advanced settings"Title Case — "Advanced Settings"
12JSON localeSentence case in JSON locale files (recommendedWithLocalesEn)Title case in locale JSON
13TS/JS localeSentence case in TS/JS locale modulesTitle case in locale modules

Note (v0.4.0): ui/sentence-case is now enabled (warn) and enforced on inline UI strings — it was disabled in v0.3.0. Use the recommendedWithLocalesEn config to also check English locale files (rules 12–13). | 14 | Command names | Omit "command" in command names/IDs | Include "command" in names/IDs | | 15 | Command IDs | Omit plugin ID/name from command IDs/names | Duplicate plugin ID in command IDs | | 16 | Hotkeys | No default hotkeys | Set default hotkeys | | 17 | Settings headings | Use .setHeading() | Create manual HTML headings; use "General", "settings", or plugin name in headings |

Declarative Settings (1.13.0+)

All four settings-tab rules ship as warn in recommended. Rules 17a/17c/17d read minAppVersion from manifest.json; 17b is not version-gated.

#Rule✅ Do❌ Don't
17asettings-tab/require-displayKeep display() when minAppVersion < 1.13.0Ship declarative-only settings that render nothing on older Obsidian
17bsettings-tab/prefer-setting-definitionsImplement getSettingDefinitions() on every PluginSettingTabRely on display() alone — settings won't appear in 1.13+ global search
17csettings-tab/prefer-update-over-displayCall this.update() to re-render declarative settingsCall this.display() — it's bypassed when definitions are non-empty
17dsettings-tab/no-deprecated-displayDelete display() once minAppVersion >= 1.13.0 and definitions existLeave a dead display() behind (auto-fixable)
Settings dataKeep all persisted data inside plugin.settingsStore sibling keys via saveData() — auto-persist clobbers them

Detection caveat: these rules match a bare extends PluginSettingTab only. extends obsidian.PluginSettingTab is out of scope and won't be flagged — but the underlying guidance still applies.

API Best Practices

#Rule✅ Do❌ Don't
18Active file editsUse Editor APIUse Vault.modify() for active file edits
19Background file modsUse Vault.process()Use Vault.modify() for background modifications
20File deletionUse FileManager.trashFile()Use Vault.trash() or Vault.delete() directly
21File lookupUse Vault.getAbstractFileByPath()Iterate all files with Vault.getFiles().find()
22User pathsUse normalizePath()Hardcode .obsidian path; use raw user paths
23OS detectionUse Platform APIUse navigator.platform/userAgent
24Network requestsUse requestUrl()Use fetch()
25LoggingMinimize console logging; none in onload/onunload in productionUse console.log in onload/onunload
26Input suggestUse built-in AbstractInputSuggestCopy Liam's TextInputSuggest implementation
27API compatibilityCheck minAppVersion for API availability (e.g., getSettingDefinitions() requires 1.13.0)Use APIs not available in declared minAppVersion
28Language detectionUse Obsidian's getLanguage()Use localStorage.getItem('language') or i18next-browser-languagedetector

Popout Window Compatibility

#Rule✅ Do❌ Don't
29Document/WindowUse activeDocument and activeWindowUse global document and window
29aGetter captureCapture activeDocument in a variable when the same document is needed laterCall activeDocument at setup and again at cleanup — it follows focus and may return different documents
30TimersUse activeWindow.setTimeout(), setInterval(), etc.Use bare setTimeout(), setInterval()
31Main workspace UIUse this.app.workspace.containerEl.ownerDocument from settingsUse activeDocument to update main workspace from settings window

Note (v0.4.0): prefer-active-doc remains disabled by default — the only Obsidian rule shipped as off. Enable it manually for popout window support.

Note (v1.13.0): Settings now open in a new window. activeDocument from settings callbacks points to the settings window, not the main vault. Use this.app.workspace.containerEl.ownerDocument to target main workspace UI.

Note: activeDocument/activeWindow are dynamic getters that track the focused window. A listener added via activeDocument.addEventListener() at setup cannot reliably be removed via activeDocument.removeEventListener() at cleanup. Prefer registerDomEvent() (rule 6a), which captures the target at registration.

Event Handling

#Rule✅ Do❌ Don't
31Editor drop/pasteCheck evt.defaultPrevented and call evt.preventDefault()Handle editor-drop/paste without checking defaultPrevented

Styling

#Rule✅ Do❌ Don't
32CSS variablesUse Obsidian CSS variables for all stylingHardcode colors, sizes, or spacing
33CSS scopeScope CSS to plugin containersUse broad CSS selectors
34Style elementsUse styles.css file (no-forbidden-elements)Create <link> or <style> elements; assign styles via JavaScript
34a!importantIncrease selector specificity or use CSS variablesUse !important — overrides user themes/snippets
34b:has selectorToggle classes from TypeScript when conditions changeUse :has — causes broad selector invalidation and performance issues

Security & Compatibility

#Rule✅ Do❌ Don't
35DOM creationUse Obsidian DOM helpers (createEl(), createDiv(), createSpan(), createSvg(), createFragment()) via prefer-create-el; linter autofixes activeDocument.createElement()activeWindow.createEl() (v0.4.1)Use document.createElement(), document.createDocumentFragment(), etc.
36Node.js modulesGuard Node.js imports with Platform.isDesktop check (no-nodejs-modules)Import Node.js modules without platform guard
37iOS compatAvoid regex lookbehind (iOS < 16.4 incompatibility)Use regex lookbehind

Accessibility (MANDATORY)

#Rule✅ Do❌ Don't
38Keyboard accessMake all interactive elements keyboard accessible; Tab through all elementsCreate inaccessible interactive elements
39ARIA labelsProvide ARIA labels for icon buttons; use data-tooltip-position for tooltipsUse icon buttons without ARIA labels
40Focus indicatorsUse :focus-visible with Obsidian CSS variables; touch targets ≥ 44×44pxRemove focus indicators; make touch targets < 44×44px

Code Quality

Rule✅ Do❌ Don't
Sample codeRemove all sample/template codeKeep class names like MyPlugin, SampleModal
Object.assignObject.assign({}, defaults, overrides) (object-assign)Object.assign(defaultsVar, other) — mutates defaults
LICENSECopyright holder must not be "Dynalist Inc."; year must be current (validate-license)Leave "Dynalist Inc." as holder or use an outdated year
AsyncUse async/awaitUse Promise chains
Deprecated packagesReplace flagged npm packages with Node.js built-ins (e.g.,

Content truncated.

When not to use it

  • When developing plugins for platforms other than Obsidian.md
  • When the project does not involve Obsidian API development

Limitations

  • Guidelines are specific to Obsidian.md plugin development
  • Rules are based on eslint-plugin-obsidianmd v0.4.1

How it compares

This skill provides specific, documented rules and a generator for Obsidian plugin development, unlike a manual approach that would require consulting multiple external documents.

Compared to similar skills

obsidian side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
obsidian (this skill)2927dNo flagsIntermediate
cursor-rules-config427dReviewIntermediate
drizzle2382moNo flagsIntermediate
zustand1132moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

cursor-rules-config

jeremylongshore

Configure .cursorrules for project-specific AI behavior. Triggers on "cursorrules", ".cursorrules", "cursor rules", "cursor config", "cursor project settings". Use when configuring systems or services. Trigger with phrases like "cursor rules config", "cursor config", "cursor".

428

drizzle

lobehub

Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.

238873

zustand

lobehub

Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.

113434

motion-canvas

davila7

Complete production-ready guide for Motion Canvas with ESM/CommonJS workarounds, full setup templates, and troubleshooting for programmatic video creation using TypeScript

58202

turborepo

vercel

Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries. Use when user: configures tasks/workflows/pipelines, creates packages, sets up monorepo, shares code between apps, runs changed/affected packages, debugs cache, or has apps/packages directories.

61191

shadcn-ui-setup

maneeshanif

Install and configure Shadcn/ui component library with Radix UI primitives, Aceternity UI effects, set up components, and manage the component registry. Use when adding Shadcn/ui to a Next.js project or installing specific UI components for Phase 2.

37194

Search skills

Search the agent skills registry