TabbyLib Wiki

How to build a config for your mod with TabbyLib

Setup

Add the TabbyLib maven repository and the dependency for your loader to your build.gradle. Pick your Minecraft version and the code adapts.

repositories {
    maven { url = "https://avie.cc/maven" }
}

dependencies {
    // Fabric (on 1.21.1: modImplementation)
    implementation "me.avie29.tabbylib:tabbylib:1.0.0+26.3"

    // NeoForge
    implementation "me.avie29.tabbylib:tabbylib-neoforge:1.0.0+26.3"

    // Forge (on 1.20.1: implementation fg.deobf("me.avie29.tabbylib:tabbylib-forge:1.0.0+1.20.1"))
    implementation "me.avie29.tabbylib:tabbylib-forge:1.0.0+26.3"
}

Also add TabbyLib as a dependency of your mod, so players get a message when it is missing:

// fabric.mod.json
"depends": {
    "tabbylib": ">=1.0.0"
}

# neoforge.mods.toml (NeoForge) / mods.toml (Forge: mandatory=true instead of type)
[[dependencies.my_mod]]
modId = "tabbylib"
type = "required"
versionRange = "[1.0.0,)"
ordering = "AFTER"
side = "CLIENT"

Your first config

Every setting is an Option object. Create them as constants and put them into a TabbyConfig. build() registers the config with TabbyLib and loads the file config/<modid>.json – it is created when it does not exist yet.

public final class MyConfig {
    public static final BooleanOption SHOW_HUD = BooleanOption.builder("showHud", true).build();
    public static final IntOption SIZE = IntOption.builder("size", 10).slider(1, 50, 1).build();
    public static final ColorOption COLOR = ColorOption.builder("color", 0xFFFFFFFF)
        .alpha()
        .dependsOn(SHOW_HUD)
        .build();

    private static TabbyConfig config;

    // Call this in your client initializer (Fabric) / mod constructor (NeoForge, Forge)
    public static void init() {
        config = TabbyConfig.builder("my-mod")
            .category("general", category -> category
                .add(SHOW_HUD)
                .group("look", group -> group.add(SIZE, COLOR)))
            .build();
    }

    public static TabbyConfig get() {
        return config;
    }
}

Read values with get(). When you change a value in code, save the config afterwards:

if (MyConfig.SHOW_HUD.get()) {
    drawHud(MyConfig.SIZE.get(), MyConfig.COLOR.get());
}

MyConfig.SHOW_HUD.set(false);
MyConfig.get().save();
In the config screen players first only change a pending value (getPending()). Only "Save" moves it into get(). Use getPending() for previews that should react right away.

Option types

Option In the screen Example
BooleanOption On / off button BooleanOption.builder("enabled", true)
IntOption Slider (slider) or text field (range) IntOption.builder("size", 10).slider(1, 50, 1)
DoubleOption Slider or text field for decimals DoubleOption.builder("scale", 1.0).slider(0.5, 3.0, 0.25)
StringOption Text field, optionally validated StringOption.builder("prefix", "Day").maxLength(32)
EnumOption Button that cycles through the values (right click goes back) EnumOption.builder("mode", Mode.SIMPLE)
ColorOption Hex field and color picker, with alpha() also transparency ColorOption.builder("color", 0xFFFF5555)
KeyBindOption Key binding, in sync with Minecraft's controls KeyBindOption.builder("toggleKey", myKeyMapping)
StringListOption List with add, remove and reorder StringListOption.builder("players", List.of())
HudPositionOption Drag and drop editor with snapping see HUD

Every builder shares these extra settings:

BooleanOption.builder("glow", false)
    .dependsOn(SHOW_HUD)                        // greyed out while SHOW_HUD is off
    .enabledWhen(() -> SIZE.getPending() > 5)   // or any other condition
    .visibleWhen(() -> MODE.getPending() == Mode.ADVANCED)  // hidden instead of greyed out
    .requiresRestart()                          // shows a hint and a message after saving
    .onChange(value -> reloadRenderer())        // runs after the value was saved
    .binding(() -> Settings.glow, value -> Settings.glow = value)  // use an existing field
    .name(Component.literal("Glow"))            // instead of the translation key
    .tooltip(Component.literal("Makes it shine"))
    .hidden()                                   // saved, but not shown in the screen
    .build();

Categories, groups, texts and buttons

Every category becomes a tab. Groups are collapsible sections inside a category. You can also add lines of text (label) and buttons that run code (ActionEntry).

TabbyConfig.builder("my-mod")
    .category("general", category -> category
        .label(Component.translatable("config.my-mod.general.intro"))
        .add(SHOW_HUD, SIZE)
        .group("colors", group -> group
            .description(Component.literal("Everything about colors"))
            .collapsed()                        // closed when the screen opens
            .add(COLOR, BACKGROUND)))
    .category("advanced", category -> category
        .add(DEBUG)
        .add(ActionEntry.of(
            Component.literal("Cache"),
            Component.literal("Clear"),
            () -> MyMod.clearCache())))
    .build();

Translations

TabbyLib looks up all names in your mod's language files (assets/<modid>/lang/en_us.json). Tooltips and enum names are optional.

{
  "config.my-mod.showHud": "Show HUD",
  "config.my-mod.showHud.tooltip": "Shows or hides the whole HUD.",
  "config.my-mod.mode.simple": "Simple",
  "config.my-mod.mode.advanced": "Advanced",
  "config.my-mod.category.general": "General",
  "config.my-mod.group.colors": "Colors"
}
NeoForge and Forge do not allow dashes in mod ids. If your mod is called my_mod there but my-mod on Fabric, set .translationId("my-mod") – then all loaders share the same language files.

HUD elements

A HudPositionOption stores where a HUD element is. The position is attached to one of nine anchors of the screen and stays there when the window size or GUI scale changes. In the screen it opens an editor where players drag the element around – with snapping to the edges, the center and a grid.

public static final HudPositionOption POSITION = HudPositionOption.builder("position",
    HudPosition.of(HudPosition.CENTER, HudPosition.END, 0, -50),   // bottom center, 50 px above the bottom
    MyHud::preview).build();

// Drawing the HUD
if (!TabbyLibApi.isHudEditorOpen()) {   // the editor draws its own preview
    HudPosition position = MyConfig.POSITION.get();
    int x = position.x(screenWidth, width);
    int y = position.y(screenHeight, height);
    draw(graphics, x, y);
}

// Preview for the editor (uses the unsaved values)
public static HudPreview preview() {
    return new HudPreview() {
        public int width() { return currentWidth(); }
        public int height() { return currentHeight(); }
        public void render(GuiGraphicsExtractor graphics, int x, int y) { draw(graphics, x, y); }
    };
}

// Open the editor directly, e.g. from a command
TabbyLibApi.openHudEditor(MyConfig.POSITION);

On 1.21.1 and 1.20.1 the graphics class is called GuiGraphics instead of GuiGraphicsExtractor.

Advanced

Opening the config screen

TabbyLibApi.openScreen("my-mod");                 // e.g. from your own command
Screen screen = TabbyLibApi.createScreen(parent, "my-mod");

// Mod Menu (Fabric)
public ConfigScreenFactory<?> getModConfigScreenFactory() {
    return parent -> TabbyLibApi.createScreen(parent, "my-mod");
}

On NeoForge and Forge, TabbyLib connects the "Config" button of the mod list to your config automatically.

File name, display and saving

TabbyConfig.builder("my-mod")
    .fileName("mymod")                          // keeps an existing config/mymod.json
    .name(Component.literal("My Mod"))          // instead of the name from the mod metadata
    .icon(Identifier.fromNamespaceAndPath("my-mod", "textures/gui/icon.png"))
    .onSave(() -> MyMod.reload())               // runs after the file was written
    ...

Migrating old config files

If you used your own config file before, you can convert it while loading. Return true and the file is written again afterwards.

.migration(json -> {
    if (!json.has("oldName")) {
        return false;
    }
    json.add("newName", json.remove("oldName"));
    return true;
})

Available versions

Minecraft Fabric NeoForge Forge
26.1 · 26.1.1 · 26.1.2 · 26.2 · 26.3tabbylibtabbylib-neoforgetabbylib-forge
1.21.1tabbylibtabbylib-neoforgetabbylib-forge
1.20.1––tabbylib-forge

The version is always 1.0.0+<Minecraft version>, for example me.avie29.tabbylib:tabbylib-neoforge:1.0.0+1.21.1. All versions have the same features and API.