Plexcord LogoPlexcord
Core Concepts

Settings Deep Dive

Master every Plexcord OptionType in detail. Learn BOOLEAN, STRING, NUMBER, SELECT, SLIDER, MULTISELECT, BIGINT, and COMPONENT with real examples.

Settings Deep Dive

The Getting Started guide covered the basics. This guide covers every available OptionType, advanced patterns, and how the settings system works internally.

How Settings Work Internally

When you call definePluginSettings(), Plexcord:

  1. Creates a reactive store backed by localStorage
  2. Generates a settings panel UI automatically
  3. Makes values available via settings.store
  4. Persists changes across Discord restarts
import { definePluginSettings } from "@api/Settings";
import { OptionType } from "@utils/types";

const settings = definePluginSettings({
    myOption: {
        type: OptionType.BOOLEAN,
        description: "My setting",
        default: true
    }
});

// Read:  settings.store.myOption  → boolean
// Write: settings.store.myOption = false  (triggers save + re-render)

All Option Types

OptionType.BOOLEAN

Renders as a toggle switch. The simplest and most common type.

enabled: {
    type: OptionType.BOOLEAN,
    description: "Enable this feature",
    default: true,
    restartNeeded: false,   // Shows "Restart required" message if true
    onChange(value: boolean) {
        console.log("Toggled:", value);
    }
},

Options:

PropertyTypeRequiredDescription
typeOptionType.BOOLEAN
descriptionstringLabel in settings UI
defaultbooleanInitial value
restartNeededbooleanShow restart warning
hiddenboolean | () => booleanHide from settings UI
onChange(value) => voidCalled on change

OptionType.STRING

A text input field.

greeting: {
    type: OptionType.STRING,
    description: "Custom greeting message",
    default: "Hello!",
    placeholder: "Enter a message...",
    isValid: (value: string) => {
        if (value.length > 100) return "Max 100 characters";
        return true;
    }
},

Options:

PropertyTypeDescription
defaultstringInitial text value
placeholderstringGray hint text when empty
isValid(v: string) => boolean | stringValidation: return true or error message

OptionType.NUMBER

A numeric input field.

delay: {
    type: OptionType.NUMBER,
    description: "Delay in milliseconds",
    default: 1000,
    isValid: (value: number) => {
        if (value < 0) return "Must be positive";
        if (value > 60000) return "Max 60 seconds";
        return true;
    }
},

OptionType.BIGINT

Like NUMBER but for JavaScript BigInt values. Useful for Discord user/guild IDs.

targetUserId: {
    type: OptionType.BIGINT,
    description: "Target user's Discord ID",
    default: 0n,    // BigInt literal
    isValid: (value: bigint) => {
        if (value <= 0n) return "Enter a valid Discord ID";
        return true;
    }
},

OptionType.SELECT

A dropdown with defined options.

logLevel: {
    type: OptionType.SELECT,
    description: "Logging verbosity",
    default: "warn",
    options: [
        { label: "Errors only", value: "error" },
        { label: "Warnings", value: "warn", default: true },
        { label: "All messages", value: "info" },
        { label: "Verbose debug", value: "debug" }
    ]
},

The default: true on an option marks the pre-selected option in the UI. The setting's default value should match the selected option's value.


OptionType.MULTISELECT

A multi-selection list. The value is an array.

features: {
    type: OptionType.MULTISELECT,
    description: "Enable specific features",
    default: ["timestamps", "reactions"],
    options: [
        { label: "Message Timestamps", value: "timestamps", default: true },
        { label: "Reaction Count", value: "reactions", default: true },
        { label: "User Badges", value: "badges" },
        { label: "Read Receipts", value: "receipts" }
    ]
},

Reading the value:

start() {
    const enabled = settings.store.features;  // string[]
    if (enabled.includes("timestamps")) {
        // Show timestamps feature
    }
},

OptionType.SLIDER

A visual range slider.

opacity: {
    type: OptionType.SLIDER,
    description: "Overlay opacity percentage",
    default: 80,
    markers: [0, 20, 40, 60, 80, 100],
    stickToMarkers: true,   // Snaps to marker values
    // stickToMarkers: false  → allows any value in range
},

OptionType.COMPONENT

Renders a custom React component inside the settings panel. Used for buttons, info boxes, or any custom UI.

import { Button } from "@webpack/common";

debugInfo: {
    type: OptionType.COMPONENT,
    description: "Plugin debug tools",
    component: () => {
        const [count, setCount] = React.useState(0);
        return (
            <div style={{ display: "flex", gap: "8px" }}>
                <Button onClick={() => {
                    clearMyCache();
                    setCount(c => c + 1);
                }}>
                    Clear Cache ({count} times)
                </Button>
                <Button
                    color={Button.Colors.RED}
                    onClick={() => resetAllSettings()}
                >
                    Reset Defaults
                </Button>
            </div>
        );
    }
},

Advanced Patterns

Conditional Visibility

Hide settings based on other settings values:

const settings = definePluginSettings({
    useCustomUrl: {
        type: OptionType.BOOLEAN,
        description: "Use a custom API endpoint",
        default: false
    },
    customUrl: {
        type: OptionType.STRING,
        description: "Custom API URL",
        default: "https://api.example.com",
        hidden: () => !settings.store.useCustomUrl
    }
});

restartNeeded

Show a restart warning when a setting is changed. Use this for settings that take effect only at startup.

patchMode: {
    type: OptionType.SELECT,
    description: "How patches are applied",
    default: "auto",
    options: [
        { label: "Automatic (recommended)", value: "auto", default: true },
        { label: "Manual", value: "manual" }
    ],
    restartNeeded: true   // Discord restart required to apply
},

Accessing Settings Outside the Plugin

Other plugins or utilities can read your settings via the Plexcord settings API:

import { Settings } from "@api/Settings";

// Read another plugin's setting
const enabled = Settings.plugins.MyPlugin.someOption;

Avoid reading or writing other plugins' settings unless you explicitly declare a dependency on them. This creates tight coupling.

Type Safety

definePluginSettings is fully typed. TypeScript will infer the correct type for each setting:

const settings = definePluginSettings({
    count: { type: OptionType.NUMBER, description: "...", default: 0 },
    name: { type: OptionType.STRING, description: "...", default: "" },
    enabled: { type: OptionType.BOOLEAN, description: "...", default: true },
    mode: {
        type: OptionType.SELECT,
        description: "...",
        default: "a",
        options: [
            { label: "A", value: "a" as const },
            { label: "B", value: "b" as const }
        ]
    }
});

settings.store.count;    // number
settings.store.name;     // string
settings.store.enabled;  // boolean
settings.store.mode;     // "a" | "b"

Next Steps

On this page