Modals display information and accept user input. To create a modal, create a class that extends [[Reference/TypeScript API/Modal|Modal]]: ```ts import { App, Modal } from 'obsidian'; export class ExampleModal extends Modal { constructor(app: App) { super(app); this.setContent('Look at me, I\'m a modal! 👀') } } ``` To open a modal, create a new instance of `ExampleModal` and call [[Reference/TypeScript API/Modal/open|open()]] on it: ```ts import { Plugin } from 'obsidian'; import { ExampleModal } from './modal'; export default class ExamplePlugin extends Plugin { async onload() { this.addCommand({ id: 'display-modal', name: 'Display modal', callback: () => { new ExampleModal(this.app).open(); }, }); } } ``` ## Accept user input Our modal in the previous example only displayed some information. Let's look at a slightly more complex example that also handles user input. ![[modal-input.png]] ```ts import { App, Modal, Setting } from 'obsidian'; export class ExampleModal extends Modal { constructor(app: App, onSubmit: (result: string) => void) { super(app); this.setTitle('What\'s your name?'); let name = ''; new Setting(this.contentEl) .setName('Name') .addText((text) => text.onChange((value) => { name = value; })); new Setting(this.contentEl) .addButton((btn) => btn .setButtonText('Submit') .setCta() .onClick(() => { this.close(); onSubmit(name); })); } } ``` The result is passed into the `onSubmit` callback when the user clicks **Submit**: ```ts new ExampleModal(this.app, (result) => { new Notice(`Hello, ${result}!`); }).open(); ``` ## Select from list of suggestions [[SuggestModal|SuggestModal]] is a special modal that lets you display a list of suggestions to the user. ![[suggest-modal.gif]] ```ts import { App, Notice, SuggestModal } from 'obsidian'; interface Book { title: string; author: string; } const ALL_BOOKS = [ { title: 'How to Take Smart Notes', author: 'Sönke Ahrens', }, { title: 'Thinking, Fast and Slow', author: 'Daniel Kahneman', }, { title: 'Deep Work', author: 'Cal Newport', }, ]; export class ExampleModal extends SuggestModal<Book> { // Returns all available suggestions. getSuggestions(query: string): Book[] { return ALL_BOOKS.filter((book) => book.title.toLowerCase().includes(query.toLowerCase()) ); } // Renders each suggestion item. renderSuggestion(book: Book, el: HTMLElement) { el.createEl('div', { text: book.title }); el.createEl('small', { text: book.author }); } // Perform action on the selected suggestion. onChooseSuggestion(book: Book, evt: MouseEvent | KeyboardEvent) { new Notice(`Selected ${book.title}`); } } ``` ### Approximate string matching results In addition to `SuggestModal`, the Obsidian API provides an even more specialized type of modal for suggestions: the [[FuzzySuggestModal|FuzzySuggestModal]], which gets you [fuzzy string search](https://en.wikipedia.org/wiki/Approximate_string_matching) out-of-the-box. ![[fuzzy-suggestion-modal.png]] ```ts import {FuzzySuggestModal, Notice} from "obsidian"; export class ExampleSuggestModal extends FuzzySuggestModal<Book> { getItems(): Book[] { return ALL_BOOKS; } getItemText(book: Book): string { return book.title; } onChooseItem(book: Book, evt: MouseEvent | KeyboardEvent) { new Notice(`Selected ${book.title}`); } } ``` ### Custom rendering of fuzzy search results For a more custom UI you implement the [[Reference/TypeScript API/fuzzysuggestmodal/renderSuggestion|renderSuggestion]] function, like in the earlier example. The [[renderResults]] method is responsible for rendering the different strings while highlighting the matched parts. ![[fuzzy-suggestion-custom-modal.png]] ```ts import {FuzzyMatch, FuzzySuggestModal, Notice, renderResults} from "obsidian"; export class ExampleSuggestModal extends FuzzySuggestModal<Book> { //return a string representation, so there is something to search getItemText(item: Book): string { return item.title + " " + item.author; } getItems(): Book[] { return ALL_BOOKS; } renderSuggestion(match: FuzzyMatch<Book>, el: HTMLElement) { const titleEl = el.createDiv(); renderResults(titleEl, match.item.title, match.match); // Only render the matches in the author name. const authorEl = el.createEl('small'); const offset = -(match.item.title.length + 1); renderResults(authorEl, match.item.author, match.match, offset); } onChooseItem(book: Book, evt: MouseEvent | KeyboardEvent): void { new Notice(`Selected ${book.title}`); } } ```