Overview
The status bar displays information at the bottom of the editor. Plugins can add custom status bar items to show information or provide quick actions.
const statusBar = this.addStatusBarItem();
statusBar.setText('Word count: 0');
statusBar.setIcon('file-text');
IStatusBarItem Interface
interface IStatusBarItem {
id: string;
setText(text: string): this;
setIcon(icon: string): this;
setTitle(title: string): this;
addClass(className: string): this;
removeClass(className: string): this;
toggleClass(className: string, active: boolean): this;
setColor(color: string): this;
setOnClick(callback: () => void): this;
show(): this;
hide(): this;
remove(): void;
}
Creating Status Bar Items
Using Plugin.addStatusBarItem()
Create a status bar item. Automatically cleaned up when plugin unloads.
export default class MyPlugin extends Plugin {
private statusBarItem: IStatusBarItem | null = null;
async onload() {
this.statusBarItem = this.addStatusBarItem();
this.statusBarItem.setText('My Plugin');
this.statusBarItem.setIcon('star');
}
}
Using StatusBarManager
addItem
(pluginId: string, location?: 'left' | 'right') => IStatusBarItem
Add a status bar item with specific positioning.
const item = this.app.statusBarManager.addItem(
this.manifest.id,
'right' // or 'left'
);
Using Plugin.addStatusBarItem() is recommended as it handles cleanup automatically.
Methods
setText()
Set the text content of the status bar item.
statusBar.setText('100 words');
statusBar.setText('Modified 2 min ago');
statusBar.setText('Ready');
setIcon()
Set an icon (Lucide icon name) for the status bar item.
statusBar.setIcon('check-circle'); // Success icon
statusBar.setIcon('alert-circle'); // Warning icon
statusBar.setIcon('info'); // Info icon
statusBar.setIcon('file-text'); // Document icon
See Lucide Icons for available icons.
setTitle()
Set the tooltip text that appears on hover.
statusBar.setText('100');
statusBar.setTitle('Word count: 100 words');
addClass()
addClass
(className: string) => this
Add a CSS class to the status bar item.
statusBar.addClass('my-custom-style');
statusBar.addClass('status-warning');
removeClass()
removeClass
(className: string) => this
Remove a CSS class from the status bar item.
statusBar.removeClass('status-warning');
toggleClass()
toggleClass
(className: string, active: boolean) => this
Add or remove a CSS class based on a condition.
statusBar.toggleClass('status-active', this.isActive);
statusBar.toggleClass('status-error', hasError);
setColor()
Set the text color of the status bar item.
statusBar.setColor('#00ff00'); // Green
statusBar.setColor('red'); // Red
statusBar.setColor('var(--text-success)'); // Theme color
setOnClick()
setOnClick
(callback: () => void) => this
Register a click handler for the status bar item.
statusBar.setOnClick(() => {
this.showNotice('Status bar clicked!');
});
statusBar.setOnClick(() => {
this.toggleFeature();
this.updateStatusBar();
});
show() / hide()
Show the status bar item.
Hide the status bar item.
// Toggle visibility based on condition
if (this.plugin.settings.showStatusBar) {
statusBar.show();
} else {
statusBar.hide();
}
remove()
Remove the status bar item permanently.
If you used Plugin.addStatusBarItem(), the item is automatically removed when your plugin unloads. Only use remove() if you need to remove it earlier.
Word Count Plugin Example
From the built-in Word Count plugin:
export default class WordCountPlugin extends Plugin {
settings: WordCountSettings = DEFAULT_SETTINGS;
private statusBarItem: IStatusBarItem | null = null;
private updateInterval: number | null = null;
async onload() {
await this.loadSettings();
await this.createStatusBarItem();
this.addSettingTab(new WordCountSettingTab(this.app, this));
this.startUpdating();
}
async onunload() {
this.stopUpdating();
}
private async createStatusBarItem() {
this.statusBarItem = this.addStatusBarItem();
this.statusBarItem.addClass('word-count-status');
this.statusBarItem.setIcon('folder-heart');
this.updateWordCount();
}
private startUpdating() {
this.updateInterval = window.setInterval(() => {
this.updateWordCount();
}, 500);
}
private stopUpdating() {
if (this.updateInterval !== null) {
window.clearInterval(this.updateInterval);
this.updateInterval = null;
}
}
private updateWordCount() {
if (!this.statusBarItem) return;
const activeEditorView = this.app.editorRegistry.getActive();
if (!activeEditorView) {
this.statusBarItem.setText('');
return;
}
const content = activeEditorView.state.doc.toString();
const wordCount = this.countWords(content);
const charCount = this.countCharacters(content);
const parts: string[] = [];
if (this.settings.showWordCount) {
parts.push(`${wordCount} ${wordCount === 1 ? 'word' : 'words'}`);
}
if (this.settings.showCharCount) {
parts.push(`${charCount} ${charCount === 1 ? 'char' : 'chars'}`);
}
this.statusBarItem.setText(parts.join(' • '));
}
private countWords(text: string): number {
if (!text || text.trim().length === 0) return 0;
const words = text.split(/\s+/).filter(word => word.length > 0);
return words.length;
}
private countCharacters(text: string): number {
if (!text) return 0;
return this.settings.countSpaces ? text.length : text.replace(/\s/g, '').length;
}
}
Quick Finder Plugin Example
From the built-in Quick Finder plugin:
export default class QuickFinderPlugin extends Plugin {
private modal: QuickFinderModal | null = null;
async onload() {
this.addCommand({
id: 'quick-finder:open',
name: 'Open Quick Finder',
hotkey: ['Mod', 'o'],
callback: () => {
this.openQuickFinder();
},
});
this.initStatusBarItem();
}
private initStatusBarItem() {
const item = this.app.statusBarManager.addItem('quick-finder', 'right');
item.setIcon('search')
.setTitle('Quick Finder')
.setOnClick(() => {
this.openQuickFinder();
});
}
private openQuickFinder() {
if (this.modal) {
this.modal.close();
}
this.modal = new QuickFinderModal(this.app);
this.modal.open();
}
}
Common Patterns
Display Current State
private updateStatusBar() {
if (!this.statusBarItem) return;
const state = this.getCurrentState();
this.statusBarItem.setText(state.text);
this.statusBarItem.setIcon(state.icon);
this.statusBarItem.setColor(state.color);
}
Toggle Feature
private statusBarItem: IStatusBarItem;
private featureEnabled = false;
async onload() {
this.statusBarItem = this.addStatusBarItem();
this.statusBarItem.setIcon('power');
this.statusBarItem.setOnClick(() => this.toggleFeature());
this.updateStatusIcon();
}
private toggleFeature() {
this.featureEnabled = !this.featureEnabled;
this.updateStatusIcon();
}
private updateStatusIcon() {
this.statusBarItem.toggleClass('status-active', this.featureEnabled);
this.statusBarItem.setTitle(
`Feature ${this.featureEnabled ? 'enabled' : 'disabled'}`
);
}
Show Progress
private statusBarItem: IStatusBarItem;
async processFiles(files: TFile[]) {
for (let i = 0; i < files.length; i++) {
await this.processFile(files[i]);
this.statusBarItem.setText(
`Processing: ${i + 1}/${files.length}`
);
}
this.statusBarItem.setText('Complete');
setTimeout(() => {
this.statusBarItem.setText('');
}, 2000);
}
Display Sync Status
private statusBarItem: IStatusBarItem;
async onload() {
this.statusBarItem = this.addStatusBarItem();
this.statusBarItem.setIcon('cloud');
this.statusBarItem.setText('Synced');
this.registerEvent(
this.app.workspace.onFileModify(() => {
this.markAsModified();
})
);
}
private markAsModified() {
this.statusBarItem.setText('Modified');
this.statusBarItem.setColor('yellow');
}
private markAsSynced() {
this.statusBarItem.setText('Synced');
this.statusBarItem.setColor('green');
}
Conditional Display
private statusBarItem: IStatusBarItem;
async onload() {
this.statusBarItem = this.addStatusBarItem();
this.updateVisibility();
// Update when settings change
this.registerEvent(
this.app.workspace.onActiveFileChange(() => {
this.updateVisibility();
})
);
}
private updateVisibility() {
const file = this.app.workspaceUI.getActiveFile();
const shouldShow = file && this.shouldShowForFile(file);
if (shouldShow) {
this.statusBarItem.show();
this.updateContent();
} else {
this.statusBarItem.hide();
}
}
Best Practices
Keep Text Concise
// ✅ Good
statusBar.setText('150 words');
statusBar.setText('3 errors');
statusBar.setText('Synced');
// ❌ Bad - too verbose
statusBar.setText('Word count: 150 words');
statusBar.setText('Number of errors: 3');
statusBar.setText('Synchronization complete');
// ✅ Good
statusBar.setText('150 words');
statusBar.setTitle('Word count: 150 words, 800 characters');
// ❌ Bad - all info in text
statusBar.setText('150 words, 800 chars');
Clean Up Intervals
// ✅ Good
export default class MyPlugin extends Plugin {
private updateInterval: number | null = null;
async onload() {
this.updateInterval = window.setInterval(() => {
this.update();
}, 1000);
}
async onunload() {
if (this.updateInterval !== null) {
clearInterval(this.updateInterval);
this.updateInterval = null;
}
}
}
// ❌ Bad - interval not cleaned up
export default class MyPlugin extends Plugin {
async onload() {
window.setInterval(() => {
this.update();
}, 1000);
// Never cleared!
}
}
Handle Missing Data
// ✅ Good
private updateStatusBar() {
if (!this.statusBarItem) return;
const editor = this.app.editorRegistry.getActive();
if (!editor) {
this.statusBarItem.setText('');
return;
}
// Update with data
}
// ❌ Bad - assumes data exists
private updateStatusBar() {
const editor = this.app.editorRegistry.getActive();
const content = editor.getValue(); // May throw!
}
Use Appropriate Update Frequency
// ✅ Good - reasonable interval
this.updateInterval = window.setInterval(() => {
this.updateStatusBar();
}, 500); // 500ms
// ❌ Bad - too frequent
this.updateInterval = window.setInterval(() => {
this.updateStatusBar();
}, 10); // 10ms - wasteful!