Prebuilt GUI Contracts
RPGJS prebuilt GUIs are regular GUI entries registered with fixed IDs. You can replace their visual implementation with your own CanvasEngine component by registering a GUI with the same ID. The server API stays the same. For example,player.showText() still opens rpg-dialog, but your component receives the dialog data and decides how to render it.
Register a Replacement
Create a.ce file and register it with the prebuilt ID you want to replace:
renderer: 'canvas' for .ce registrations; registrations without a renderer still use legacy component-shape detection for compatibility. This page focuses on CanvasEngine .ce replacements; for Vue-specific examples, see Vue.js integration.
Component Interface
CanvasEngine GUI components receive:
For example, this CanvasEngine component can replace the built-in dialog box:
onFinish() when the server is waiting for a final answer, such as a dialog choice. Use onInteraction() for actions that should keep the GUI open, such as buying an item or equipping gear.
Character Select
Data contains
actors, title, subtitle, selectedActorId, and
allowCancel. Each Actor presentation contains id, optional name, optional
description, and optional graphic and faceset spritesheet IDs. A custom
renderer must return an Actor ID from this list; the server ignores unknown IDs
and keeps the GUI open. The promise resolves to the original server-owned Actor,
not the client payload, and never calls setActor() implicitly.
Input
The prebuilt input GUI renders either an HTML input or textarea. Server-owned
validation keeps the GUI open when a submitted value is invalid. Projects can
replace the component by registering another GUI with the same ID, but the
replacement must use the interactions above so validation still runs on the
authoritative server.
Data:
null.
Text, password, email, and textarea inputs resolve to strings.
Default button labels and validation errors use rpg.input.* translation keys.
Game-level i18n messages can override them; explicit confirmText and
cancelText options take precedence for one form.
Dialog Box
Data:
To return a choice, call
onFinish(index) where index is the selected choice index. For text without choices, call onFinish() when the player dismisses the dialog.
For a dialog input, use onInteraction('submit', { value }) and
onInteraction('cancel'). This keeps parsing and validation on the server and
allows invalid submissions to update input.errorKey without closing the GUI.
CanvasEngine example:
Hotbar
Data:
Each presentation slot contains
index, type, entry, name,
description, icon, quantity, cost, badge, usable, cooldownMs,
readyAt, activation, locked, and lockedHint when applicable. Empty
slots use type: "empty" and entry: null.
Interactions:
Custom components should render only the received presentation and send these
interactions. They must not consume inventory, SP, or apply gameplay effects
on the client. For color, spacing, and typography changes, prefer the native
hotbar theme variables over a
replacement.
Main Menu
Data:
Interactions:
The built-in client also applies optimistic updates for
useItem and equipItem. Custom Vue and CanvasEngine replacements can use the same interaction names and still benefit from those reducers.
Shop
Data:
ShopItem includes id, name, description, price, icon, type, optional stats, optional quantity, and equipped.
Interactions:
Save and Load
Data:
Server interactions:
The built-in
save-load.ce also uses SaveClientService directly for local save/load UI flows. A replacement can use either the server interactions above or its own client service flow, depending on the game architecture.
Title Screen
Data:
Interaction:
Game Over
Data:
Interaction:
HUD and Notifications
HudComponent and NotificationComponent are also GUI components, but they are less driven by a server payload than the modal GUIs above.
HUD data:
The HUD reads the current player from the client engine for
hp, sp, max values, and level.
Notifications use PrebuiltGui.Notification / rpg-notification, but the built-in component reads engine.notificationManager.stack() instead of a direct data payload. Custom notification UIs should use the same notification manager if they need the existing player.showNotification() behavior.