| name | obsidian-webhooks-events |
| description | Handle Obsidian events and workspace callbacks for plugin development.
Use when implementing reactive features, handling file changes,
or responding to user interactions in your plugin.
Trigger with phrases like "obsidian events", "obsidian callbacks",
"obsidian file change", "obsidian workspace events".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Obsidian Webhooks & Events
Overview
Comprehensive guide to handling events in Obsidian plugins - workspace events, vault events, and custom event patterns.
Prerequisites
- Understanding of Obsidian plugin lifecycle
- Familiarity with TypeScript event patterns
- Basic plugin setup completed
Event Categories
Available Events
| Category | Events | Use Case |
|---|
| Workspace | file-open, active-leaf-change, layout-change | UI state changes |
| Vault | create, modify, delete, rename | File system changes |
| MetadataCache | changed, resolved | Metadata/link updates |
| Editor | change, cursor-activity | Text editing |
Instructions
Step 1: Workspace Events
import { Plugin, TFile, WorkspaceLeaf } from 'obsidian';
export default class MyPlugin extends Plugin {
async onload() {
this.registerEvent(
this.app.workspace.on('file-open', (file: TFile | null) => {
if (file) {
console.log('Opened file:', file.path);
this.onFileOpen(file);
}
})
);
this.registerEvent(
this.app.workspace.on('active-leaf-change', (leaf: WorkspaceLeaf | null) => {
if (leaf) {
console.log(, leaf..());
}
})
);
.(
...(, {
.();
.();
})
);
.(
...(, {
view = ...();
(view) {
mode = view.();
.(, mode);
}
})
);
.(
...(, {
.();
})
);
}
() {
}
() {
}
}
Step 2: Vault Events
import { Plugin, TFile, TFolder, TAbstractFile } from 'obsidian';
export default class MyPlugin extends Plugin {
async onload() {
this.registerEvent(
this.app.vault.on('create', (file: TAbstractFile) => {
if (file instanceof TFile) {
console.log('File created:', file.path);
this.onFileCreated(file);
} else if (file instanceof TFolder) {
console.log('Folder created:', file.path);
}
})
);
this.registerEvent(
this.app.vault.(, {
(file ) {
.(, file.);
.(file);
}
})
);
.(
...(, {
.(, file.);
.(file);
})
);
.(
...(, {
.();
.(file, oldPath);
})
);
}
() {
}
() {
}
() {
}
() {
}
}
Step 3: MetadataCache Events
import { Plugin, TFile, CachedMetadata } from 'obsidian';
export default class MyPlugin extends Plugin {
async onload() {
this.registerEvent(
this.app.metadataCache.on('changed', (file: TFile, data: string, cache: CachedMetadata) => {
console.log('Metadata changed:', file.path);
if (cache.frontmatter) {
console.log('Frontmatter:', cache.frontmatter);
}
if (cache.tags) {
const tags = cache.tags.map(t => t.tag);
console.(, tags);
}
(cache.) {
links = cache..( l.);
.(, links);
}
})
);
.(
...(, {
.();
.();
})
);
}
() {
allFiles = ...();
.();
}
}
Step 4: Debounced Event Handling
import { Plugin, TFile } from 'obsidian';
export class DebouncedEventHandler {
private modifyTimeout: Map<string, NodeJS.Timeout> = new Map();
private debounceMs: number;
private handler: (file: TFile) => void;
constructor(debounceMs: number, handler: (file: TFile) => void) {
this.debounceMs = debounceMs;
this.handler = handler;
}
handleModify(file: TFile) {
const existing = this.modifyTimeout.get(file.path);
if (existing) {
clearTimeout(existing);
}
const timeout = setTimeout( {
..(file.);
.(file);
}, .);
..(file., timeout);
}
() {
( timeout ..()) {
(timeout);
}
..();
}
}
{
: ;
() {
. = (
,
.(file)
);
.(
...(, {
(file ) {
..(file);
}
})
);
}
() {
..();
}
() {
.(, file.);
}
}
Step 5: Custom Event Emitter
import { Events } from 'obsidian';
interface MyPluginEvents {
'index-updated': (count: number) => void;
'sync-complete': (success: boolean, error?: string) => void;
'settings-changed': (key: string, value: any) => void;
}
export class PluginEventBus extends Events {
on<K extends keyof MyPluginEvents>(
event: K,
callback: MyPluginEvents[K],
ctx?: any
): EventRef {
return super.on(event as string, callback, ctx);
}
off<K extends keyof MyPluginEvents>(
event: K,
callback: MyPluginEvents[K]
): {
.(event , callback);
}
trigger<K keyof >(
: K,
...: <[K]>
): {
.(event , ...args);
}
}
eventBus = ();
eventBus.(, {
.();
});
eventBus.(, );
Step 6: File Watcher Pattern
import { Plugin, TFile } from 'obsidian';
interface WatchConfig {
folder?: string;
extension?: string;
includePattern?: RegExp;
excludePattern?: RegExp;
}
export class FileWatcher {
private plugin: Plugin;
private config: WatchConfig;
private callbacks: {
onCreate?: (file: TFile) => void;
onModify?: (file: TFile) => void;
onDelete?: (path: string) => void;
onRename?: (file: TFile, oldPath: string) => void;
};
constructor(
plugin: ,
: ,
: .prototype.callbacks
) {
. = plugin;
. = config;
. = callbacks;
.();
}
(: ): {
(.. && !path.(..)) {
;
}
(.. && !path.(..)) {
;
}
(.. && !...(path)) {
;
}
(.. && ...(path)) {
;
}
;
}
() {
(..) {
..(
....(, {
(file && .(file.)) {
..!(file);
}
})
);
}
(..) {
..(
....(, {
(file && .(file.)) {
..!(file);
}
})
);
}
(..) {
..(
....(, {
(.(file.)) {
..!(file.);
}
})
);
}
(..) {
..(
....(, {
(file && (.(file.) || .(oldPath))) {
..!(file, oldPath);
}
})
);
}
}
}
(
,
{
: ,
: ,
: ,
},
{
: .(, file.),
: .(, file.),
}
);
Output
- Workspace event handlers for UI state
- Vault event handlers for file operations
- MetadataCache handlers for content changes
- Debounced handlers for frequent events
- Custom event bus for internal communication
- Filtered file watchers
Error Handling
| Issue | Cause | Solution |
|---|
| Memory leak | Unregistered events | Use registerEvent |
| Performance issues | Too many handlers | Debounce frequent events |
| Missed events | Late registration | Register in onload |
| Stale data | Cache not updated | Use metadataCache.on('changed') |
Examples
Complete Event-Driven Plugin
export default class MyPlugin extends Plugin {
async onload() {
this.app.workspace.onLayoutReady(() => {
this.initializeAfterVaultReady();
});
}
private initializeAfterVaultReady() {
this.buildInitialIndex();
this.registerAllEvents();
}
private registerAllEvents() {
this.registerEvent(
this.app.vault.on('modify', this.handleModify.bind(this))
);
this.registerEvent(
this.app.workspace.on('file-open', this..())
);
}
() {
(file ) {
}
}
() {
(file) {
}
}
}
Resources
Next Steps
For performance optimization, see obsidian-performance-tuning.