How the GUI system works
The GUI extension turns a Typewriter entry into an interactive inventory menu. You never place items slot-by-slot in code — you describe a tree of layouts, and the engine assembles, clips and renders it for each player.
Think of it like building with boxes:
- A layout is a box that produces items at positions.
- Boxes can contain other boxes (a scrollable box wrapping a grid box, a composite box stacking several boxes…).
- The viewport is the window the player looks through — a double chest shows 9×6 slots, but your content can be much bigger and scroll behind it.
The flow, end to end
- A player triggers an
open_guiaction entry (from a quest, an NPC, a command…). - The entry's
layoutPoolcontains named layout definitions;mainLayoutIdpicks the root. - The engine builds the layout tree, opens the inventory, and starts a session for that player.
- Every scroll, page change or reactive update re-renders the visible viewport.
- Clicks are matched to the virtual slot under the cursor and run its commands, triggers or internal actions (
gui:scroll,gui:back…).
Vocabulary
| Term | What it really is |
|---|---|
open_gui entry | The menu itself: container type, title, size, layout pool, audio |
layoutPool | A list of named layout definitions. Layouts reference each other by id |
mainLayoutId | The id of the layout used as the root of the tree |
| Layout | A node producing items: a grid, a scroll area, pages, frames… (all types) |
| Virtual size | The full size of a layout's content (e.g. 9×20 rows) |
| Viewport | The visible window (9 columns × inventory rows). Scrolling moves the viewport over the virtual space |
Item (GuiItemData) | One visual cell: an ItemStack + name/lore + interactions + options |
| Session | The per-player runtime state: scroll positions, current pages, history for gui:back |
Your first menu, step by step
A 3-row chest with a decorated border and a close button in the middle:
{
"id": "my_first_menu",
"type": "open_gui",
"name": "First Menu",
"guiType": "CHEST",
"size": "ROWS_3",
"title": "<gradient:#4f8cff:#3ecf6f>My First Menu</gradient>",
"mainLayoutId": "main",
"layoutPool": [
{
"case": "composite",
"value": { "id": "main", "children": ["border", "content"] }
},
{
"case": "simple",
"value": {
"id": "border",
"items": [
{ "x": 0, "y": 0, "count": 9, "direction": "right",
"item": { "material": "GRAY_STAINED_GLASS_PANE" }, "displayName": " " },
{ "x": 0, "y": 2, "count": 9, "direction": "right",
"item": { "material": "GRAY_STAINED_GLASS_PANE" }, "displayName": " " }
]
}
},
{
"case": "simple",
"value": {
"id": "content",
"items": [
{ "x": 4, "y": 1,
"item": { "material": "BARRIER" },
"displayName": "<red>Close",
"interactions": [
{ "type": "LEFT", "commands": [], "closeMenu": true }
] }
]
}
}
]
}
What to notice:
- Two layers stacked by the
compositelayout — the border renders first, the content on top. - Repetition without copy-paste:
count: 9, direction: "right"places 9 panes from(0,0). closeMenu: trueon the interaction adds the internalgui:closeaction — no command string needed.- The title is MiniMessage: gradients, colors, hover — and custom glyphs if you use a resource pack.
- Item names and lore render upright by default. Minecraft normally forces custom item text into italics; the GUI engine turns that off so your menus stay clean. If you actually want italics on a name or a lore line, add the
<italic>MiniMessage tag explicitly (e.g.<italic><gray>A hint) — it wins over the default.
tip
A web-based WYSIWYG editor (drag & drop, live preview, states) is in development and will write this JSON for you.
Where to go next
- Layout Types — every layout, when to use it, complete examples
- Commands & Interactions — click types, internal
gui:actions, cooldowns, input dialogs - Cookbook — ready-to-paste recipes (shop, pagination, confirmation, storage)