Spaces:
Sleeping
Sleeping
| If you want users to be able to configure parts of your plugin themselves, you can expose them as _settings_. | |
| In this guide, you'll learn how to create a settings page like this 👇 | |
| ![[settings.png]] | |
| The main reason to add settings to a plugin is to store configuration that persists even after the user quits Obsidian. The following example demonstrates how to save and load settings from disk: | |
| ```ts | |
| import { Plugin } from "obsidian"; | |
| import { ExampleSettingTab } from "./settings"; | |
| interface ExamplePluginSettings { | |
| dateFormat: string; | |
| } | |
| const DEFAULT_SETTINGS: Partial<ExamplePluginSettings> = { | |
| dateFormat: "YYYY-MM-DD", | |
| }; | |
| export default class ExamplePlugin extends Plugin { | |
| settings: ExamplePluginSettings; | |
| async onload() { | |
| await this.loadSettings(); | |
| this.addSettingTab(new ExampleSettingTab(this.app, this)); | |
| } | |
| async loadSettings() { | |
| this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); | |
| } | |
| async saveSettings() { | |
| await this.saveData(this.settings); | |
| } | |
| } | |
| ``` | |
| > [!warning] Nested properties in settings | |
| > `Object.assign()` copies the references to any nested properties (shallow copy). If your settings object contains nested properties, you need to copy each nested property recursively (deep copy). Otherwise, any changes to a nested property will apply do all objects that were copied using `Object.assign()`. | |
| There's a lot going on here 🤯, so let's look closer at each part. | |
| ## Create a settings definition | |
| First, you need to create a definition, `ExamplePluginSettings`, for what settings you want the user to be able to configure. While the plugin is enabled, you can access the settings from the `settings` member variable. | |
| ```ts | |
| interface ExamplePluginSettings { | |
| dateFormat: string; | |
| } | |
| export default class ExamplePlugin extends Plugin { | |
| settings: ExamplePluginSettings; | |
| // ... | |
| } | |
| ``` | |
| ## Save and load the settings object | |
| [[loadData|loadData()]] and [[saveData|saveData()]] provide an easy way to store and retrieve data from disk. The example also introduces two helper methods that makes it easier to use `loadData()` and `saveData()` from other parts of the plugin. | |
| ```ts | |
| export default class ExamplePlugin extends Plugin { | |
| // ... | |
| async loadSettings() { | |
| this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); | |
| } | |
| async saveSettings() { | |
| await this.saveData(this.settings); | |
| } | |
| } | |
| ``` | |
| Finally, make sure to load the settings when the plugin loads: | |
| ```ts | |
| async onload() { | |
| await this.loadSettings(); | |
| // ... | |
| } | |
| ``` | |
| ## Provide default values | |
| When the user enables the plugin for the first time, none of the settings have been configured yet. The preceding example provides default values for any missing settings. | |
| To understand how this work, let's look at the following code: | |
| ```ts | |
| Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) | |
| ``` | |
| `Object.assign()` is a JavaScript function that copies all properties from one object to another. Any properties that are returned by `loadData()` override the properties in `DEFAULT_SETTINGS`. | |
| ```ts | |
| const DEFAULT_SETTINGS: Partial<ExamplePluginSettings> = { | |
| dateFormat: "YYYY-MM-DD", | |
| }; | |
| ``` | |
| > [!tip] | |
| > `Partial<Type>` is a TypeScript utility that returns a type with all properties of `Type` set to optional. It enables type checking while letting you only define the properties you want to provide defaults for. | |
| ## Register a settings tab | |
| The plugin can now save and load plugin configuration, but the user doesn't yet have any way of changing any of the settings. By adding a settings tab you can provide an easy-to-use interface for the user to update their plugin settings: | |
| ```ts | |
| this.addSettingTab(new ExampleSettingTab(this.app, this)); | |
| ``` | |
| Here, the `ExampleSettingTab` is a class that extends [[PluginSettingTab|PluginSettingTab]]: | |
| ```ts | |
| import ExamplePlugin from "./main"; | |
| import { App, PluginSettingTab, Setting } from "obsidian"; | |
| export class ExampleSettingTab extends PluginSettingTab { | |
| plugin: ExamplePlugin; | |
| constructor(app: App, plugin: ExamplePlugin) { | |
| super(app, plugin); | |
| this.plugin = plugin; | |
| } | |
| display(): void { | |
| let { containerEl } = this; | |
| containerEl.empty(); | |
| new Setting(containerEl) | |
| .setName("Date format") | |
| .setDesc("Default date format") | |
| .addText((text) => | |
| text | |
| .setPlaceholder("MMMM dd, yyyy") | |
| .setValue(this.plugin.settings.dateFormat) | |
| .onChange(async (value) => { | |
| this.plugin.settings.dateFormat = value; | |
| await this.plugin.saveSettings(); | |
| }) | |
| ); | |
| } | |
| } | |
| ``` | |
| `display()` is where you build the content for the settings tab. For more information, refer to [[HTML elements]]. | |
| `new Setting(containerEl)` appends a setting to the container element. This example uses a text field using `addText()`, but there are several other setting types available. | |
| Update the settings object whenever the value of the text field changes, and then save it to disk: | |
| ```ts | |
| .onChange(async (value) => { | |
| this.plugin.settings.dateFormat = value; | |
| await this.plugin.saveSettings(); | |
| }) | |
| ``` | |
| Nice work! 💪 Your users will thank you for giving them a way to customize how they interact with your plugin. Before heading to the next guide, experiment with what you've learned by adding another setting. | |