How to build interface elements from a script using the Geode UI classes exposed under geode.*.
Covers the factory pattern, the layout system, popup queues, and scene helpers.
Signatures come from type stubs. For class behavior, see the Geode docs.
Use imgui for player mod menus you can rebuild every frame. Settings, toggles, tabs, lists, color edits, and confirm popups fit well there.
Use geode.* Cocos UI when you need native game nodes, sprites, persistent layers, or layout objects.
Cocos UI lives in the scene graph. ImGui is immediate mode and redraws each frame.
Geode UI classes sit directly under geode, next to cocos2d and gd.
You create one through its create factory, the same way you create engine objects.
The result can be nil, so check it.
local cc2d = geode.cocos2d
local modId = geode.Mod.getID()
local sprite = geode.CircleButtonSprite.create(cc2d.CCSprite.create("GJ_plusBtn_001.png"))
if not sprite then return end
sprite:setID(modId .. "/add-button")Common factories include:
- Button sprites:
CircleButtonSprite,IconButtonSprite - Text:
TextInput,TextRenderer,SimpleTextArea,MDTextArea - Lists:
ListView,ListBorders - Overlays:
Notification,LoadingSpinner,ProgressBar,ColorPickPopup
A layout positions a node's children for you.
Set a layout on any CCNode with :setLayout(), then call :updateLayout() after adding children.
geode.AxisLayout, with shorthandsgeode.RowLayoutandgeode.ColumnLayout, arranges children along an axis.geode.AnchorLayoutpins children to anchor points of the parent.- The
*Optionsfactories tweak per-child behavior, attached with:setLayoutOptions().
local cc2d = geode.cocos2d
local modId = geode.Mod.getID()
local menu = cc2d.CCMenu.create()
menu:setID(modId .. "/my-menu")
menu:setLayout(geode.ColumnLayout.create())
-- add your buttons to menu here
menu:updateLayout()geode.createQuickPopup is a free function that builds a modal alert with up to two buttons and a callback.
The callback receives the popup and which button was pressed.
geode.createQuickPopup(
"Confirm", -- title
"Delete this level?", -- body
"Cancel", "Delete", -- button 1, button 2
function(_popup, btn2)
if btn2 then print("deleted") end
end,
true, -- doShow: show the popup now
false -- cancelledByEscape: Escape does not close it
)geode.PopupManager serializes modal popups so they do not overlap.
Use alert for a one-button message, quickPopup for a two-button prompt, or manage to queue an existing popup.
geode.PopupManager.DEFAULT_WIDTH is the standard alert width.
manage returns a ManagedPopup:
| Method | Role |
|---|---|
getInner |
Get the managed native popup |
setPersistent |
Keep the popup visible across scene transitions |
setPriority |
Set its queue priority |
blockClosingFor |
Temporarily prevent closing |
showInstant |
Show immediately |
showQueue |
Add it to the popup queue |
isShown |
Whether it is currently shown |
shouldPreventClosing |
Whether closing is currently blocked |
Use geode.PopupManager.isManaged(popup) to test a popup and
geode.PopupManager.hasPendingPopups() to test the queue.
Popup callbacks follow Limits and errors.
The stub also exposes a few free functions on geode itself:
geode.createDefaultLogo()andgeode.createServerModLogo(modId)for mod branding nodes.createServerModLogotakes a server mod ID string, not a local package path.geode.openModsList()to open the in-game mod listgeode.openInfoPopup(modID)to show an installed mod or fetch mod info. It returns nil when the mod is installed. Otherwise it returns aGeodeTaskHandle<boolean>for the async lookup.geode.Notification.create(text, icon, duration)for toast overlaysgeode.pushSceneWithLayer(layer)to wrap aCCLayerin a new scene and push it
geode.Button:setDisplayNode(node) replaces a button's display node while preserving Geode's button Z-order behavior.
Not every factory is listed here. For the rest, read type stubs and the Geode SDK docs.
See imgui and mod/demo/demo_modmenu.luau for overlay menus.
These classes are generated from the Geode bindings. The authoritative argument lists live in the generated type stubs, surfaced as editor autocomplete. See type stubs. For class behavior, read the upstream Geode UI docs.
tools/luau_codegen/emit/luau_types/factories.pytools/luau_codegen/parse/geode_sdk.pytools/luau_codegen/model/free_fn_sources.pytools/luau_codegen/extra_bindings/popup.dluausrc/bindings/geode/GeodePopupBinding.cpptypes/geode.d.luau