// Comprehensive guidelines for Obsidian.md plugin development including all 27 ESLint rules, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI/UX standards, and submission requirements. Use when working with Obsidian plugins, main.ts files, manifest.json, Plugin class, MarkdownView, TFile, vault operations, or any Obsidian API development.
| name | obsidian |
| version | 1.1.0 |
| description | Comprehensive guidelines for Obsidian.md plugin development including all 27 ESLint rules, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI/UX standards, and submission requirements. Use when working with Obsidian plugins, main.ts files, manifest.json, Plugin class, MarkdownView, TFile, vault operations, or any Obsidian API development. |
You are assisting with Obsidian plugin development. Follow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices.
For new plugin projects, an interactive boilerplate generator is available:
tools/create-plugin.js in the skill repository/create-plugin for guided setupRecommend the boilerplate generator when users:
Submission & Naming:
.?!) punctuation - Validation bot enforcedMemory & Lifecycle:
6. Use registerEvent() for automatic cleanup - Prevents memory leaks
7. Don't store view references in plugin - Causes memory leaks
Type Safety:
8. Use instanceof instead of type casting - Type safety for TFile/TFolder
UI/UX:
9. Use sentence case for all UI text - "Advanced settings" not "Advanced Settings"
10. No "command" in command names/IDs - Redundant
11. No plugin ID in command IDs - Obsidian auto-namespaces
12. No default hotkeys - Avoid conflicts
13. Use .setHeading() for settings headings - Not manual HTML
API Best Practices:
14. Use Editor API for active file edits - Preserves cursor position
15. Use Vault.process() for background file mods - Prevents conflicts
16. Use normalizePath() for user paths - Cross-platform compatibility
17. Use Platform API for OS detection - Not navigator
18. Use requestUrl() instead of fetch() - Bypasses CORS restrictions
19. No console.log in onload/onunload in production - Pollutes console
Styling: 20. Use Obsidian CSS variables - Respects user themes 21. Scope CSS to plugin containers - Prevents style conflicts
Accessibility (MANDATORY):
22. Make all interactive elements keyboard accessible - Accessibility required
23. Provide ARIA labels for icon buttons - Accessibility required
24. Define clear focus indicators - Use :focus-visible
Security & Compatibility:
25. Don't use innerHTML/outerHTML - Security risk (XSS)
26. Avoid regex lookbehind - iOS < 16.4 incompatibility
Code Quality: 27. Remove all sample/template code - MyPlugin, SampleModal, etc.
For comprehensive information on specific topics, see the reference files:
registerEvent(), addCommand(), registerDomEvent(), registerInterval()instanceof instead of type castingany typeconst and let over varMemory & Lifecycle:
registerEvent(), addCommand(), registerDomEvent(), registerInterval()Type Safety:
instanceof for type checking (not type casting)unknown instead of anyconst and let (not var)API Usage:
this.app (not global app)Vault.process() for background file modificationsFileManager.processFrontMatter() for YAMLfileManager.trashFile() for deletionsnormalizePath() for user-defined pathsPlatform API for OS detectionAbstractInputSuggest for autocompleterequestUrl() instead of fetch() for network requestsUI/UX:
.setHeading() for settings headingscreateDiv(), createSpan(), createEl())window.setTimeout/setInterval with number typeStyling:
Accessibility (MANDATORY):
:focus-visibledata-tooltip-position for tooltipsCode Quality:
Memory & Lifecycle:
onunload()Type Safety:
instanceof)any typevarAPI Usage:
app objectVault.modify() for active file edits.obsidian path (use vault.configDir)navigator.platform/userAgent (use Platform API)fetch() (use requestUrl() instead)UI/UX:
.setHeading())Styling:
<link> or <style> elements (use styles.css file)Security & Compatibility:
innerHTML/outerHTML (XSS risk)Accessibility:
Code Quality:
document.createElement (use Obsidian helpers)Use this checklist for code review and implementation:
instanceof instead of casts?:focus-visible and proper CSS variables?// โ
CORRECT
this.addCommand({
id: 'insert-timestamp',
name: 'Insert timestamp',
editorCallback: (editor: Editor, view: MarkdownView) => {
editor.replaceSelection(new Date().toISOString());
}
});
// โ
CORRECT
const file = this.app.vault.getAbstractFileByPath(path);
if (file instanceof TFile) {
// TypeScript now knows it's a TFile
await this.app.vault.read(file);
}
// โ
CORRECT
const button = containerEl.createEl('button', {
attr: {
'aria-label': 'Open settings',
'data-tooltip-position': 'top'
}
});
button.setText('โ๏ธ');
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
performAction();
}
});
/* โ
CORRECT */
.my-plugin-modal {
background: var(--modal-background);
color: var(--text-normal);
padding: var(--size-4-4);
border-radius: var(--radius-m);
font-size: var(--font-ui-medium);
}
.my-plugin-button:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
}
eslint-plugin-obsidianmd (install for automatic checking)eslint-plugin-obsidianmd which is under active development--fix flagWhen helping with Obsidian plugin development, proactively apply these rules and suggest improvements based on these guidelines. Refer to the detailed reference files for comprehensive information on specific topics.