This guide collects low-friction patterns and copy-pasteable scaffolds so new modders can iterate quickly with FoxAPI. Everything here leans on the existing helper families and keeps the amount of custom boilerplate as small as possible.
Use ContentCreationHelper to wire a custom item into the registry and a
creative tab without manually touching event listeners:
public final class ModItems {
public static final Identifier GLOVETRAP_ID = id("glovetrap");
public static Item GLOVETRAP;
public static void init() {
GLOVETRAP = ContentCreationHelper.item(GLOVETRAP_ID)
.settings(settings -> settings.maxCount(16))
.factory(settings -> new Item(settings))
.addToGroupAfter(ItemGroups.COMBAT, Items.IRON_SWORD)
.register();
}
private static Identifier id(String path) {
return new Identifier("example", path);
}
}This pattern keeps the fields you need for later lookups while hiding the creative-tab wiring and registration boilerplate.
Pair the content helper with a simple block entity class to get ticking state and rendering hooks without duplicating registration code:
public class ReactorBlock extends Block {
public ReactorBlock(Settings settings) { super(settings); }
}
public class ReactorBlockEntity extends BlockEntity {
public ReactorBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.REACTOR, pos, state);
}
public static void tick(World world, BlockPos pos, BlockState state, ReactorBlockEntity be) {
// tick logic here
}
}
public final class ModBlocks {
public static final Identifier REACTOR_ID = id("reactor");
public static Block REACTOR;
public static BlockEntityType<ReactorBlockEntity> REACTOR_BE;
public static void init() {
REACTOR = ContentCreationHelper.block(REACTOR_ID, settings -> new ReactorBlock(settings.strength(4.0F)))
.withBlockItem(item -> new BlockItem(item, new Item.Settings().maxCount(1)))
.addBlockItemToGroup(ItemGroups.REDSTONE)
.register();
REACTOR_BE = ContentCreationHelper.blockEntity(REACTOR_ID, ReactorBlockEntity::new)
.blocks(() -> REACTOR)
.onRegister(type -> BlockEntityRendererFactories.register(type, ReactorRenderer::new))
.configure(builder -> builder.ticker((world, pos, state, be) -> ReactorBlockEntity.tick(world, pos, state, be)))
.register();
}
private static Identifier id(String path) {
return new Identifier("example", path);
}
}The helper ensures both registrations occur with matching IDs while exposing hooks for renderers and custom tickers.
Combine DataDrivenHelper and CraftingGridHelper to emit the JSON
alongside the code change:
public final class ModAssets {
public static void generate(FabricDataOutput output, Item registeredItem) {
DataDrivenHelper.create(output)
.itemModel(registeredItem, model -> model.parent("item/generated").texture("layer0", "example:item/glovetrap"))
.shapedRecipe(registeredItem, recipe -> recipe
.patternLine("SSS")
.patternLine("S S")
.patternLine("SSS")
.key('S', Items.STRING))
.write();
}
}Calling write() queues both the model and recipe without hand-authoring
files in resources/.
- Action gating: pair
CooldownswithTickSchedulerto debounce right-click abilities or timed charges without scattered counters. - Damage math: run hit calculations through
DamageHelperandEnchantHelperso knockback, crits and enchant caps stay consistent with vanilla expectations. - Status + attributes:
EffectHelperandAttributeHelperlet you attach temporary buffs/debuffs with deterministic IDs, which makes them easy to remove or refresh. - Targeting:
RaycastHelpermirrors vanilla block/entity raycasts with fewer arguments, which keeps projectile or spell targeting code readable. - Client options:
OptionsBindingHelperbindsSimpleOptioninstances to the customStyledCycleButtonso you can add toggles without duplicating GUI glue code.
- Keep creative tabs tidy with
addToGroupBefore/AfterandaddBlockItemToGroupBefore/Afterto avoid manualItemGroupEventslisteners. - For custom screens, define slot indices as constants and reuse them
when creating
Slotinstances; it keeps handler/renderer alignment obvious even for beginners. - When syncing handler state, lean on
FoxapiPacketto round-trip the payload locally before you add a real server listener.
- Prefer helper lifecycle callbacks (
onRegister,configure) to stash references or log IDs—the errors surface during init instead of mid gameplay. - Use
WorldStateHelperto persist testing toggles in the save without wiring codecs manually. - Texture experiments can be quick:
TextureMixingHelpercan tint and blend sprites, then export the result for reuse.
- New item: define a static field, register via the item builder,
place it in a creative tab, and add a shaped recipe and generated
model through
DataDrivenHelper. - New block + BE: register the block and block item, wire a block
entity type via
ContentCreationHelper, add a ticker, and generate the blockstate/model variants together. - Networked tool: gate the action with
Cooldowns, marshal the payload throughFoxapiPacket, and useDamageHelper+EffectHelperfor server-side effects.
- Blank item class
public class SimpleItem extends Item {
public SimpleItem(Settings settings) { super(settings); }
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
return TypedActionResult.success(user.getStackInHand(hand));
}
}- Blank block entity
public class SimpleBlockEntity extends BlockEntity {
public SimpleBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.SIMPLE, pos, state);
}
public static void tick(World world, BlockPos pos, BlockState state, SimpleBlockEntity be) {
// tick logic here
}
}Drop these stubs into your project and focus on gameplay logic instead of wiring.