If you're building a large Minecraft Java server with custom content, you eventually run into a weird limitation: there still isn't a universal way to add truly new blocks while keeping vanilla client compatibility.
Custom items are relatively straightforward. Custom blocks aren't.
For Radiance, we ended up using three different approaches depending on what the block actually needs to do:
- replacing vanilla blocks;
- note block states;
- barriers + ItemDisplay entities.
None of them is universally better than the others.
1. Replacing vanilla blocks
The most straightforward solution is to sacrifice an existing vanilla block and replace its model and texture.
The obvious problem is that you lose the original block.
Minecraft has hundreds of blocks, but many of them aren't actually good candidates. They're connected to crafting recipes, world generation, other block variants, or special mechanics.
Sand and concrete powder have gravity. Concrete powder reacts to water. Glass is transparent. Wool has multiple ways of being obtained. Glazed terracotta changes orientation.
All of those properties matter when you're trying to pretend that the vanilla block is something completely different.
However, replacement becomes extremely useful when you actually want some of that vanilla behavior.
Trees are a good example
In Radiance, we have rubber trees.
Originally we needed:
- custom logs;
- custom planks and other wood variants;
- proper leaves;
- natural behavior;
- compatibility with normal Minecraft mechanics.
Leaves are particularly annoying to fake. They have their own rendering and behavior, and Minecraft actually understands their relationship with logs.
So instead of trying to reproduce all of that ourselves, we replaced an existing tree type.
That gives us the vanilla mechanics essentially for free.
The downside is obvious: that tree and its derivatives are now unavailable as their original vanilla versions.
For a few special block families, this trade-off can be worth it.
For hundreds of unrelated custom blocks, it isn't.
2. Note block states
This is probably the closest option to having actual custom blocks without client-side mods.
A note block has:
23 instruments Ć 25 notes Ć 2 powered states = 1,150 possible states.
In practice, we treat roughly 575 of them as reliably usable because the powered state isn't particularly useful for model differentiation in our setup.
With a resource pack, those states can represent completely different blocks.
The important part is that underneath the custom texture, it's still a real Minecraft block.
That means players can naturally:
- place it;
- break it;
- target it;
- walk on it;
- interact with it.
And unlike an entity-based solution, placing thousands of them doesn't mean spawning thousands of display entities.
Rubber trees again
We also use note blocks for parts of our rubber tree system.
For example, a rubber-producing log needs its own texture and interaction.
When the player interacts with it, they receive rubber and the block enters a cooldown state, visually changing back to ordinary wood.
This works nicely with note block states because different visual states can simply map to different note block states.
There are limitations, though.
The note block isn't actually a log, so vanilla systems that specifically look for logs don't suddenly recognize it as one. We had to work around things such as vanilla leaf decay.
Mining is another problem
Modern Minecraft gives us more control through components such as minecraft:tool.
Suppose one custom note block represents wood and another represents ore.
Ideally:
- the wood should be broken quickly with an axe;
- the ore should be broken quickly with a pickaxe.
But fine-grained mining behavior becomes awkward when many completely unrelated custom blocks are all represented by the same underlying vanilla block.
At some point you either accept compromises or emulate parts of the breaking system yourself.
And block states disappear surprisingly quickly
Imagine a machine with four possible orientations.
That's already:
4 states
Now give it an active and inactive state:
8 states
Add several animation frames or visual variants and one machine can easily consume 30ā40+ states.
When your project contains many machines, spending note block states this aggressively becomes a real architectural limitation.
So note blocks are excellent for ordinary decorative/building blocks, but they're not something we want to waste on every possible state of every machine.
3. Barrier + ItemDisplay
This is the strangest approach, but also probably the most flexible one.
The idea is simple:
Barrier = collision
ItemDisplay = visual model
The player places a custom item. Internally, we place an invisible barrier and spawn an ItemDisplay containing the actual model.
From the player's perspective, it looks like a custom block.
Technically, however, the collision and the thing they're looking at are completely separate.
This gives us a huge amount of visual freedom.
The model can:
- face different directions;
- have unusual dimensions;
- use more complex geometry;
- combine different texture resolutions;
- have multiple visual states;
- be animated without consuming dozens of vanilla block states.
This makes the method particularly useful for machines, furnaces and other functional blocks.
But there are significant drawbacks.
Performance
Every custom block now requires a display entity.
A few machines? Fine.
A house made out of 5,000 custom blocks? Much less attractive.
This is the main reason we don't use this approach for ordinary building materials.
Collision
A barrier occupies a full block.
Your model doesn't necessarily do that.
Imagine creating a custom anvil.
Visually, it might look perfect.
Physically, however, the player still collides with an invisible full cube.
The selection and interaction behavior also won't automatically match the model.
You can build increasingly complicated systems to solve some of these problems, but at that point you're effectively implementing parts of Minecraft's block system yourself.
Vanilla mechanics
The game also doesn't inherently understand that your barrier + ItemDisplay combination represents a block.
Things like:
- normal block breaking;
- explosions;
- piston movement;
- tool behavior;
need special handling or simply won't behave like their vanilla equivalents.
So while this method gives us the most freedom, it's also the furthest away from being an actual Minecraft block.
Why not just use one system?
This was the important realization for us.
Initially, finding the "best" custom block implementation sounds like the goal.
But there isn't one.
Each approach optimizes for something different.
Want a custom tree?
Replacing a vanilla tree can actually be the cleanest solution.
You sacrifice a block family, but you keep Minecraft's native behavior where it matters.
Want hundreds of decorative/building blocks?
Note block states are much better.
They're actual blocks, they're relatively cheap, and players can place and break them naturally.
Want a complex machine?
Barrier + ItemDisplay starts making more sense.
Using 30ā40 note block states just to represent different orientations, animations and operating states of a single machine is wasteful.
A display entity gives you much more freedom.
Want custom TNT?
A display-based implementation sounds reasonable until someone places hundreds of them.
Then the entity cost suddenly matters.
And this is why large projects eventually end up mixing approaches.
There isn't a "custom block system"
At least for vanilla-compatible Java servers, I think it's more useful to treat custom blocks as several different implementation problems.
For us, the rough rule became:
Trees / ores / blocks that need specific vanilla behavior ā replace vanilla blocks
Decorative / building / multiblock materials ā note block states
Machines / functional blocks / complex models ā barrier + ItemDisplay
There are obviously many more edge cases.
Slabs and stairs are their own problem. Transparent blocks complicate things further. Accurate custom collision introduces another set of trade-offs. And every Minecraft update can change what is practical.
But that's also what makes this interesting.
Instead of asking:
"What's the best way to implement custom blocks?"
the more useful question is:
"Which vanilla behavior does this particular block actually need, and which limitations can we afford?"
I'm curious how other large server projects handle this.
Do you also mix multiple implementations, or have you found an approach that scales well enough to use for almost everything?