Skip to main content

Overview

Notices are temporary toast notifications that appear at the top of the screen. Use them to provide feedback to users.
import { Notice } from '@inkdown/api';

new Notice('File saved successfully!');

Notice Class

Constructor

class Notice {
  constructor(message: string, duration?: number);
  setMessage(message: string): this;
  hide(): void;
}
message
string
required
The message to display.
duration
number
How long to show the notice in milliseconds. Default is 3000 (3 seconds). Set to 0 for indefinite.

Basic Usage

// Simple notice (3 second duration)
new Notice('Hello!');

// Custom duration (5 seconds)
new Notice('This will stay longer', 5000);

// Indefinite (must be manually closed)
const notice = new Notice('Click to dismiss', 0);

Plugin Helper Method

The Plugin class provides a convenience method:
class Plugin {
  showNotice(message: string, duration?: number): Notice;
}
export default class MyPlugin extends Plugin {
  async onload() {
    this.showNotice('Plugin loaded!');
    
    this.addCommand({
      id: 'my-command',
      name: 'My Command',
      callback: () => {
        this.showNotice('Command executed', 2000);
      },
    });
  }
}

Methods

setMessage()

setMessage
(message: string) => this
Update the notice message.
const notice = new Notice('Processing...');

setTimeout(() => {
  notice.setMessage('Almost done...');
}, 2000);

setTimeout(() => {
  notice.setMessage('Complete!');
}, 4000);

hide()

hide
() => void
Manually hide the notice.
const notice = new Notice('Click to dismiss', 0);

// Hide after user action
notice.hide();

Common Use Cases

Success Messages

this.addCommand({
  id: 'save-file',
  name: 'Save File',
  callback: async () => {
    try {
      await this.saveFile();
      new Notice('File saved successfully!');
    } catch (error) {
      new Notice('Failed to save file');
    }
  },
});

Error Messages

try {
  await this.processData();
  new Notice('Processing complete');
} catch (error) {
  console.error('Processing failed:', error);
  new Notice('Processing failed: ' + error.message, 5000);
}

Progress Updates

async processFiles() {
  const files = await this.app.workspace.getMarkdownFiles();
  const notice = new Notice(`Processing ${files.length} files...`, 0);

  for (let i = 0; i < files.length; i++) {
    await this.processFile(files[i]);
    notice.setMessage(`Processing ${i + 1}/${files.length}...`);
  }

  notice.setMessage('Processing complete!');
  setTimeout(() => notice.hide(), 2000);
}

Confirmation Feedback

this.addCommand({
  id: 'copy-to-clipboard',
  name: 'Copy to Clipboard',
  editorCallback: (editor) => {
    const content = editor.getSelection() || editor.getValue();
    navigator.clipboard.writeText(content);
    new Notice('Copied to clipboard!');
  },
});

Warning Messages

this.addCommand({
  id: 'clear-cache',
  name: 'Clear Cache',
  callback: async () => {
    new Notice('Clearing cache...', 2000);
    await this.clearCache();
    new Notice('Cache cleared. Some features may be slower temporarily.', 5000);
  },
});

Long Operations

async exportAllNotes() {
  const notice = new Notice('Starting export...', 0);

  try {
    const files = await this.app.workspace.getMarkdownFiles();
    notice.setMessage(`Exporting ${files.length} notes...`);

    for (const file of files) {
      await this.exportNote(file);
    }

    notice.setMessage('Export complete!');
    setTimeout(() => notice.hide(), 2000);
  } catch (error) {
    notice.setMessage('Export failed: ' + error.message);
    setTimeout(() => notice.hide(), 5000);
  }
}

Keyboard Shortcut Feedback

this.addCommand({
  id: 'toggle-feature',
  name: 'Toggle Feature',
  hotkey: ['Mod', 'k'],
  callback: () => {
    this.featureEnabled = !this.featureEnabled;
    new Notice(
      `Feature ${this.featureEnabled ? 'enabled' : 'disabled'}`,
      2000
    );
  },
});

Best Practices

Keep Messages Short

// ✅ Good - concise
new Notice('File saved');
new Notice('Export complete');

// ❌ Bad - too verbose
new Notice('The file has been successfully saved to disk');
new Notice('The export operation completed successfully and all files have been processed');

Use Appropriate Durations

// ✅ Good durations
new Notice('Saved');                    // 3s (default) - quick action
new Notice('Processing...', 0);         // Indefinite - ongoing
new Notice('Error occurred', 5000);     // 5s - important message

// ❌ Bad durations
new Notice('Saved', 10000);             // 10s - too long for simple action
new Notice('Critical error', 1000);     // 1s - too short for errors

Provide Actionable Feedback

// ✅ Good - tells user what happened
new Notice('3 files exported to /exports');
new Notice('Copied to clipboard');
new Notice('Settings saved');

// ❌ Bad - vague
new Notice('Done');
new Notice('Success');
new Notice('OK');

Handle Errors Gracefully

// ✅ Good - error handling
try {
  await this.saveFile();
  new Notice('File saved');
} catch (error) {
  console.error('Save failed:', error);
  new Notice('Failed to save file', 5000);
}

// ❌ Bad - no error handling
await this.saveFile();
new Notice('File saved'); // May not be true!

Don’t Spam Notices

// ✅ Good - single summary notice
async processManyFiles(files: TFile[]) {
  for (const file of files) {
    await this.processFile(file);
  }
  new Notice(`Processed ${files.length} files`);
}

// ❌ Bad - notice for each file
async processManyFiles(files: TFile[]) {
  for (const file of files) {
    await this.processFile(file);
    new Notice(`Processed ${file.name}`); // Too many!
  }
}

Clean Up Long-Running Notices

// ✅ Good - hide when done
const notice = new Notice('Processing...', 0);
try {
  await this.longOperation();
  notice.setMessage('Complete!');
  setTimeout(() => notice.hide(), 2000);
} catch (error) {
  notice.setMessage('Failed');
  setTimeout(() => notice.hide(), 3000);
}

// ❌ Bad - forgotten indefinite notice
const notice = new Notice('Processing...', 0);
await this.longOperation();
// Notice never hidden!

Anti-Patterns

Don’t Use for Debugging

// ❌ Bad
function debugInfo() {
  new Notice('Debug: value = ' + value);
}

// ✅ Good
function debugInfo() {
  console.log('Debug: value =', value);
}

Don’t Use for Critical Information

// ❌ Bad - notice disappears
new Notice('Your API key is: ' + apiKey, 5000);

// ✅ Good - use modal for important info
class ApiKeyModal extends Modal {
  onOpen() {
    this.setTitle('API Key');
    this.contentEl.createEl('p', { text: 'Your API key:' });
    this.contentEl.createEl('code', { text: apiKey });
  }
}

Don’t Use for User Input

// ❌ Bad - can't interact with notice
new Notice('Click OK to continue', 0);

// ✅ Good - use modal for interaction
class ConfirmModal extends Modal {
  onOpen() {
    this.setTitle('Confirm');
    // Add buttons for user interaction
  }
}

Examples from Built-in Plugins

Quick Finder Plugin

async onChooseSuggestion(item: TFile) {
  try {
    await this.app.workspaceUI.openFile(item);
  } catch (error) {
    console.error('Failed to open note:', error);
    new Notice('Failed to open note');
  }
}

async toggleTodo(result: SearchResult) {
  try {
    // ... toggle logic
    new Notice('Todo toggled');
  } catch (e) {
    console.error(e);
    new Notice('Failed to toggle todo');
  }
}

private async createNote(inputValue: string) {
  try {
    // ... creation logic
    await this.app.tabManager.openTab(notePath, { openInNewTab: true });
    this.close();
  } catch (error) {
    console.error('Failed to create note:', error);
    new Notice('Failed to create note');
  }
}

Live Preview Plugin

export default class LivePreviewPlugin extends Plugin {
  async onload() {
    console.log('LivePreviewPlugin loaded');
    this.showNotice('Live Preview plugin loaded!', 1000);
  }
}