Events
WebGUI has a bidirectional named event system between the server and the page — a direct analogue of alt:V's WebView event system.
Server → Page
Java API
// Send an event to a specific player's open page(s)
WebviewApi.emitToPage(player, "walletUpdate", "{\"balance\":1500}");
// Emit a plain string — also valid JSON
WebviewApi.emitToPage(player, "serverRestart", "\"5 minutes\"");
// Null payload
WebviewApi.emitToPage(player, "sessionExpired", null);The second argument is the event name. The third is any valid JSON value — object, array, string, number, boolean, or null.
Receiving in the page — React hook
import { useWebGUIEvent } from '@webgui/react'
interface WalletUpdate { balance: number }
export function Wallet() {
const [balance, setBalance] = useState(0)
useWebGUIEvent<WalletUpdate>('walletUpdate', ({ balance }) => {
setBalance(balance)
})
return <p>Balance: {balance}</p>
}Receiving in the page — vanilla JS
// Via window.webgui.on (injected by the mod)
window.webgui.on('walletUpdate', (data) => {
console.log('balance:', data.balance)
})
// Or directly via the DOM event
window.addEventListener('webgui:walletUpdate', (e) => {
console.log('balance:', e.detail.balance)
})Both window.webgui.on and window.addEventListener listen to the same underlying CustomEvent('webgui:<eventName>').
Unsubscribing
function onWalletUpdate(data) { ... }
window.webgui.on('walletUpdate', onWalletUpdate)
// later:
window.webgui.off('walletUpdate', onWalletUpdate)In React, useWebGUIEvent cleans up automatically when the component unmounts.
Page → Server
Sending from the page
Use window.webgui.postToGame with any channel name that isn't a built-in (log, close):
window.webgui.postToGame({ channel: 'shop:buy', itemId: 'minecraft:diamond', qty: 1 })Or via the React hook:
const post = usePostToGame()
post({ channel: 'shop:buy', itemId: 'minecraft:diamond', qty: 1 })The full JSON payload (including the channel field) is forwarded to the server as a C2S packet.
Receiving on the server — Java API
// Register once, e.g. during mod initialisation
WebviewApi.onPageEvent("shop:buy", (player, rawJson) -> {
JsonObject obj = JsonParser.parseString(rawJson).getAsJsonObject();
String item = obj.get("itemId").getAsString();
int qty = obj.get("qty").getAsInt();
giveItem(player, item, qty);
});Receiving on the server — Fabric event
For mods that prefer the Fabric event bus:
WebviewServerEvents.PAGE_EVENT.register((player, channel, payload) -> {
if (!channel.equals("shop:buy")) return;
// parse payload...
});Built-in channels (client-only, never reach the server)
| Channel | Action |
|---|---|
log | Logs the message to the client-side game log |
close | Closes the active GUI or HUD overlay |
Full round-trip example
Server — open a shop and push inventory data:
WebviewApi.openGui(player, "https://your-site.example.com/shop");
WebviewApi.emitToPage(player, "inventoryLoad", buildInventoryJson(player));
WebviewApi.onPageEvent("shop:buy", (player, rawJson) -> {
processShopPurchase(player, rawJson);
// push updated balance back to the page
WebviewApi.emitToPage(player, "walletUpdate", "{\"balance\":" + getBalance(player) + "}");
});Page — React component:
import { useWebGUIEvent, usePostToGame } from '@webgui/react'
export function ShopScreen() {
const [items, setItems] = useState([])
const [balance, setBalance] = useState(0)
const post = usePostToGame()
useWebGUIEvent('inventoryLoad', setItems)
useWebGUIEvent<{ balance: number }>('walletUpdate', ({ balance }) => setBalance(balance))
function buy(itemId: string) {
post({ channel: 'shop:buy', itemId, qty: 1 })
}
return (
<>
<p>Balance: {balance}</p>
{items.map(item => (
<button key={item.id} onClick={() => buy(item.id)}>{item.name}</button>
))}
</>
)
}