loomgui.com ↗

Your first screen

This walks you from a freshly installed Loom package to a working screen you can click. The flow is: scaffold the UI app, write a bridge in C#, add the Loom UI component to your scene, then build the screen.

  1. Scaffold the UI project.

    In the Editor, run Loom -> Setup UI Project. It creates a Vite + Solid app at <ProjectRoot>/UI/ from the bundled starter template and then runs Loom -> Sync UI Dependencies for you (installs the @loomgui/* packages and runs npm install). You get:

    UI/
      index.html
      package.json
      tsconfig.json
      vite.config.ts          # outDir 'dist', base './' preconfigured for Loom
      src/index.tsx           # boots the bridge, mounts <App/>
      src/App.tsx             # starter "Loom is running" screen
      src/styles.css
      generated/bridge.d.ts   # typed bridge surface (regenerated from your C#)

    It won’t overwrite a non-empty UI/. If one already exists, it tells you and stops.

  2. Define your bridge in C#.

    Create a bridge class anywhere under Assets/ (e.g. Assets/Scripts/UI/GameBridge.cs). The [Bridge] attribute marks it as the contract between C# and your UI. The class must inherit LoomBridgeBase; the source generator requires it:

    // Assets/Scripts/UI/GameBridge.cs
    using Loom;
    
    [Bridge]
    public partial class GameBridge : LoomBridgeBase {
      [BridgeState] public int Score { get; set; }
      [BridgeAction] public void AddPoint() { Score += 1; }
    }

    It must be partial so the source generator can add plumbing to it. Every time Unity recompiles, the matching TypeScript types are emitted automatically to UI/generated/bridge.d.ts (open it any time with Loom -> Open Generated bridge.d.ts).

  3. Add a Loom UI component to your startup scene.

    Open your startup scene and run Loom -> Setup UI in Current Scene. This adds a single Loom UI GameObject that handles the canvas, input capture, and runtime initialization for you. Re-running it is safe; it skips setup if a LoomUI component is already present.

    Alternatively, add the LoomUI component manually to any existing GameObject in the scene. That’s the only scene setup step; you don’t need a separate bootstrap MonoBehaviour.

  4. Hit Play. LoomUI builds the Loom Canvas and starts the runtime on Awake. Access your bridge from gameplay code via the generated static property:

    GameBridge.Instance.Score = 10;
  5. Build your screen.

    The scaffold’s UI/src/App.tsx renders a placeholder. Replace it to read state and call actions through the bridge, the C# and TypeScript sides line up by name (Scorescore, AddPointaddPoint):

C#
[BridgeState]
public int Score { get; set; }

[BridgeAction]
public void AddPoint() {
  Score += 1;
}
TS
// UI/src/App.tsx
import { useBridge } from "@loomgui/bridge";

export default function App() {
  const bridge = useBridge();
  return (
    <div class="app">
      <span>{bridge.state.score}</span>
      <button onClick={() => bridge.actions.addPoint()}>+1</button>
    </div>
  );
}

Seed the matching initial state in UI/src/index.tsx’s bootBridge({ initial: { score: 0 } }) call, it’s the snapshot the UI renders before the engine’s first push, and the seed for Mock mode.

Click +1 in Play mode: bridge.actions.addPoint() reaches C#, Score bumps, and the UI re-renders with the new value.

Where to go next