Skip to content

Fabric Registration Guide

This guide covers all the essential registration patterns you’ll need when creating rendering mods with Fabric. Unlike Forge, Fabric uses a more direct approach to registration that’s simpler and more explicit.

Every Fabric mod starts with initialization classes that implement the appropriate interfaces:

// Example: Main mod class with client and server entry points
public class MyRenderingMod implements ModInitializer {
public static final String MOD_ID = "myrenderingmod";
@Override
public void onInitialize() {
// Server-side initialization
MyEntities.register();
MyBlockEntities.register();
MyItems.register();
// Register other mod components...
}
}

Rendering components must be registered on the client side:

// Example: Client entry point for renderer registration
public class MyRenderingModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
// Client-side initialization
MyRenderers.registerEntityRenderers();
MyRenderers.registerBlockEntityRenderers();
MyRenderers.registerModelLayers();
// Register other client components...
}
}
// Example: Custom entity class
public class CrystalEntity extends Entity {
public CrystalEntity(EntityType<? extends CrystalEntity> entityType, Level level) {
super(entityType, level);
}
@Override
public void tick() {
super.tick();
// Entity logic here
}
// Other entity methods...
}
// Example: Entity registration class
public class MyEntities {
public static final EntityType<CrystalEntity> CRYSTAL =
Registry.register(Registries.ENTITY_TYPE,
new ResourceLocation(MyRenderingMod.MOD_ID, "crystal"),
EntityType.Builder.of(CrystalEntity::new, MobCategory.MISC)
.sized(0.8f, 1.6f)
.clientTrackingRange(32)
.build("crystal"));
public static void register() {
// Registration happens in static initialization
MyRenderingMod.LOGGER.info("Registering entities for " + MyRenderingMod.MOD_ID);
}
}
// Example: Entity renderer implementation
public class CrystalEntityRenderer extends EntityRenderer<CrystalEntity> {
private final CrystalModel model;
public CrystalEntityRenderer(EntityRendererProvider.Context context) {
super(context);
this.model = new CrystalModel(context.bakeLayer(CrystalModel.LAYER_LOCATION));
}
@Override
public void render(CrystalEntity entity, float entityYaw, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
poseStack.pushPose();
poseStack.translate(0.0, entity.getBbHeight() * 0.5, 0.0);
this.model.renderToBuffer(poseStack, bufferSource.getBuffer(RenderType.entityCutout(getTextureLocation(entity))),
packedLight, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f);
poseStack.popPose();
}
@Override
public ResourceLocation getTextureLocation(CrystalEntity entity) {
return new ResourceLocation(MyRenderingMod.MOD_ID, "textures/entity/crystal.png");
}
}
// Example: Entity renderer registration
public class MyRenderers {
public static void registerEntityRenderers() {
EntityRendererRegistry.register(MyEntities.CRYSTAL, CrystalEntityRenderer::new);
// Register other entity renderers...
EntityRendererRegistry.register(MyEntities.OTHER_ENTITY, OtherEntityRenderer::new);
}
}
// Example: Block entity implementation
public class EnergyCoreBlockEntity extends BlockEntity {
private int energy = 0;
public EnergyCoreBlockEntity(BlockPos pos, BlockState state) {
super(MyBlockEntities.ENERGY_CORE, pos, state);
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
setChanged();
}
@Override
public void save(CompoundTag tag) {
super.save(tag);
tag.putInt("Energy", energy);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
energy = tag.getInt("Energy");
}
}
// Example: Block entity registration
public class MyBlockEntities {
public static final BlockEntityType<EnergyCoreBlockEntity> ENERGY_CORE =
Registry.register(Registries.BLOCK_ENTITY_TYPE,
new ResourceLocation(MyRenderingMod.MOD_ID, "energy_core"),
BlockEntityType.Builder.of(EnergyCoreBlockEntity::new, MyBlocks.ENERGY_CORE_BLOCK).build(null));
public static void register() {
// Registration happens in static initialization
}
}
// Example: Block entity renderer
public class EnergyCoreRenderer implements BlockEntityRenderer<EnergyCoreBlockEntity> {
private final EnergyCoreModel model;
public EnergyCoreRenderer(BlockEntityRendererProvider.Context context) {
this.model = new EnergyCoreModel(context.bakeLayer(EnergyCoreModel.LAYER_LOCATION));
}
@Override
public void render(EnergyCoreBlockEntity blockEntity, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int light, int overlay) {
poseStack.pushPose();
poseStack.translate(0.5, 0.5, 0.5);
float energyRatio = (float) blockEntity.getEnergy() / 1000.0f;
this.model.renderToBuffer(poseStack, bufferSource.getBuffer(RenderType.entityCutout(getTextureLocation(blockEntity))),
light, overlay, energyRatio, energyRatio, 1.0f, 1.0f);
poseStack.popPose();
}
}
// Example: Block entity renderer registration
public class MyRenderers {
public static void registerBlockEntityRenderers() {
BlockEntityRendererRegistry.register(MyBlockEntities.ENERGY_CORE, EnergyCoreRenderer::new);
// Register other block entity renderers...
BlockEntityRendererRegistry.register(MyBlockEntities.CUSTOM_DISPLAY, CustomDisplayRenderer::new);
}
}
// Example: Entity model with layer definition
public class CrystalModel extends EntityModel<CrystalEntity> {
public static final ModelLayerLocation LAYER_LOCATION =
new ModelLayerLocation(new ResourceLocation(MyRenderingMod.MOD_ID, "crystal"), "main");
private final ModelPart crystal;
public CrystalModel(ModelPart root) {
this.crystal = root.getChild("crystal");
}
public static LayerDefinition createBodyLayer() {
MeshDefinition meshDefinition = new MeshDefinition();
PartDefinition partDefinition = meshDefinition.getRoot();
partDefinition.addOrReplaceChild("crystal",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(-4.0f, -8.0f, -4.0f, 8.0f, 16.0f, 8.0f),
PartPose.ZERO);
return LayerDefinition.create(meshDefinition, 32, 32);
}
@Override
public void renderToBuffer(PoseStack poseStack, VertexConsumer consumer,
int packedLight, int packedOverlay, float red,
float green, float blue, float alpha) {
crystal.render(poseStack, consumer, packedLight, packedOverlay, red, green, blue, alpha);
}
}
// Example: Model layer registration
public class MyRenderers {
public static void registerModelLayers() {
EntityModelLayerRegistry.registerModelLayer(CrystalModel.LAYER_LOCATION, CrystalModel::createBodyLayer);
EntityModelLayerRegistry.registerModelLayer(EnergyCoreModel.LAYER_LOCATION, EnergyCoreModel::createBodyLayer);
}
}

Fabric uses a different event system than Forge. Here are common rendering-related events:

// Example: Client tick event registration
public class ClientEventHandler {
public static void registerEvents() {
ClientTickEvents.END_CLIENT_TICK.register(ClientEventHandler::onClientTick);
RenderTickEvent.END.register(ClientEventHandler::onRenderTickEnd);
}
private static void onClientTick(MinecraftClient client) {
// Client-side tick logic
if (client.world != null && client.player != null) {
// Update animations, effects, etc.
}
}
private static void onRenderTickEnd(TickEvent.RenderTickEvent event) {
// End of render tick logic
updateRenderEffects(event.tickDelta);
}
}
// Example: World render event registration
public class WorldRenderEventHandler {
public static void registerEvents() {
WorldRenderEvents.AFTER_ENTITIES.register(WorldRenderEventHandler::renderAfterEntities);
}
private static void renderAfterEntities(WorldRenderContext context) {
MatrixStack matrices = context.matrixStack();
Camera camera = context.camera();
matrices.push();
// Custom world rendering logic
renderCustomEffects(matrices, camera);
matrices.pop();
}
}

Fabric uses the standard resource loading system:

// Example: Resource loading
public class MyResourceManager {
public static void registerResources() {
// Most resources are automatically loaded from assets folder
// Custom resource packs can be registered here
}
}

Here’s a complete example showing all registrations together:

fabric.mod.json
{
"schemaVersion": 1,
"id": "myrenderingmod",
"version": "1.0.0",
"name": "My Rendering Mod",
"description": "Example rendering mod for Minecraft 26.1",
"authors": ["YourName"],
"contact": {
"homepage": "https://github.com/yourname/myrenderingmod",
"sources": "https://github.com/yourname/myrenderingmod"
},
"license": "MIT",
"icon": "assets/myrenderingmod/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"com.example.myrenderingmod.MyRenderingMod"
],
"client": [
"com.example.myrenderingmod.MyRenderingModClient"
]
},
"mixins": [
"myrenderingmod.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.0",
"fabric-api": "*",
"minecraft": "~1.26.1",
"java": ">=17"
},
"suggests": {
"another-mod": "*"
}
}
// Complete main mod class
public class MyRenderingMod implements ModInitializer {
public static final String MOD_ID = "myrenderingmod";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override
public void onInitialize() {
LOGGER.info("Initializing " + MOD_ID);
// Register all mod components
MyEntities.register();
MyBlockEntities.register();
MyBlocks.register();
MyItems.register();
LOGGER.info("Finished initializing " + MOD_ID);
}
}
// Complete client mod class
public class MyRenderingModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
MyRenderingMod.LOGGER.info("Initializing client side");
// Register all client-side components
MyRenderers.registerEntityRenderers();
MyRenderers.registerBlockEntityRenderers();
MyRenderers.registerModelLayers();
// Register events
ClientEventHandler.registerEvents();
WorldRenderEventHandler.registerEvents();
MyRenderingMod.LOGGER.info("Finished initializing client side");
}
}
// ❌ WRONG - Creating render resources in main mod initializer
public class MyMod implements ModInitializer {
@Override
public void onInitialize() {
// This is server-side, not safe for rendering
GpuBuffer buffer = RenderSystem.getDevice().createBuffer(...); // Will crash!
}
}
// ✅ CORRECT - Creating render resources in client initializer
public class MyModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
// This is client-side, safe for rendering
MyRenderers.initializeResources(); // Safe!
}
}
// ❌ WRONG - Trying to use entity before it's registered
public class MyRenderers {
public static void registerEntityRenderers() {
EntityRendererRegistry.register(MyEntities.ENTITY_THAT_DOESNT_EXIST_YET, MyRenderer::new);
}
}
// ✅ CORRECT - Ensure entities are registered first
public class MyModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
MyEntities.register(); // Register entities first
MyRenderers.registerEntityRenderers(); // Then register renderers
}
}
// ❌ WRONG - Inconsistent resource location names
public class MyEntities {
public static final EntityType<MyEntity> ENTITY =
Registry.register(Registries.ENTITY_TYPE,
new ResourceLocation("differentmodid", "entity_name"), // Different MOD_ID!
EntityType.Builder.of(MyEntity::new, MobCategory.MISC).build());
}
// ✅ CORRECT - Consistent resource location names
public class MyEntities {
public static final EntityType<MyEntity> ENTITY =
Registry.register(Registries.ENTITY_TYPE,
new ResourceLocation(MyRenderingMod.MOD_ID, "entity_name"), // Same MOD_ID
EntityType.Builder.of(MyEntity::new, MobCategory.MISC).build());
}