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:
The .plugin.js extension is required for BetterDiscord to recognize the file as a plugin.
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
*/
The display name of your plugin. Should match the filename without the .plugin.js extension.
A brief description of what your plugin does. This appears in the plugins list.
The current version of your plugin. Follow semantic versioning (e.g., 1.0.0).
URL to your website or the plugin’s homepage.
URL to the plugin’s source code repository.
Direct URL to the raw plugin file for automatic updates.
Discord server invite code for support.
URL for donations or sponsorship.
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:
/**
* @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:
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