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

You can start a new project using this template with:

pnpm create mud@latest tutorial --template vanilla

For more information, see the quickstart guide.

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 id="incrementButton">Increment</button>

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


This is the main TypeScript file.

import { setup } from "./mud/setup";
import mudConfig from "contracts/mud.config";
const {
  systemCalls: { increment },
} = await setup();

The general setup code (opens in a new tab) for MUD.

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

// Attach the increment function to the html element with ID `incrementButton` (if it exists)
document.querySelector("#incrementButton")?.addEventListener("click", increment);

Here we attach the increment function from createSystemCalls.ts to incrementButton.


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.