Skip to main content

Overview

The SuggestModal<T> class extends Modal to provide a search input with dynamic suggestions. It’s useful for creating interfaces where users can search and select from a list of items. This is an abstract class that requires you to implement three key methods:
  • getSuggestions() - Return suggestions based on the query
  • renderSuggestion() - Render each suggestion in the list
  • onChooseSuggestion() - Handle when a user selects a suggestion
Since: 0.9.20

Type Parameter

T
generic
The type of items that will be suggested

Constructor

app
App
required
Reference to the Obsidian App instance
class MyModal extends SuggestModal<string> {
  constructor(app: App) {
    super(app);
  }
  // ... implement required methods
}

Properties

limit
number
Maximum number of suggestions to display at once. Default value varies.Since: 0.9.20
emptyStateText
string
Text to display when no suggestions are found.Since: 0.9.20
inputEl
HTMLInputElement
The search input element.Since: 0.9.20
resultContainerEl
HTMLElement
Container element for the suggestion results.Since: 0.9.20

Methods

setPlaceholder()

Sets the placeholder text for the search input.
placeholder
string
required
The placeholder text
this.setPlaceholder('Type to search...');
Since: 0.9.20

setInstructions()

Sets instruction prompts that appear below the input.
instructions
Instruction[]
required
Array of instruction objects with command and purpose properties
this.setInstructions([
  { command: '↑↓', purpose: 'to navigate' },
  { command: '↵', purpose: 'to select' },
  { command: 'esc', purpose: 'to dismiss' }
]);
Since: 0.9.20

onNoSuggestion()

Called when there are no suggestions to display. Override this to customize the empty state.
onNoSuggestion() {
  // Custom logic when no results found
}
Since: 0.9.20

selectSuggestion()

Programmatically selects a suggestion.
value
T
required
The suggestion item to select
evt
MouseEvent | KeyboardEvent
required
The triggering event
this.selectSuggestion(item, evt);
Since: 0.9.20

selectActiveSuggestion()

Selects the currently highlighted suggestion.
evt
MouseEvent | KeyboardEvent
required
The triggering event
this.selectActiveSuggestion(evt);
Since: 1.7.2

Abstract Methods

You must implement these methods when extending SuggestModal.

getSuggestions()

Returns an array of suggestions based on the user’s query.
query
string
required
The current search query
Returns: T[] | Promise<T[]> - Array of suggestions or a Promise that resolves to suggestions
getSuggestions(query: string): string[] {
  return ['apple', 'banana', 'cherry']
    .filter(item => item.toLowerCase().includes(query.toLowerCase()));
}
Since: 1.5.7

renderSuggestion()

Renders a suggestion item into the provided HTML element.
value
T
required
The suggestion item to render
el
HTMLElement
required
The HTML element to render the suggestion into
renderSuggestion(value: string, el: HTMLElement) {
  el.createEl('div', { text: value });
}
Since: 1.5.7

onChooseSuggestion()

Called when the user selects a suggestion.
item
T
required
The selected suggestion item
evt
MouseEvent | KeyboardEvent
required
The event that triggered the selection
onChooseSuggestion(item: string, evt: MouseEvent | KeyboardEvent) {
  new Notice(`You selected: ${item}`);
}
Since: 1.5.7

Example

import { App, SuggestModal, Notice } from 'obsidian';

interface Book {
  title: string;
  author: string;
}

class BookSuggestModal extends SuggestModal<Book> {
  books: Book[];

  constructor(app: App, books: Book[]) {
    super(app);
    this.books = books;
    this.setPlaceholder('Search for a book...');
    this.setInstructions([
      { command: '↑↓', purpose: 'to navigate' },
      { command: '↵', purpose: 'to select' },
      { command: 'esc', purpose: 'to dismiss' }
    ]);
  }

  getSuggestions(query: string): Book[] {
    return this.books.filter(book =>
      book.title.toLowerCase().includes(query.toLowerCase()) ||
      book.author.toLowerCase().includes(query.toLowerCase())
    );
  }

  renderSuggestion(book: Book, el: HTMLElement) {
    el.createEl('div', { text: book.title, cls: 'book-title' });
    el.createEl('small', { text: book.author, cls: 'book-author' });
  }

  onChooseSuggestion(book: Book, evt: MouseEvent | KeyboardEvent) {
    new Notice(`Selected: ${book.title} by ${book.author}`);
  }
}

// Usage
const books = [
  { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
  { title: '1984', author: 'George Orwell' },
  { title: 'To Kill a Mockingbird', author: 'Harper Lee' }
];

const modal = new BookSuggestModal(this.app, books);
modal.open();

Example: Async Suggestions

import { App, SuggestModal, requestUrl } from 'obsidian';

interface SearchResult {
  id: string;
  name: string;
}

class AsyncSuggestModal extends SuggestModal<SearchResult> {
  async getSuggestions(query: string): Promise<SearchResult[]> {
    if (!query) return [];
    
    try {
      const response = await requestUrl({
        url: `https://api.example.com/search?q=${encodeURIComponent(query)}`
      });
      return response.json;
    } catch (error) {
      console.error('Search failed:', error);
      return [];
    }
  }

  renderSuggestion(result: SearchResult, el: HTMLElement) {
    el.createEl('div', { text: result.name });
  }

  onChooseSuggestion(result: SearchResult, evt: MouseEvent | KeyboardEvent) {
    console.log('Selected:', result);
  }
}

See Also

Build docs developers (and LLMs) love