Skip to content

Building Item GUI Bundles

KabanFriends edited this page Aug 27, 2026 · 3 revisions

Building item GUI bundles

The item GUI renders a container screen you describe in JSON: one background texture, a set of slots, some labels, and a library of items to put in the slots. You compile that description into a bundle with mcw-itemgui-preprocess, and the widget loads the bundle.

Slots and items are separate on purpose. The bundle defines what the screen looks like and which items exist. The embed decides which item goes in which slot, so one bundle can serve every recipe on your page. See items in the Item GUI reference.

Get the tool

git clone https://github.com/KabanFriends/mcwidgets.git
cd mcwidgets
npm ci
npm run build

You need Node 20.19 or later, or Node 22.12 or later. The build produces the preprocessor at packages/itemgui/bin/mcw-itemgui-preprocess.mjs.

Building needs network access. The preprocessor downloads vanilla item renders and container textures from public CDNs, pinned to one Minecraft version.

A complete example

Three files, no images of your own.

my-gui/
  guis/
    crafting.json
  items.json

guis/crafting.json describes the screen. The texture is a crop of the vanilla crafting table page, so the preprocessor downloads it for you.

{
  "id": "crafting",
  "texture": { "vanilla": "container/crafting_table", "w": 176, "h": 166 },
  "slots": [
    { "name": "in1", "x": 30, "y": 17 },
    { "name": "in2", "x": 48, "y": 17 },
    { "name": "in3", "x": 66, "y": 17 },
    { "name": "in4", "x": 30, "y": 35 },
    { "name": "in5", "x": 48, "y": 35 },
    { "name": "in6", "x": 66, "y": 35 },
    { "name": "in7", "x": 30, "y": 53 },
    { "name": "in8", "x": 48, "y": 53 },
    { "name": "in9", "x": 66, "y": 53 },
    { "name": "result", "x": 124, "y": 35 }
  ],
  "texts": [
    { "x": 29, "y": 6, "text": "Crafting" }
  ]
}

items.json is the library of things that can appear in those slots.

{
  "items": [
    "stick",
    { "name": "diamond", "tooltip": "Diamond" },
    {
      "name": "diamond_sword",
      "tooltip": [
        "Diamond Sword",
        { "text": "Very sharp", "color": "gray", "italic": true }
      ]
    }
  ]
}

Build it:

node packages/itemgui/bin/mcw-itemgui-preprocess.mjs \
  --input ./my-gui \
  --output ./public/crafting
1 GUI(s), 3 item(s) -> wrote ./public/crafting/bundle.json

Then embed it, choosing the items per recipe:

<iframe
  src="https://mcwidgets.kaban.sh/itemgui/embed.html?bundle=https://example.com/crafting/bundle.json&amp;items=in1=diamond,in2=diamond,in3=diamond,in5=stick,in8=stick,result=diamond_sword"
  style="width: 100%; height: 220px; border: 0;"
  title="Crafting a diamond sword"
></iframe>

The input directory

my-gui/
  guis/          every .json under here, at any depth, is one GUI
  items.json     the item library
  textures/      your own PNG files, referenced by path from the input root

Only guis/ and items.json are required, and both can be moved with --gui and --items.

Custom texture paths in your JSON are resolved from the input root, so "texture": "textures/my_gui.png" means my-gui/textures/my_gui.png. Any directory name works. textures/ is just a habit.

Every JSON file here accepts _comment keys, at any level, and ignores them. Every other unknown field is an error, so a misspelled hilight stops the build instead of silently doing nothing.

GUI definitions

Field Type Notes
id string Required. Unique in the bundle. This is what gui selects at embed time.
texture string or object Required. See below.
slots array Required, may be empty.
texts array Optional.

texture

A path to your own PNG, resolved from the input root:

"texture": "textures/my_screen.png"

Your file is copied into the bundle byte for byte, and its size is read from the PNG header. Draw it at Minecraft's own scale, one image pixel per GUI pixel, not at 2x or 4x.

Or a crop of a vanilla texture page:

"texture": { "vanilla": "container/crafting_table", "w": 176, "h": 166 }

vanilla is a path under the game's textures/gui/ directory, without the extension. The preprocessor downloads the page and crops the top left w by h, which is the region the game itself blits. A crop larger than the source page is an error.

The texture's size is the widget's minimum size, and the GUI is centred in any container larger than that.

slots

Field Type Default
x, y integer, at least -8 required
name string slot1, slot2, and so on, by position
w, h integer, at least 1 16
highlight boolean true

x and y are the top left of the item cell, in the same coordinates the game's own slot definitions use, relative to the texture's top left. Copy them straight out of the game's screen code or measure them off your PNG.

name is what an embed writes in items, so pick names your page's authors will enjoy typing. result beats slot10.

highlight: false suppresses the hover ring for a cell that is decorative rather than a real slot. The tooltip still appears, which is what an armour-slot placeholder or a label chip wants.

A slot whose cell falls outside the texture produces a warning and is still emitted, so a deliberate overhang is possible. Negative coordinates are allowed down to -8.

texts

Field Type Default
text text component required
x, y integer required
shadow boolean false

Labels are drawn under the items, in the texture's coordinates. shadow adds the 1 pixel drop shadow the game uses for text over dark backgrounds. Container labels have no shadow, which is why the default is off.

text accepts anything Text and fonts describes, including translate components.

The item library

items.json is one object with one items array. Names must be unique across the whole library.

A vanilla item, shortest form

"stick"

The name and the item id are the same, and there is no tooltip.

A vanilla item with a tooltip

{ "name": "diamond", "tooltip": "Diamond" }

The name is used as the item id unless you give an id, which lets you have two library entries for the same item:

{ "name": "cursed_sword", "id": "minecraft:iron_sword", "tooltip": "Cursed Sword" }

An id with no namespace is read as minecraft:.

A custom item

{ "name": "soul_shard", "texture": "textures/soul_shard.png", "tooltip": "Soul Shard" }

texture replaces the icon entirely and id is not consulted. The file is resolved from the input root and copied into the bundle unchanged. Draw it 16 by 16, or at whatever size you want it to occupy in GUI pixels.

A rotation group

A group cycles through other items in place, the way the game cycles through the possible ingredients of a recipe.

{
  "name": "any_wool",
  "interval": 1.5,
  "items": [
    { "name": "white_wool", "tooltip": "White Wool" },
    { "name": "orange_wool", "tooltip": "Orange Wool" },
    "diamond"
  ]
}
Field Type Default
name string required
items array of names or inline definitions required, non-empty
interval seconds, greater than 0 1

Members are either the name of another library item or an inline definition, which becomes a library entry of its own. Groups cannot contain groups.

Bind the group's name to a slot and the slot cycles. The tooltip changes with it. Set the widget's static option to freeze every group on its first member.

A group carries no icon and no tooltip of its own. Those come from whichever member is showing.

Options

Option Effect
--input <dir> Input directory. Required.
--output <dir> Output directory. Default ./dist.
--items <file> Item library path. Default <input>/items.json.
--gui <file> One GUI definition. Repeatable, and replaces the guis/ scan entirely.
--bake-lang <file.json> Resolve translate components at build time. See Text and fonts.
--icon <id>=<file or url> Replace one vanilla item's icon. Repeatable.
--icon-map <file.json> The same overrides as a JSON object of id to file or URL.
--icon-source <ref> Git ref of the item render source. Default master.
--source <string> A provenance string recorded in the bundle's metadata.
--single-file Inline every texture and icon as a data: URI.
--quiet Suppress warnings.
--json-errors Print errors and warnings as JSON.
--version Print the preprocessor version.
--help Print the option list.

What comes out

public/crafting/
  bundle.json
  guis/crafting.png          one per GUI, named by its id
  icons/minecraft/stick.png  one per vanilla item referenced
  items/soul_shard.png       your custom textures, path preserved

bundle.json references everything by relative path, resolved against the bundle's own URL, so move the directory as a unit. Serve all of it. If the page that runs the widget is on another origin, bundle.json needs Access-Control-Allow-Origin and the images beside it do not.

--single-file folds every image into bundle.json as a data: URI, giving you one file to host at the cost of about a third more bytes and no separate image caching.

Several GUIs in one bundle

Put more definitions under guis/. Each needs a unique id, and they share the one item library.

An embed then has to say which one it wants with the gui setting. Leave it out and the mount fails with the valid ids listed, which is a better outcome than rendering an arbitrary one.

Errors

The build stops, naming the file, when:

  • Two GUIs share an id, or two library items share a name.
  • A field is unknown, anywhere in either file.
  • A slot's x or y is not an integer, or is below -8.
  • A referenced texture file is missing.
  • A vanilla crop is larger than the page it comes from.
  • A group has an empty items array, contains another group, or references a name that is not in the library.
  • A definition mixes items with texture, id, or tooltip.
  • interval appears on something that is not a group.

A vanilla icon that cannot be downloaded becomes the missing-texture square and prints a warning, because one absent icon is not worth failing a build over.

Clone this wiki locally