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 uses a sophisticated injection system to modify Discord without altering its original files. This approach allows BetterDiscord to work alongside Discord updates while maintaining compatibility.

How injection works

The injection process happens in three stages:

Stage 1: Injector

The injector is a local process that modifies Discord’s startup to include BetterDiscord. When you run the BetterDiscord installer or use pnpm inject, it:
  1. Locates your Discord installation
  2. Modifies Discord’s entry point to load BetterDiscord’s injector code
  3. Sets up the necessary file structure in Discord’s app directory

Stage 2: Preload script

When Discord starts, the preload script executes before Discord’s own code:
// Preload runs in a special context
// It has access to both Node.js and browser APIs

// Set up secure IPC channels
contextBridge.exposeInMainWorld('BdPreload', {
    // Exposed APIs for renderer
});
The preload script:
  • Runs in an isolated context with special privileges
  • Sets up communication between main and renderer processes
  • Exposes necessary Node.js functionality to the renderer safely
  • Cannot be accessed or modified by Discord’s own code
The preload script is critical for security. It carefully controls what Node.js APIs are exposed to prevent malicious code from accessing system resources.

Stage 3: Renderer execution

After preload completes, the renderer application loads into Discord’s renderer process:
// src/betterdiscord/index.ts
import require from "./polyfill";
import secure from "./secure";
import LoadingIcon from "./loadingicon";
import BetterDiscord from "@modules/core";
import BdApi from "@api/index";

// Perform security setup
secure();

// Expose BdApi globally
Object.defineProperty(window, "BdApi", {
    value: BdApi,
    writable: false,
    configurable: false
});

window.global = window;

// Show loading indicator
LoadingIcon.show();

// Start BetterDiscord
BetterDiscord.startup();

The Patcher system

Once injected, BetterDiscord needs to modify Discord’s behavior without changing its source files. This is done through the Patcher system.

Patching functions

The Patcher allows intercepting function calls at runtime:
import Patcher from "@modules/patcher";

// Patch before a function runs
Patcher.before("MyPlugin", SomeModule, "someFunction", (thisObject, args) => {
    // Modify arguments before function runs
    args[0] = "modified";
});

// Patch after a function runs
Patcher.after("MyPlugin", SomeModule, "someFunction", (thisObject, args, returnValue) => {
    // Modify return value after function runs
    return modifiedValue;
});

// Replace a function entirely
Patcher.instead("MyPlugin", SomeModule, "someFunction", (thisObject, args, originalFunction) => {
    // Completely replace function behavior
    // Call originalFunction() if needed
    return customValue;
});

Patch types

Execute code before the original function runs. Useful for:
  • Modifying arguments
  • Preventing execution based on conditions
  • Logging function calls
Patcher.before("Logger", MessageActions, "sendMessage", (_, [channelId, message]) => {
    console.log(`Sending to ${channelId}:`, message.content);
});
Execute code after the original function completes. Useful for:
  • Modifying return values
  • Reacting to function results
  • Post-processing data
Patcher.after("Enhancer", MessageParser, "parse", (_, args, result) => {
    // Add custom parsing to messages
    result.customData = processMessage(result);
    return result;
});
Completely replace a function’s behavior. Useful for:
  • Overriding functionality entirely
  • Conditional execution of original
  • Complex behavior changes
Patcher.instead("Override", SomeModule, "someFunction", (thisObject, args, original) => {
    if (customCondition) {
        return customImplementation(args);
    }
    return original(...args); // Call original if needed
});

Managing patches

Patches should always be cleaned up:
class MyPlugin {
    start() {
        // Store unpatch function
        this.unpatch = Patcher.after("MyPlugin", Module, "function", callback);
    }
    
    stop() {
        // Clean up specific patch
        this.unpatch();
        
        // Or clean up all patches for this plugin
        Patcher.unpatchAll("MyPlugin");
    }
}
Always unpatch when disabling a plugin. Lingering patches can cause conflicts and memory leaks.

Security model

BetterDiscord’s injection includes several security measures:

Sandboxing

The preload script uses Electron’s contextBridge to safely expose functionality:
  • Only specific APIs are exposed to the renderer
  • Direct Node.js access is restricted
  • File system operations are controlled

Secure communication

IPC (Inter-Process Communication) channels are carefully designed:
// Only specific messages are allowed
IPC.on('allowed-event', handler);

// Validate all inputs
IPC.on('file-read', (path) => {
    if (!isPathSafe(path)) return;
    // Process request
});

Repair and reinstall

If Discord updates overwrite BetterDiscord’s injection:
1

Automatic detection

BetterDiscord can detect when it’s been removed and prompt for repair.
2

Manual repair

Run the BetterDiscord installer again or use:
pnpm inject <channel>
3

Verification

Restart Discord completely to verify the injection is working.

Development considerations

Testing injection locally

When developing BetterDiscord:
# Build the project
pnpm build

# Inject into a specific Discord channel
pnpm inject stable  # or canary, ptb

# Restart Discord to test

Injection changes

Different parts require different reload procedures:
ComponentReload Method
Renderer codeCtrl+R in Discord
Preload scriptFull Discord restart
InjectorFull Discord restart + re-inject
Most development work happens in the renderer, which only requires a Discord reload (Ctrl+R), making iteration fast.

Next steps

Architecture overview

Understand BetterDiscord’s overall structure

Addon system

Learn how plugins and themes are loaded