BuildBot

The Desktop App

The local control plane UI

Lesson 4 of 10

What you'll learn

  • See how the desktop UI stays in sync with the Go core via events
  • Understand "local-first" rendering versus fetching from a server
  • Build a fleet view that renders node state as cards

The Quorum window is a normal React app — Vite for the dev server and build, Tailwind for styling, pages for Dashboard, Playground, Models, Nodes, and Settings. What's different from the web apps you've built is the data source. There's no API server to fetch from. State arrives from the Go core: a snapshot when the page mounts, then a stream of events as peers come and go.

That's the local-first shape. Instead of useEffect(() => fetch(...)) against a backend, you call a binding once for the initial snapshot and subscribe to events for updates. The UI is always reflecting the core's current truth, pushed the instant it changes.

Snapshot, then subscribe

import { useEffect, useState } from "react";
import { ListNodes } from "../wailsjs/go/main/App";
import { EventsOn } from "../wailsjs/runtime";

function useNodes() {
  const [nodes, setNodes] = useState([]);
  useEffect(() => {
    ListNodes().then(setNodes);                  // initial snapshot from Go
    const off = EventsOn("nodes:changed", setNodes); // live pushes from Go
    return off;                                   // unsubscribe on unmount
  }, []);
  return nodes;
}

The Go core owns the cluster registry; the React layer is a thin, reactive view of it. When a heartbeat marks a peer offline, Go emits nodes:changed, and every open page re-renders. No polling loop, no stale list.

Render the source of truth, don't duplicate it

Resist copying core state into a second store you then have to reconcile. Keep one snapshot-plus-subscription per concern (nodes, models, cloud status) and let the Go core be the single owner. Divergence bugs disappear when there's nothing to diverge.

The challenge is a React fleet view: given a list of nodes, render a card per node with an online/offline dot. This is the actual shape of the Nodes page — only the data source (a prop here, a binding in the app) differs.

Fleet cards from node state

Edit the nodes array — flip an `online` flag or add a node — and watch the cards update.

Loading editor…
Knowledge check

How does the desktop UI learn that a peer just went offline?

Next: how nodes on the same network discover each other in the first place — mDNS and UDP broadcast.

Saved on this device. Sign in to sync your progress everywhere.