BetterCurseForge/docs/guide/plugins.md
tomasalias fed62b18bc
Some checks are pending
Build Windows Executable / Build BetterCurseForge.exe (push) Waiting to run
Deploy Documentation / build (push) Waiting to run
Deploy Documentation / deploy (push) Blocked by required conditions
docs: rewrite documentation and README in clear, direct developer tone
2026-10-25 14:03:15 +01:00

5.5 KiB

Writing Plugins

Plugins are small JavaScript modules that run inside CurseForge. They can change the DOM, patch methods at runtime, and save settings between sessions.


Plugin folder

Plugins live in the BetterCurseForge user directory:

%APPDATA%\CurseForge\BetterCurseForge\plugins\

Each plugin gets its own subfolder:

plugins/
└── compact-mode/
    ├── manifest.json       # Required: Plugin metadata
    ├── index.js            # Required: Entry script
    └── styles.css          # Optional: Companion stylesheet

The Open Plugins Folder button in the desktop app opens it.


Manifest (manifest.json)

manifest.json describes the plugin:

{
  "id": "compact-mode",
  "name": "Compact Mode",
  "version": "1.0.0",
  "description": "Reduces padding across mod lists, sidebar items, and modpack details for high-density browsing.",
  "author": "ModderDev",
  "main": "index.js",
  "styles": "styles.css"
}

Fields

Field Type Required Description
name string Yes Name shown in the management UI.
version string Yes Semantic version (e.g. 1.0.0).
description string No Short summary of what the plugin does.
author string No Author name or handle.
main string No Entry script. Defaults to index.js.
styles string No Stylesheet that's injected automatically when the plugin is enabled. Defaults to styles.css.

Lifecycle

A plugin's script runs when the plugin is enabled. It gets the BetterCurseForge API and a plugin context object:

// index.js
// BetterCurseForge passes (BetterCurseForge, plugin)
const { injectCSS, patcher, settings, on, off } = BetterCurseForge;

console.log('[MyPlugin] Plugin initialized!');

// Attach an onDisable cleanup handler:
plugin.onDisable = () => {
  console.log('[MyPlugin] Plugin disabled! Cleaning up...');
  // Remove any custom DOM elements, event listeners, or intervals here
};

::: tip STYLESHEETS If your plugin folder has a styles.css (or the file named in "styles" in manifest.json), BetterCurseForge injects it into <head> when the plugin is enabled and removes it when the plugin is disabled. You don't need to inject or clean it up yourself. :::


Plugin API

1. BetterCurseForge.injectCSS(css)

Injects CSS into <head>. Returns a function that removes the style tag:

const removeStyles = BetterCurseForge.injectCSS(`
  .addon-row { padding: 4px 8px !important; }
`);

// Later in cleanup:
plugin.onDisable = () => {
  removeStyles();
};

2. BetterCurseForge.settings

Reads and writes settings that are saved to disk (settings.json):

// Retrieve a stored setting (or null if not set)
const isCustomFont = await BetterCurseForge.settings.get('compactMode.customFont');

// Store a setting
await BetterCurseForge.settings.set('compactMode.density', 'ultra');

3. BetterCurseForge.patcher

Helpers for monkey-patching methods on objects. Each call returns a function that undoes the patch:

patcher.before(object, methodName, callback)

Runs your callback before the original method. The callback gets (args):

const unpatch = BetterCurseForge.patcher.before(console, 'error', (args) => {
  console.warn('[Intercepted Console Error]', ...args);
});

plugin.onDisable = () => unpatch();

patcher.after(object, methodName, callback)

Runs after the original method. The callback gets (args, result) and can return a new value to replace the result:

const unpatch = BetterCurseForge.patcher.after(targetObj, 'getItemTitle', (args, result) => {
  return `⚡ ${result}`;
});

plugin.onDisable = () => unpatch();

patcher.instead(object, methodName, callback)

Replaces the original method. The callback gets (args, originalMethod):

const unpatch = BetterCurseForge.patcher.instead(targetObj, 'fetchData', (args, originalFn) => {
  console.log('Intercepting fetchData call...');
  return originalFn(...args);
});

plugin.onDisable = () => unpatch();

4. BetterCurseForge.on(event, callback) / .off() / .emit()

An event emitter shared across the app:

const onReady = () => console.log('CurseForge UI is fully loaded!');

BetterCurseForge.on('ready', onReady);

plugin.onDisable = () => {
  BetterCurseForge.off('ready', onReady);
};

Example: Compact Mode

This plugin makes CurseForge's layout denser.

::: code-group

{
  "id": "compact-mode",
  "name": "Compact Mode",
  "version": "1.0.0",
  "description": "Reduces margins and padding for high-density modpack browsing.",
  "author": "BetterCurseForge Team",
  "main": "index.js",
  "styles": "styles.css"
}
// Add compact layout class to the document root
document.documentElement.classList.add('bcf-compact-layout');

console.log('[Compact Mode] Activated');

// Clean up when toggled off
plugin.onDisable = () => {
  document.documentElement.classList.remove('bcf-compact-layout');
  console.log('[Compact Mode] Deactivated');
};
/* Automatically injected when plugin is enabled */
.bcf-compact-layout .tile-view-item {
  width: 150px !important;
  height: 240px !important;
}

.bcf-compact-layout .action-button-container {
  height: 48px !important;
  margin: 6px 0 !important;
}

.bcf-compact-layout .details-title {
  font-size: 13px !important;
}

:::