Skip to content

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

java
// 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

tsx
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

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

js
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):

js
window.webgui.postToGame({ channel: 'shop:buy', itemId: 'minecraft:diamond', qty: 1 })

Or via the React hook:

tsx
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

java
// 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:

java
WebviewServerEvents.PAGE_EVENT.register((player, channel, payload) -> {
    if (!channel.equals("shop:buy")) return;
    // parse payload...
});

Built-in channels (client-only, never reach the server)

ChannelAction
logLogs the message to the client-side game log
closeCloses the active GUI or HUD overlay

Full round-trip example

Server — open a shop and push inventory data:

java
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:

tsx
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>
      ))}
    </>
  )
}