Loam is free and open source for macOS. Your notes stay plain Markdown files on your disk. Download for macOS.[Download for macOS.]
All pages

Plugins

Anatomy of a plugin

.md

The files of a plugin package, what each one does, and the naming rules for ids, classes and the entry point.

A vault plugin is a small Swift package. This page explains each file that loam plugin new creates, the fields of plugin.json, and the names the build expects. The examples use the word-count plugin from the quickstart.

The package

.loam/plugins/word-count/
  plugin.json                                       id, name, version, library, permissions
  Package.swift                                     the package; links LoamRuntime only
  Sources/WordCount/WordCountPlugin.swift           the plugin class and the entry point
  Tests/WordCountTests/WordCountPluginTests.swift   one test
  README.md                                         a short README to fill in
  .gitignore                                        ignores .build/ and .swiftpm/

Loam compiles the package into .loam/cache/plugins/word-count/, outside the plugin folder. A logic plugin uses Swift tools 5.10 and targets macOS 13.

Add --ui to plugin new for a plugin with a window part. Its Package.swift targets Swift tools 6.2 and macOS 26, and it also links LoamUIRuntime. See Interface plugins.

Link only the runtime libraries. Do not add other products of the Loam SDK: loam plugin build refuses a library that uses other parts of Loam.

plugin.json

The manifest tells Loam what the plugin is before it loads any code. The generated file looks like this:

plugin.json

{
  "id": "word-count",
  "name": "Word Count",
  "version": "0.1.0",
  "description": "",
  "icon": "puzzlepiece.extension",
  "library": "WordCountPlugin",
  "permissions": ["vault.read"]
}
  • id: kebab-case, starting with a letter. It must equal the folder name.
  • name and version: the display name and the version.
  • library: the name of the library product. The generated value is <Name>Plugin.
  • description: one line, shown under the name in Settings > Plugins.
  • icon: an SF Symbol name. iconFile can name an image in the plugin folder instead.
  • permissions: what the plugin does, such as vault.read. You see them before you enable the plugin. See Permissions.

The plugin class

The class conforms to Plugin. It declares its manifest, has an empty init(), and registers its features in activate(context:).

Sources/WordCount/WordCountPlugin.swift

import PluginKit

public final class WordCountPlugin: Plugin {
    public static var manifest: PluginManifest {
        PluginManifest(id: "word-count", name: "Word Count", version: "0.1.0")
    }

    public required init() {}

    public func activate(context: PluginContext) throws {
        // Register commands, services and listeners here.
    }
}

Name the class <PascalCase id>Plugin. The id word-count gives the module WordCount and the class WordCountPlugin.

Everything you register through context is removed when the plugin turns off. Keep no global state, and do no cleanup by hand.

The entry point

The last function of the file is the entry point. Loam calls it to get the plugin classes, and it records the SDK version the plugin was built against. The generated function is:

@_cdecl("loam_plugin_entry")
public func loamPluginEntry() -> UnsafeMutableRawPointer {
    Unmanaged.passRetained(PluginEntry(plugins: [WordCountPlugin.self])).toOpaque()
}

Keep it. If the package has more plugin classes, add each one to its list.

Ids, imports and versions

  • Every plugin class in the entry must use the package id, or the id followed by a dot: word-count or word-count.extra.
  • Name commands, views and panels <id>.<verb>. Ids are shared by every plugin in the app.
  • Import only PluginKit, PluginKitUI, SwiftUI and Foundation.
  • On macOS, a plugin built once keeps working after app updates that only add to the SDK. Rebuild it when the SDK major version changes.
  • Switch over an SDK enum with @unknown default, because a later release may add cases.
For AI agents: loam guide plugins describes the same package, and the checks that loam plugin build runs.