BetterStrap docs

BetterStrap plugin guide

BetterStrap has a small plugin system, so you can extend it with your own scripts. This guide covers the trust model (read it), how to write a plugin, and the full API.

Where plugins live

<userData>/plugins/
  my-plugin/
    plugin.json
    main.js

<userData> is BetterStrap’s per-user data folder, the same place its settings and database live. The Plugins page has an Open folder button that reveals it.

Anatomy of a plugin

A plugin is a folder containing two files.

plugin.json (manifest)

plugin.json
{
  "name": "Hello Notify",
  "version": "1.0.0",
  "description": "Greets you when you join a game.",
  "main": "main.js"
}
FieldRequiredNotes
nameyesShown in the Plugins list.
versionnoDefaults to "0.0.0". Free-form string.
descriptionnoOne line, shown under the name.
mainnoEntry file, defaults to main.js. Must stay inside the plugin folder (no ../ escape; this is validated).

main.js (entry)

Export either a factory function that receives the context…

main.js
module.exports = function (context) {
  context.on("gameJoin", (info) => {
    context.notify("Have fun!", `Joined place ${info.placeId}`);
  });
  // optional: return a teardown function
  return function deactivate() { /* clean up */ };
};

…or an object with activate(context) and (optionally) deactivate():

main.js
module.exports = {
  activate(context) { /* ... */ },
  deactivate() { /* ... */ },
};

A working, fully commented example is in the worked example below. Copy it into your plugins directory to try it.

API

Your plugin receives one context object. That is the whole intended surface.

context.id

Your plugin’s slug (its folder name).

context.on(event, callback)

Subscribe to a host lifecycle event. Subscriptions are torn down automatically when your plugin is disabled, reloaded, or the app quits, so you can’t leak listeners.

EventPayloadWhen
"appReady"{}Once, right after your plugin activates.
"gameJoin"{ userId, placeId, jobId }You joined or teleported into a game. Fields may be null if not yet known.
"gameLeave"{}You left the game.

context.notify(title, body)

Show an OS notification. Clicking it opens BetterStrap on the Plugins page.

context.settingsGet(key) / context.settingsSet(key, value)

Read and write your plugin’s own settings. Keys are transparently namespaced per plugin, so one plugin can never read or overwrite another plugin’s (or the app’s) settings. Values persist across restarts. Returns null for missing keys.

context.log(...args)

Console logging, prefixed with [plugin:<your-slug>].

That’s the whole documented API. It is deliberately small. If you need more, you are writing trusted code and can require() Node or Electron modules directly, but understand that you are then fully responsible for what that code does.

Worked example: hello-notify

The canonical example plugin. It sends a notification each time you join a game and counts how many times it has greeted you, touching every part of the API along the way.

  1. On the Plugins page, click Open folder and create a folder named hello-notify inside it.
  2. Save the two files below into that folder.
  3. Turn plugins on (read the warning), hit Reload plugins, and join a game.
hello-notify/plugin.json
{
  "name": "Hello Notify",
  "version": "1.0.0",
  "description": "Sends a friendly notification each time you join a game, and counts how many times it has greeted you.",
  "main": "main.js"
}
hello-notify/main.js
/**
 * hello-notify — the canonical BetterSTRAP example plugin.
 *
 * It shows every piece of the plugin API:
 *   - the factory function that receives the `context` object
 *   - subscribing to lifecycle events with context.on(...)
 *   - showing a notification with context.notify(...)
 *   - persisting the plugin's own state with context.settingsGet/Set(...)
 *   - logging with context.log(...)
 *   - returning a teardown function that runs on disable/reload
 *
 * To try it: copy this whole `hello-notify/` folder into
 *   <BetterSTRAP userData>/plugins/hello-notify/
 * then open the Plugins page, turn plugins on (read the warning), and join a game.
 *
 * IMPORTANT: plugins run in BetterSTRAP's main process with full privileges.
 * There is no sandbox — only run plugins you trust.
 * See https://betterroblox.com/roblox-bootstrapper/plugins
 */

module.exports = function (context) {
  context.log("hello-notify loaded");

  // Greet the user once, right after the plugin activates.
  context.on("appReady", () => {
    const count = context.settingsGet("greetings") || 0;
    context.notify(
      "Hello from a plugin 👋",
      count > 0
        ? `We've said hi ${count} time${count === 1 ? "" : "s"} so far.`
        : "This is the hello-notify example plugin. Join a game to see it in action."
    );
  });

  // Fire a notification whenever you join (or teleport into) a game.
  context.on("gameJoin", (info) => {
    const count = (context.settingsGet("greetings") || 0) + 1;
    context.settingsSet("greetings", count); // persisted per-plugin, survives restarts
    const where = info && info.placeId ? `place ${info.placeId}` : "a game";
    context.notify("Have fun! 🎮", `You just joined ${where}. Greeting #${count}.`);
    context.log("gameJoin", info);
  });

  // Optional: react to leaving a game.
  context.on("gameLeave", () => {
    context.log("gameLeave");
  });

  // A plugin may return a teardown function. BetterSTRAP calls it when the
  // plugin is disabled, reloaded, or the app quits — undo anything you set up
  // here (timers, watchers, etc.). Event subscriptions are cleaned up for you.
  return function deactivate() {
    context.log("hello-notify unloaded");
  };
};

Managing plugins (the Plugins page)

  • Enable plugins (advanced): the master switch. Off by default; turning it on requires acknowledging the security warning. Off means no plugin code runs at all.
  • Per-plugin toggle: enable or disable each plugin individually (only when the master switch is on).
  • Reload plugins: re-scan the folder and re-activate. Use this after adding a plugin or editing its code. A reload picks up your edits, because the require cache is cleared per plugin on activation.
  • Open folder: reveal the plugins directory in your OS file manager.

Error handling

One bad plugin never takes down the app:

  • A plugin with an invalid or missing plugin.json is listed with an error and is not run.
  • If a plugin throws during activation, the error is caught and shown on its row; other plugins are unaffected.
  • Errors thrown inside your event handlers are caught and logged, not propagated.

Don’t have BetterStrap yet?

BetterStrap is a free Roblox bootstrapper for Windows and Mac. Plugins stay off until you switch them on.

Free · Windows 10 & 11 · macOS 11+ (Apple silicon & Intel) · On a Mac? Get the macOS build