Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/betterdiscord/betterdiscord/llms.txt

Use this file to discover all available pages before exploring further.

BetterDiscord plugins follow a specific structure that includes metadata headers and required exports. Understanding this structure is essential for creating functional plugins.

File naming

Plugin files must follow the naming convention:
PluginName.plugin.js
The .plugin.js extension is required for BetterDiscord to recognize the file as a plugin.

Metadata header

Every plugin must start with a JSDoc comment block containing metadata:
/**
 * @name PluginName
 * @description A brief description of what your plugin does
 * @version 1.0.0
 * @author YourName
 * @authorId 123456789012345678
 * @website https://yourwebsite.com
 * @source https://github.com/yourusername/your-plugin
 * @updateUrl https://raw.githubusercontent.com/yourusername/your-plugin/main/PluginName.plugin.js
 */

Required metadata fields

@name
string
required
The display name of your plugin. Should match the filename without the .plugin.js extension.
@description
string
required
A brief description of what your plugin does. This appears in the plugins list.
@version
string
required
The current version of your plugin. Follow semantic versioning (e.g., 1.0.0).
@author
string
required
Your name or username.

Optional metadata fields

@authorId
string
Your Discord user ID.
@website
string
URL to your website or the plugin’s homepage.
@source
string
URL to the plugin’s source code repository.
@updateUrl
string
Direct URL to the raw plugin file for automatic updates.
@invite
string
Discord server invite code for support.
@donate
string
URL for donations or sponsorship.
@patreon
string
Patreon username or URL.
URL to author’s profile or portfolio.

Plugin class export

Plugins must export a class using module.exports:
module.exports = class MyPlugin {
    // Plugin implementation
};

Alternative: named class

module.exports = class MyPluginClass {
    getName() { return "MyPlugin"; }
    getDescription() { return "My plugin description"; }
    getVersion() { return "1.0.0"; }
    getAuthor() { return "YourName"; }
    
    start() { }
    stop() { }
};
If you don’t provide metadata in the header comment, you must implement the getName(), getDescription(), getVersion(), and getAuthor() methods.

Required methods

start()

Called when the plugin is enabled. This is where you should initialize your plugin:
start() {
    this.api = new BdApi("MyPlugin");
    this.api.Logger.info("Plugin started");
    
    // Initialize your plugin logic
    this.setupPatches();
    this.loadSettings();
}

stop()

Called when the plugin is disabled. Clean up all patches, listeners, and modifications:
stop() {
    // Unpatch all modifications
    BdApi.Patcher.unpatchAll("MyPlugin");
    
    // Remove any added elements
    this.cleanupUI();
    
    this.api.Logger.info("Plugin stopped");
}
Always clean up in the stop() method. Failing to do so can cause memory leaks and conflicts with other plugins.

Optional methods

constructor()

Called when the plugin is first loaded. Initialize persistent properties here:
constructor() {
    this.api = new BdApi("MyPlugin");
    this.settings = {};
    this.defaultSettings = {
        enabled: true,
        option: "value"
    };
}

getSettingsPanel()

Returns a settings panel for your plugin. Can return a DOM element, React element, or HTML string:
getSettingsPanel() {
    const {React} = BdApi;
    
    return BdApi.UI.buildSettingsPanel({
        settings: [
            {
                type: "switch",
                id: "enabled",
                name: "Enable Feature",
                note: "Toggle the main feature on or off",
                value: this.settings.enabled,
                onChange: (value) => {
                    this.settings.enabled = value;
                    this.api.Data.save("settings", this.settings);
                }
            }
        ]
    });
}

load()

Called when BetterDiscord first loads the plugin file. Rarely used, as most initialization should happen in constructor() or start():
load() {
    console.log("Plugin file loaded");
}

Complete example

Here’s a complete plugin showing proper structure:
ExamplePlugin.plugin.js
/**
 * @name ExamplePlugin
 * @description A complete example plugin
 * @version 1.0.0
 * @author YourName
 * @authorId 123456789012345678
 * @website https://example.com
 * @source https://github.com/example/plugin
 */

module.exports = class ExamplePlugin {
    constructor() {
        this.api = new BdApi("ExamplePlugin");
        this.settings = {};
        this.defaultSettings = {
            showToasts: true,
            debugMode: false
        };
    }
    
    start() {
        // Load saved settings
        this.settings = this.api.Data.load("settings") || this.defaultSettings;
        
        // Show startup notification
        if (this.settings.showToasts) {
            BdApi.UI.showToast("ExamplePlugin started!", { type: "success" });
        }
        
        // Setup patches and functionality
        this.setupPatches();
        
        if (this.settings.debugMode) {
            this.api.Logger.info("Debug mode enabled");
        }
    }
    
    stop() {
        // Clean up all patches
        BdApi.Patcher.unpatchAll("ExamplePlugin");
        
        // Show shutdown notification
        if (this.settings.showToasts) {
            BdApi.UI.showToast("ExamplePlugin stopped", { type: "info" });
        }
    }
    
    setupPatches() {
        // Your patch logic here
        this.api.Logger.info("Patches setup complete");
    }
    
    getSettingsPanel() {
        return BdApi.UI.buildSettingsPanel({
            settings: [
                {
                    type: "switch",
                    id: "showToasts",
                    name: "Show Toast Notifications",
                    note: "Display toast notifications on plugin start/stop",
                    value: this.settings.showToasts,
                    onChange: (value) => {
                        this.settings.showToasts = value;
                        this.api.Data.save("settings", this.settings);
                    }
                },
                {
                    type: "switch",
                    id: "debugMode",
                    name: "Debug Mode",
                    note: "Enable debug logging",
                    value: this.settings.debugMode,
                    onChange: (value) => {
                        this.settings.debugMode = value;
                        this.api.Data.save("settings", this.settings);
                    }
                }
            ],
            onChange: (category, id, value) => {
                this.api.Logger.info(`Setting changed: ${id} = ${value}`);
            }
        });
    }
};

TypeScript support

While plugins must be JavaScript files, you can develop in TypeScript and compile to JavaScript:
ExamplePlugin.ts
interface PluginSettings {
    showToasts: boolean;
    debugMode: boolean;
}

class ExamplePlugin {
    private api: BdApi;
    private settings: PluginSettings;
    
    constructor() {
        this.api = new BdApi("ExamplePlugin");
        this.settings = { showToasts: true, debugMode: false };
    }
    
    start(): void {
        // Implementation
    }
    
    stop(): void {
        // Implementation
    }
}

module.exports = ExamplePlugin;
Then compile with:
tsc ExamplePlugin.ts --target ES6 --module commonjs