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. | |