The vanilla template uses plain HTML and JavaScript for the web client.

Onchain code

This template uses the standard Counter onchain code.

Offchain code

The files you are likely to need to change in the offchain code are:


This is the HTML displayed to the user. The lines that are relevant to MUD are:

<script type="module" src="/src/index.ts"></script>

Import the script that runs most of the application.

<div>Counter: <span id="counter">0</span></div>

Create an HTML span (opens in a new tab) that JavaScript code can update. This span will be updated every time the value of the counter changes.

<button onclick="window.increment()">Increment</button>

A button that calls window.increment(), which calls the onchain system to increment the counter.


This is the main TypeScript file. It calls the general setup code for MUD, and then does application-specific setup actions.

// Components expose a stream that triggers when the component is updated.
components.Counter.update$.subscribe((update) => {
  const [nextValue, prevValue] = update.value;
  console.log("Counter updated", update, { nextValue, prevValue });
  document.getElementById("counter")!.innerHTML = String(nextValue?.value ?? "unset");

Specify a function to be called when the value of Counter changes. This function emits a log message (opens in a new tab) and then updates the counter span.

// Just for demonstration purposes: we create a global function that can be
// called to invoke the Increment system contract via the world. (See IncrementSystem.sol.)
(window as any).increment = async () => {
  console.log("new counter value:", await increment());

Here we know how to call a system call, but the code inside the HTML does not. We use a global function to share this knowledge. Generally speaking global definitions are considered bad, but this is sample code written to optimize understanding, not maintainability.


This file is where you place the calls that go to the onchain system. In this case there is only one, increment.

const increment = async () => {
   * Because IncrementSystem
   * (https://mud.dev/tutorials/walkthrough/minimal-onchain#incrementsystemsol)
   * is in the root namespace, `.increment` can be called directly
   * on the World contract.
  const tx = await worldContract.write.increment();
  await waitForTransaction(tx);
  return getComponentValue(Counter, singletonEntity);

You create a transaction, wait for it to be included, and return the value. If there is any need for translation between the parameters provided by the user interface and those that are expected by the onchain code, this is where you would add it.