Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Fabric - toolchain for Minecraft
https://github.com/fabricmc/fabric-docs
Admin
Official curated documentation for Fabric, a modding toolchain for Minecraft.
Tokens:
125,417
Snippets:
1,562
Trust Score:
7.8
Update:
1 week ago
Context
Skills
Chat
Benchmark
78.8
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Fabric Documentation Fabric is a lightweight modding toolchain for Minecraft: Java Edition designed to be simple and easy-to-use. The Fabric project consists of three main components: Fabric Loader (a flexible, platform-independent mod loader), Fabric API (a set of APIs and tools for mod development), and Fabric Loom (a Gradle plugin for developing and debugging mods). This documentation provides comprehensive guides for creating Minecraft mods using Fabric, covering everything from basic setup to advanced topics like rendering, networking, and data generation. The documentation is organized into player guides (for installing and troubleshooting mods) and developer guides (for creating mods). Developer topics include project setup, creating items and blocks, handling events, implementing commands, networking between client and server, data generation for JSON files, and rendering. All code examples are available in a reference mod that accompanies the documentation, providing working implementations of every concept covered. ## Creating a New Mod Project Generate a new Fabric mod project using the official template generator or by cloning the example mod repository. The generator creates all necessary files including build.gradle, fabric.mod.json, and the basic mod structure. ```bash # Clone the example mod repository manually git clone https://github.com/FabricMC/fabric-example-mod/ my-mod # Or use the template generator at https://fabricmc.net/develop/template/ # Install dependencies and run the development server cd my-mod ./gradlew genSources ./gradlew runClient ``` ## Registering Items Create custom items by registering them with Minecraft's item registry. Use a helper method to simplify registration and ensure all items are properly initialized. ```java public class ModItems { // Helper method to register items public static <T extends Item> T register(String name, Function<Item.Properties, T> itemFactory, Item.Properties settings) { ResourceKey<Item> itemKey = ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath("example-mod", name)); T item = itemFactory.apply(settings.setId(itemKey)); Registry.register(BuiltInRegistries.ITEM, itemKey, item); return item; } // Register a simple item public static final Item SUSPICIOUS_SUBSTANCE = register( "suspicious_substance", Item::new, new Item.Properties() ); // Register a food item with effects public static final Consumable POISON_CONSUMABLE = Consumables.defaultFood() .onConsume(new ApplyStatusEffectsConsumeEffect( new MobEffectInstance(MobEffects.POISON, 6 * 20, 1), 1.0f)) .build(); public static final FoodProperties POISON_FOOD = new FoodProperties.Builder() .alwaysEdible() .build(); public static final Item POISONOUS_APPLE = register( "poisonous_apple", Item::new, new Item.Properties().food(POISON_FOOD, POISON_CONSUMABLE) ); // Initialize in mod entrypoint public static void initialize() { // Add item to creative tab CreativeModeTabEvents.modifyOutputEvent(CreativeModeTabs.INGREDIENTS) .register((creativeTab) -> creativeTab.accept(ModItems.SUSPICIOUS_SUBSTANCE)); } } ``` ## Registering Blocks Create custom blocks with associated block items. Blocks require additional assets including blockstates, models, and textures. ```java public class ModBlocks { private static Block register(String name, Function<BlockBehaviour.Properties, Block> blockFactory, BlockBehaviour.Properties settings, boolean shouldRegisterItem) { ResourceKey<Block> blockKey = ResourceKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("example-mod", name)); Block block = blockFactory.apply(settings.setId(blockKey)); if (shouldRegisterItem) { ResourceKey<Item> itemKey = ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath("example-mod", name)); BlockItem blockItem = new BlockItem(block, new Item.Properties().setId(itemKey).useBlockDescriptionPrefix()); Registry.register(BuiltInRegistries.ITEM, itemKey, blockItem); } return Registry.register(BuiltInRegistries.BLOCK, blockKey, block); } // Simple block with dirt-like properties public static final Block CONDENSED_DIRT = register( "condensed_dirt", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true // Register block item ); // Rotatable pillar block (like logs) public static final Block CONDENSED_OAK_LOG = register( "condensed_oak_log", RotatedPillarBlock::new, BlockBehaviour.Properties.of().sound(SoundType.WOOD), true ); // Block with dynamic light level public static final Block PRISMARINE_LAMP = register( "prismarine_lamp", PrismarineLampBlock::new, BlockBehaviour.Properties.of() .sound(SoundType.LANTERN) .lightLevel(PrismarineLampBlock::getLuminance), true ); public static void initialize() { CreativeModeTabEvents.modifyOutputEvent(CreativeModeTabs.BUILDING_BLOCKS) .register((creativeTab) -> creativeTab.accept(ModBlocks.CONDENSED_DIRT.asItem())); } } ``` ## Handling Events React to game events using Fabric API's event system. Register callbacks to respond to player actions, world changes, and more. ```java public class ExampleModEvents { public static void initialize() { // Damage player when hitting blocks that don't drop items AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> { BlockState state = world.getBlockState(pos); if (state.isAir()) { return InteractionResult.PASS; } // Check if block drops nothing when mined by hand if (state.getBlock().getDrops(state, (ServerLevel) world, pos, null).isEmpty()) { player.damage(player.getDamageSources().generic(), 1.0f); } return InteractionResult.PASS; }); // Modify loot tables - add eggs to coal ore drops LootTableEvents.MODIFY.register((key, tableBuilder, source, registries) -> { if (key.equals(BuiltInLootTables.COAL_ORE)) { LootPool.Builder poolBuilder = LootPool.lootPool() .add(ItemEntry.builder(Items.EGG)); tableBuilder.pool(poolBuilder); } }); } } ``` ## Creating Custom Events Define custom events for other mods to hook into. Use EventFactory to create array-backed events with proper result handling. ```java public interface SheepShearCallback { Event<SheepShearCallback> EVENT = EventFactory.createArrayBacked(SheepShearCallback.class, (listeners) -> (player, sheep) -> { for (SheepShearCallback listener : listeners) { InteractionResult result = listener.interact(player, sheep); if (result != InteractionResult.PASS) { return result; } } return InteractionResult.PASS; }); InteractionResult interact(Player player, Sheep sheep); } // Trigger the event from a mixin @Mixin(Sheep.class) public class SheepEntityMixin { @Inject(method = "mobInteract", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Sheep;shear(...)V"), cancellable = true) private void onShear(Player player, InteractionHand hand, CallbackInfoReturnable<InteractionResult> cir) { InteractionResult result = SheepShearCallback.EVENT.invoker() .interact(player, (Sheep) (Object) this); if (result == InteractionResult.FAIL) { cir.setReturnValue(InteractionResult.FAIL); } } } // Register a listener SheepShearCallback.EVENT.register((player, sheep) -> { sheep.level().addFreshEntity(new ItemEntity( sheep.level(), sheep.getX(), sheep.getY(), sheep.getZ(), new ItemStack(Items.DIAMOND))); return InteractionResult.FAIL; // Cancel normal wool drop }); ``` ## Registering Commands Create server and client commands using Brigadier. Commands can have arguments, requirements, and sub-commands. ```java public class ExampleModCommands { public static void initialize() { // Basic command CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { dispatcher.register(Commands.literal("test_command") .executes(context -> { context.getSource().sendSuccess( () -> Component.literal("Hello from test command!"), false); return Command.SINGLE_SUCCESS; })); }); // Command with permission requirement CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { dispatcher.register(Commands.literal("admin_command") .requires(source -> source.hasPermission(2)) // Require operator level 2 .executes(context -> { context.getSource().sendSuccess( () -> Component.literal("Admin command executed!"), true); return Command.SINGLE_SUCCESS; })); }); // Command with sub-commands CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { dispatcher.register(Commands.literal("mymod") .then(Commands.literal("reload") .executes(context -> { // Reload logic return Command.SINGLE_SUCCESS; })) .then(Commands.literal("status") .executes(context -> { context.getSource().sendSuccess( () -> Component.literal("Mod is running!"), false); return Command.SINGLE_SUCCESS; }))); }); } } // Client-side command (in client initializer) ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { dispatcher.register(ClientCommands.literal("client_ping") .executes(context -> { context.getSource().sendFeedback(Component.literal("Pong from client!")); return Command.SINGLE_SUCCESS; })); }); ``` ## Networking - Server to Client Send custom packets from server to client using Fabric's networking API. Define payloads as records with codecs for serialization. ```java // Define the payload public record SummonLightningPayload(BlockPos pos) implements CustomPacketPayload { public static final Identifier ID = Identifier.fromNamespaceAndPath("example-mod", "summon_lightning"); public static final CustomPayload.Type<SummonLightningPayload> TYPE = new CustomPayload.Type<>(ID); public static final StreamCodec<RegistryByteBuf, SummonLightningPayload> CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, SummonLightningPayload::pos, SummonLightningPayload::new); @Override public Type<? extends CustomPacketPayload> type() { return TYPE; } } // Register in common initializer public class ExampleMod implements ModInitializer { @Override public void onInitialize() { PayloadTypeRegistry.clientboundPlay().register(SummonLightningPayload.TYPE, SummonLightningPayload.CODEC); } } // Send packet from server (e.g., in an item's use method) public class LightningTaterItem extends Item { @Override public InteractionResult use(Level level, Player user, InteractionHand hand) { if (level.isClientSide()) { return InteractionResult.PASS; } SummonLightningPayload payload = new SummonLightningPayload(user.blockPosition()); for (ServerPlayer player : PlayerLookup.world((ServerLevel) level)) { ServerPlayNetworking.send(player, payload); } return InteractionResult.SUCCESS; } } // Receive on client (in client initializer) public class ExampleModClient implements ClientModInitializer { @Override public void onInitializeClient() { ClientPlayNetworking.registerGlobalReceiver(SummonLightningPayload.TYPE, (payload, context) -> { BlockPos pos = payload.pos(); LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(context.player().level(), EntitySpawnReason.TRIGGERED); bolt.setPos(pos.getX(), pos.getY(), pos.getZ()); context.player().level().addFreshEntity(bolt); }); } } ``` ## Networking - Client to Server Send packets from client to server with proper validation on the server side. ```java // Define serverbound payload public record GlowingEffectPayload(int entityId) implements CustomPacketPayload { public static final Identifier ID = Identifier.fromNamespaceAndPath("example-mod", "give_glowing"); public static final CustomPayload.Type<GlowingEffectPayload> TYPE = new CustomPayload.Type<>(ID); public static final StreamCodec<RegistryByteBuf, GlowingEffectPayload> CODEC = StreamCodec.composite(ByteBufCodecs.INT, GlowingEffectPayload::entityId, GlowingEffectPayload::new); @Override public Type<? extends CustomPacketPayload> type() { return TYPE; } } // Register in common initializer PayloadTypeRegistry.serverboundPlay().register(GlowingEffectPayload.TYPE, GlowingEffectPayload.CODEC); // Send from client UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> { if (!world.isClientSide()) return InteractionResult.PASS; if (!player.getItemInHand(hand).is(Items.POISONOUS_POTATO)) return InteractionResult.PASS; GlowingEffectPayload payload = new GlowingEffectPayload(entity.getId()); ClientPlayNetworking.send(payload); return InteractionResult.SUCCESS; }); // Receive on server (in common initializer) ServerPlayNetworking.registerGlobalReceiver(GlowingEffectPayload.TYPE, (payload, context) -> { ServerPlayer player = context.player(); Entity entity = player.level().getEntity(payload.entityId()); // Validate the request if (entity == null) return; if (!(entity instanceof LivingEntity living)) return; if (player.distanceTo(entity) > 5.0) return; // Range check living.addEffect(new MobEffectInstance(MobEffects.GLOWING, 10 * 20)); // 10 seconds }); ``` ## Data Generation Setup Use data generation to programmatically create JSON files for recipes, loot tables, tags, and models. ```java // Data generator entrypoint (in fabric.mod.json: "fabric-datagen": ["com.example.docs.datagen.ExampleModDataGenerator"]) public class ExampleModDataGenerator implements DataGeneratorEntrypoint { @Override public void onInitializeDataGenerator(FabricDataGenerator generator) { FabricDataGenerator.Pack pack = generator.createPack(); // Add providers for different data types pack.addProvider(ExampleModRecipeProvider::new); pack.addProvider(ExampleModBlockLootTableProvider::new); pack.addProvider(ExampleModModelProvider::new); pack.addProvider(ExampleModTagProvider::new); } } // Recipe provider example public class ExampleModRecipeProvider extends FabricRecipeProvider { public ExampleModRecipeProvider(FabricDataOutput output, CompletableFuture<HolderLookup.Provider> registriesFuture) { super(output, registriesFuture); } @Override public void buildRecipes(RecipeOutput exporter) { // Shaped recipe ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.SUSPICIOUS_SUBSTANCE) .pattern("###") .pattern("#X#") .pattern("###") .define('#', Items.DIRT) .define('X', Items.DIAMOND) .unlockedBy("has_diamond", has(Items.DIAMOND)) .save(exporter); // Shapeless recipe ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ModItems.POISONOUS_APPLE) .requires(Items.APPLE) .requires(Items.SPIDER_EYE) .unlockedBy("has_apple", has(Items.APPLE)) .save(exporter); } } // Run data generation // ./gradlew runDatagen // Output files are created in src/main/generated ``` ## Custom Creative Tabs Create custom creative mode tabs to organize your mod's items. ```java public class ModItems { public static final ResourceKey<CreativeModeTab> CUSTOM_TAB_KEY = ResourceKey.create( BuiltInRegistries.CREATIVE_MODE_TAB.key(), Identifier.fromNamespaceAndPath("example-mod", "creative_tab") ); public static final CreativeModeTab CUSTOM_TAB = FabricCreativeModeTab.builder() .icon(() -> new ItemStack(ModItems.GUIDITE_SWORD)) .title(Component.translatable("creativeTab.example-mod")) .displayItems((params, output) -> { output.accept(ModItems.SUSPICIOUS_SUBSTANCE); output.accept(ModItems.POISONOUS_APPLE); output.accept(ModBlocks.CONDENSED_DIRT); // Add custom ItemStack with modified components ItemStack customStack = new ItemStack(Items.SEA_PICKLE); customStack.set(DataComponents.ITEM_NAME, Component.literal("Pickle Rick")); customStack.set(DataComponents.LORE, new ItemLore( List.of(Component.literal("I'm pickle riiick!!")))); output.accept(customStack); }) .build(); public static void initialize() { Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB, CUSTOM_TAB_KEY, CUSTOM_TAB); } } // Translation in assets/example-mod/lang/en_us.json: // { "creativeTab.example-mod": "Example Mod Items" } ``` ## Custom Armor and Tools Create custom armor sets and tools with custom materials and properties. ```java // Define tool material public static final ToolMaterial GUIDITE_TOOL_MATERIAL = new ToolMaterial( BlockTags.INCORRECT_FOR_WOODEN_TOOL, // Mining level tag 455, // Durability 5.0F, // Mining speed 1.5F, // Attack damage bonus 22, // Enchantability GuiditeArmorMaterial.REPAIRS_GUIDITE_ARMOR // Repair ingredient tag ); // Register tools public static final Item GUIDITE_SWORD = register( "guidite_sword", Item::new, new Item.Properties().sword(GUIDITE_TOOL_MATERIAL, 1f, 1f) ); public static final Item GUIDITE_AXE = register( "guidite_axe", settings -> new AxeItem(GUIDITE_TOOL_MATERIAL, 5.0F, -3.0F, settings), new Item.Properties() ); // Armor material class public class GuiditeArmorMaterial { public static final int BASE_DURABILITY = 15; public static final TagKey<Item> REPAIRS_GUIDITE_ARMOR = TagKey.create( Registries.ITEM, Identifier.fromNamespaceAndPath("example-mod", "repairs_guidite_armor")); public static final EquipmentAsset INSTANCE = EquipmentAssets.register( Identifier.fromNamespaceAndPath("example-mod", "guidite"), ArmorMaterial.builder() .durability(BASE_DURABILITY) .defense(ArmorType.HELMET, 3) .defense(ArmorType.CHESTPLATE, 8) .defense(ArmorType.LEGGINGS, 6) .defense(ArmorType.BOOTS, 3) .enchantmentValue(9) .repairIngredient(REPAIRS_GUIDITE_ARMOR) .toughness(2.0F) .knockbackResistance(0.1F) .build() ); } // Register armor pieces public static final Item GUIDITE_HELMET = register( "guidite_helmet", Item::new, new Item.Properties() .humanoidArmor(GuiditeArmorMaterial.INSTANCE, ArmorType.HELMET) .durability(ArmorType.HELMET.getDurability(GuiditeArmorMaterial.BASE_DURABILITY)) ); ``` ## Item and Block Interactions Create custom item behavior by overriding interaction methods. ```java public class LightningStick extends Item { public LightningStick(Properties settings) { super(settings); } @Override public InteractionResult use(Level level, Player user, InteractionHand hand) { if (level.isClientSide()) { return InteractionResult.PASS; } // Spawn lightning at player's position BlockPos pos = user.blockPosition(); LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(level, EntitySpawnReason.TRIGGERED); bolt.setPos(pos.getX(), pos.getY(), pos.getZ()); level.addFreshEntity(bolt); // Damage the item user.getItemInHand(hand).hurtAndBreak(1, user, LivingEntity.getSlotForHand(hand)); return InteractionResult.SUCCESS; } @Override public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> tooltip, TooltipFlag flag) { tooltip.accept(Component.translatable("item.example-mod.lightning_stick.tooltip") .withStyle(ChatFormatting.GRAY)); } } ``` ## Compostable Items and Fuel Register items as compostable or as fuel using Fabric API registries. ```java public static void initialize() { // Make item compostable (30% chance to increase composter level) CompostableRegistry.INSTANCE.add(ModItems.SUSPICIOUS_SUBSTANCE, 0.3f); // Make item usable as fuel (burn time in ticks, 20 ticks = 1 second) FuelValueEvents.BUILD.register((builder, context) -> { builder.add(ModItems.SUSPICIOUS_SUBSTANCE, 30 * 20); // 30 seconds burn time }); } ``` Fabric provides a comprehensive modding framework for Minecraft that emphasizes simplicity and interoperability. The event system allows mods to hook into game behavior without conflicting, while the networking API enables safe client-server communication. Data generation streamlines the creation of JSON assets, and the registry system ensures all game objects are properly registered. For developers new to Fabric, the recommended approach is to start with the template generator, follow the getting-started guides to create basic items and blocks, then progressively add more complex features like events, commands, and networking. The reference mod accompanying this documentation provides working examples of every concept, making it an invaluable resource for understanding implementation patterns.