Get AI summaries of any video or article — Sign up free
Setting up plugin settings with Marcus Olsson thumbnail

Setting up plugin settings with Marcus Olsson

Obsidian·
5 min read

Based on Obsidian's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Define a settings schema (e.g., a string “spooky text”) and create a default settings object so first-run behavior never breaks.

Briefing

Obsidian plugin settings can be made persistent by wiring three pieces together: a typed settings model with defaults, runtime load/save using Obsidian’s data.json storage, and a Settings Tab UI that edits those values. The payoff is straightforward—users can customize behavior (like the “boo” text in a Halloween plugin) and have the change survive restarts instead of being hard-coded.

The tutorial starts with a concrete problem: the “peek into the dark” command and ribbon action currently display a fixed, hard-coded scare message. To let users choose their own scare text, the plugin needs a configurable property and a way to remember it. The first step is defining what’s configurable by creating a settings interface (for example, a single string property such as “spooky text”) and pairing it with defaults. Defaults are handled using a “partial” type so missing fields won’t crash the plugin, and the initial value can safely fall back to something like “boo.”

Next comes the runtime plumbing. Obsidian provides APIs to move settings between disk and memory: load data from the plugin directory’s data.json into the plugin at startup, and save data back when users change values. The tutorial recommends extracting loading into an async loadSettings function that reads from disk, then merging loaded values over the defaults using Object.assign. That merge step matters because it avoids a common mistake: directly assigning defaults by reference can cause updates in the live settings to mutate the default object itself.

Once settings are loaded, the plugin must actually use them. The scare notice is updated to read from settings.spookyText (or the equivalent property), but nothing changes until loadSettings is called during plugin initialization. After that, the command reflects the stored value.

The final—and most user-facing—piece is the Settings Tab. Obsidian’s SettingsTab mechanism lets plugins register a new section under the app’s settings UI. A custom SpookySettingsTab class extends the built-in PluginSettingTab, implements a display method, and receives the plugin and app context via its constructor so it can access the current settings. Inside display, the tab builds UI using HTML container elements and Obsidian’s Setting components, including a text input bound to the current setting value.

To persist edits, the text input’s onChange handler updates the plugin’s in-memory settings and calls a saveSettings method. That method uses Obsidian’s saveData API to write the updated settings object back to data.json. The tutorial closes by showing the resulting data.json structure in the vault (where user-editable settings live), and suggests extending the same pattern with additional setting types and UI controls. Theme development is teased as the next topic after settings.

Cornell Notes

The core workflow for persistent Obsidian plugin settings is: define a settings schema with defaults, load saved values from the plugin’s data.json into runtime on startup, and provide a Settings Tab UI that edits those values. The tutorial uses Object.assign to merge loaded settings over default values safely, avoiding reference bugs where defaults get mutated. A custom SettingsTab class extends PluginSettingTab and renders a text input bound to the current setting. When the user changes the input, the plugin updates its in-memory settings and calls saveData so the new value is written back to data.json and reappears after restart.

Why define defaults and merge them with loaded settings instead of relying on whatever is in data.json?

Defaults prevent missing fields from breaking the plugin on first run. The tutorial defines a settings interface (e.g., spooky text as a string) and creates a default settings object. It then loads data.json asynchronously and merges loaded values over defaults using Object.assign({}, defaultSettings, loadedSettings). This ensures that if data.json is empty or missing properties, the plugin still has valid values. It also avoids the reference mistake where assigning defaults directly can cause updates to mutate the default object itself.

What does “load settings” actually do in an Obsidian plugin?

It reads the plugin’s stored settings from disk into runtime. The tutorial uses Obsidian’s loadData API to fetch an object from the plugin directory’s data.json, then assigns it to the plugin’s settings variable after merging with defaults. The loadSettings function is async because it performs file-system I/O, and it’s called during plugin initialization so commands and UI immediately reflect the saved values.

How does the plugin make the Settings Tab appear under Obsidian’s settings UI?

It registers a custom settings tab with the app using the plugin’s addSettingsTab call (the tutorial uses an addSettingsTab-style registration). The custom class extends PluginSettingTab and implements display(). During display(), it uses the provided containerEl (an HTML element) to render headings and Setting components, such as a text input labeled “Spooky text” with a description like “Make it real scary.”

How does a text input in the Settings Tab stay synchronized with the stored setting value?

The text input is initialized with the current value from the plugin’s settings (e.g., .setValue(this.plugin.settings.spookyText)). It also attaches an onChange callback that receives the new value. In that callback, the plugin updates its in-memory settings (this.plugin.settings.spookyText = value) and then persists it by calling saveSettings.

What ensures changes persist after restarting Obsidian?

saveSettings writes the updated settings object back to disk using Obsidian’s saveData API. The tutorial’s saveSettings method mirrors loadSettings: it calls saveData with the settings object. After disabling and re-enabling the plugin, the Settings Tab repopulates with the previously saved value, confirming data.json persistence.

Where can developers inspect the persisted settings on disk?

In the vault’s .obsidian/plugins/<plugin-name>/ directory, where data.json contains a JSON object holding the settings defined by the plugin. The tutorial points out that user-customizable settings are stored there, while other plugin state can also be kept in the same file if it doesn’t need to be user-editable.

Review Questions

  1. What problem does Object.assign({}, defaultSettings, loadedSettings) solve when implementing plugin settings defaults?
  2. Which two lifecycle moments are required for settings to work end-to-end (loading and saving), and what APIs are used at each moment?
  3. How does the Settings Tab UI connect user input to persistence (name the callback and the persistence call)?

Key Points

  1. 1

    Define a settings schema (e.g., a string “spooky text”) and create a default settings object so first-run behavior never breaks.

  2. 2

    Load settings asynchronously from the plugin’s data.json into runtime on plugin initialization so commands immediately reflect stored values.

  3. 3

    Merge loaded settings over defaults using Object.assign to avoid reference bugs where defaults get mutated.

  4. 4

    Render a custom Settings Tab by extending PluginSettingTab, implementing display(), and registering it with the plugin.

  5. 5

    Bind UI controls (like a text input) to the current settings value and update in-memory settings on every change.

  6. 6

    Persist edits by calling saveData with the updated settings object so changes survive restarts.

  7. 7

    Inspect the resulting data.json in the vault’s .obsidian/plugins/<plugin-name>/ folder to verify what gets stored.

Highlights

Persistent settings require more than a settings object: they need loadSettings at startup and saveSettings on user edits.
Object.assign({}, defaultSettings, loadedSettings) is used to merge defaults safely and prevent accidental mutation of the default settings reference.
A Settings Tab becomes visible by registering a PluginSettingTab subclass and implementing display() using containerEl and Setting components.
The text input’s onChange handler updates plugin.settings and then calls saveData, making the change durable in data.json.

Topics

  • Plugin Settings
  • Settings Tab UI
  • Load/Save Data
  • Default Values
  • Persistent Customization