Integration recipes
These snippets show how to combine the API with a mod config, translations and a dedicated settings screen. Replace ExampleConfig and ExampleAdvancedScreen with your project’s own classes.
1 · Three options and a custom icon
A category containing a toggle, cycle choice, action button and custom icon.
private fun registerVisualSettings() {
ElytraF5SettingsApi.registerCategory(
SettingsCategory(
id = "examplemod:visuals",
title = Component.translatable("examplemod.settings.category.visuals"),
icon = Identifier.fromNamespaceAndPath(
"examplemod", "textures/gui/settings/visuals.png"
),
options = listOf(
BooleanSettingOption(
id = "particles",
title = Component.translatable("examplemod.settings.particles"),
description = Component.translatable("examplemod.settings.particles.description"),
currentValue = { ExampleConfig.particles },
onChanged = { value ->
ExampleConfig.particles = value
ExampleConfig.save()
}
),
ChoiceSettingOption(
id = "quality",
title = Component.translatable("examplemod.settings.quality"),
description = Component.translatable("examplemod.settings.quality.description"),
choices = listOf(
SettingsChoice("low", Component.translatable("examplemod.quality.low")),
SettingsChoice("balanced", Component.translatable("examplemod.quality.balanced")),
SettingsChoice("high", Component.translatable("examplemod.quality.high"))
),
currentChoice = { ExampleConfig.quality },
onChanged = { value ->
ExampleConfig.quality = value
ExampleConfig.save()
}
),
ActionSettingOption(
id = "advanced",
title = Component.translatable("examplemod.settings.advanced"),
description = Component.translatable("examplemod.settings.advanced.description"),
buttonTitle = Component.translatable("examplemod.settings.open_advanced"),
action = { parent ->
Minecraft.getInstance().gui.setScreen(ExampleAdvancedScreen(parent))
}
)
)
)
)
}Imports for this snippet: net.minecraft.network.chat.Component, net.minecraft.resources.Identifier, net.minecraft.client.Minecraft and net.turtle.ef5am.api.client.settings.*.
Java example: a boolean setting
The API is written in Kotlin. Java callbacks use Kotlin’s Function0 and Function1 interfaces. A change callback returns Unit.INSTANCE.
import java.util.List;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import net.minecraft.network.chat.Component;
import net.turtle.ef5am.api.client.settings.*;
ElytraF5SettingsApi.registerCategory(new SettingsCategory(
"examplemod:visuals",
Component.translatable("examplemod.settings.category.visuals"),
List.of(new BooleanSettingOption(
"particles",
Component.translatable("examplemod.settings.particles"),
Component.translatable("examplemod.settings.particles.description"),
(Function0<Boolean>) () -> ExampleConfig.isParticlesEnabled(),
(Function1<Boolean, Unit>) value -> {
ExampleConfig.setParticlesEnabled(value);
ExampleConfig.save();
return Unit.INSTANCE;
}
)),
null
));Compile with ElytraF5’s development jar and Kotlin libraries. The Kotlin API is most concise from Kotlin; Java integrations should verify generated signatures against the exact API version they target.
2 · Fabric client entrypoint
class ExampleModClient : ClientModInitializer {
override fun onInitializeClient() {
ExampleConfig.load()
registerVisualSettings()
}
}Load your config before registration: ElytraF5 reads the value provider when the screen is built. Avoid disk I/O inside UI callbacks.
3 · Add translations
Create src/client/resources/assets/examplemod/lang/en_us.json and fr_fr.json (or the resource path configured by your project):
{
"examplemod.settings.category.visuals": "Visuals",
"examplemod.settings.particles": "Particles",
"examplemod.settings.particles.description": "Shows custom particle effects.",
"examplemod.settings.quality": "Quality",
"examplemod.settings.quality.description": "Selects the detail level.",
"examplemod.quality.low": "Low",
"examplemod.quality.balanced": "Balanced",
"examplemod.quality.high": "High",
"examplemod.settings.advanced": "Advanced options",
"examplemod.settings.advanced.description": "Opens advanced controls.",
"examplemod.settings.open_advanced": "Configure"
}Translation keys belong to your mod’s namespace. They are not added to ElytraF5’s language files.
4 · Child screen with a back path
class ExampleAdvancedScreen(private val parent: Screen) :
Screen(Component.translatable("examplemod.settings.advanced.title")) {
override fun onClose() {
Minecraft.getInstance().gui.setScreen(parent)
}
}The supplied parent is the currently open ElytraF5 screen. Do not create a fresh settings screen when closing; restore the parent instance.
5 · Optional integration
If ElytraF5 is optional, declare it under suggests and check that it is loaded before running the integration bridge. Keep all ElytraF5 class references in a separate bridge class that is only called when the mod is present.
override fun onInitializeClient() {
ExampleConfig.load()
if (FabricLoader.getInstance().isModLoaded("ef5am")) {
ExampleElytraF5Bridge.register()
}
}If the dependency is required, declare it under depends. Never import the client API into shared server code.
6 · Callback behavior
currentValueandcurrentChoiceprovide state when controls are created.onChangedreceives the newly selected value. Update your source of truth and persist it according to your config strategy.- Controls do not continuously poll your values. If config changes elsewhere while the screen is open, close and reopen the screen to rebuild controls.
- Avoid expensive work, network access or resource loading in UI callbacks.
The API provides simple controls. It does not provide automatic persistence, sliders, nested categories or access to another mod’s settings.