Custom content is declared at the top level, like everything else. An item block gives you an identity-carrying item with the exact lore you write and a tree of NBT data; a mob block gives you a spawnable creature with AI, drops, and handlers. Both are checked: rarities, AI modes, and click filters are closed enums, and a drop referencing an unknown item id is a compile error.
Declaring an item
item "aspect_of_the_end" {
material: "DIAMOND_SWORD"
name: "<blue>Aspect of the End"
rarity: rare
glint: true
tags: {
damage: 100,
meta: { tier: 3, keywords: ["end", "sword"] }
}
lore {
line "<gray>A legendary blade from the End."
blank
line "<dark_gray>Soulbound"
}
}
command "kit" {
execute {
give item "aspect_of_the_end" to sender
send "<green>Kit delivered." to sender
}
}The declaration owns four things: identity (material or skull), appearance (name, rarity, glint), the lore you write line for line, and a tags tree of freeform NBT. The lore is what-you-see-is-what-you-get — no sections are assembled for you — and one of the four vanilla rarities (common, uncommon, rare, epic) tints the name. lore { } allows line, blank, and if/loop, the same restricted DSL as scoreboard lines.
# vanilla Skript: the "custom item" is rebuilt inline
# at every give site — name, lore, and enchant glint
# restated each time, drifting apart over time
command /kit:
trigger:
give player diamond sword named "&9Aspect of the End" with lore
"&7A legendary blade from the End." and "&8Soulbound"item "aspect_of_the_end" {
material: "DIAMOND_SWORD"
name: "<blue>Aspect of the End"
rarity: rare
lore {
line "<gray>A legendary blade from the End."
}
}
command "kit" {
execute {
give item "aspect_of_the_end" to sender
}
}Why it maps this way
The declaration is the single source of truth: every give item site stays in sync because there's nothing to restate. The item also carries its identity — see custom_id below — which inline-built items never do, so you can recognise it again after it's been dropped, stored, or traded.
Identity: custom_id
Every declared item is tagged with its id, and the tag survives inventories, chests, and restarts. custom_id(<item>) reads it back as an optional<String> — none for vanilla items, so Step 07 discipline applies:
event PlayerUseItem {
execute {
if custom_id(event.item) otherwise "" is "aspect_of_the_end" {
send "<gray>You used the Aspect!" to event.player
}
}
}custom_item("id") conjures a fresh instance as an expression, and any item value exposes a tags namespace: item.tags.<path> reads and writes freeform NBT, nesting through compounds (item.tags.meta.tier) and integrating with optionals — a missing tag reads as none.
Tags, attributes, and on_click
Three optional pieces turn a decorated item into interactive equipment:
tags { }— arbitrary nested NBT (damage: 100,meta: { tier: 3 }), read and written at runtime throughitem.tags.*. This is where your own "stats" live — plain data you design.attributes { }— real vanilla attributes (speed,max_health,attack_damage,attack_speed,armor,knockback_resistance), applied while the item is held or worn and removed when it isn't.on_click(<filter>) { }— a filtered use handler (left,right,any) bound to this item, withplayeranditemin scope. A tag write inside re-renders the lore and flushes back to the hand.
item "crypt_key" {
skull: "1ae3855f952cd4a03c148a946e3f812a5955ad35cbcb52627ea4acd47d3081"
name: "<gold>Crypt Key"
rarity: epic
tags: {
uses: 3
}
on_click(left) {
set item.tags.uses to (item.tags.uses otherwise 0) - 1
send "<light_purple>The key turns... (${item.tags.uses otherwise 0} uses left)" to player
}
}(skull: instead of material: renders a player-head with that texture — one or the other, never both; the checker enforces it.)
Build a whole item system
Stats, tiers, and cooldowned abilities are things you assemble from these pieces — tags for the numbers, lore for the display, events or an addon for the behavior. Step 14 walks the full Hypixel-style pattern, and the reference has every key.
Declaring a mob
item "rotten_scimitar" {
material: "GOLDEN_SWORD"
name: "<gray>Rotten Scimitar"
rarity: uncommon
tags: {
damage: 40
}
}
mob "crypt_ghoul" {
type: "ZOMBIE"
name: "<red>Crypt Ghoul <green>${mob.health}<red>❤"
health: 200
damage: 25
speed: 0.3
ai: melee
drops {
item "rotten_scimitar" chance 0.05 amount 1
item "ROTTEN_FLESH" chance 0.5 amount 2
}
on_spawn {
broadcast "<gray>A ${mob.custom_id} rises from the crypt..."
}
on_death {
if killer exists {
send "<green>You slew the Crypt Ghoul!" to killer
}
}
on_attack {
send "<red>The ghoul rakes at you!" to victim
}
}The pieces:
type— the base entity, validated against the real entity-type registry.name— live-interpolated:${mob.health}re-renders on the nameplate as the mob takes damage.ai—melee(target, chase, attack),passive(wander), ornone(statue).drops { }— each roll isitem <id> chance <p> amount <n>; ids can be your custom items or vanilla materials, checked at compile time.- Handlers bind
mob, pluskiller(anoptional<Player>— narrowing required) inon_deathandvictiminon_attack.
A custom mob is an entity, so the shared entity properties and statements — velocity, glowing, mount, remove entity — work on it alongside its typed mob rows.
Spawning and querying
These commands live in the same file as the mob "crypt_ghoul" declaration above — spawning an undeclared mob id is a compile error.
command "ghoul" {
execute {
spawn mob "crypt_ghoul" at location(10, 64, 20) as m
set m.health to m.max_health / 2
send "<gray>Spawned ${m.custom_id} at half health." to sender
}
}
command "cleanse" {
execute {
loop all_mobs("crypt_ghoul") as g {
despawn g
}
send "<green>The crypt is quiet." to sender
}
}spawn mob ... as m binds the new mob to a variable — a Mob value with properties health (rw), max_health (ro), name (rw), location (rw), type (ro), and custom_id (ro). all_mobs("id") lists the living instances; despawn removes one.
Server-wide, the MobSpawn, MobDeath, and MobDamageevents fire for every custom mob — per-mob handlers for the mob's own story, events for global systems:
persistent mob_kills for Player: Integer = 0
event MobDeath {
execute {
if event.killer exists {
set mob_kills for event.killer to (mob_kills for event.killer) + 1
send "<gold>Kill #${mob_kills for event.killer}: ${event.mob.custom_id}" to event.killer
}
}
}That's a kill tracker in ten lines — persistence from Step 10, optionals from Step 07, and content from this one. Next: packaging systems like it into reusable modules.