← All Workshops

MudEngine Part 4: Single-Player Dioxus GUI

Step 9 / 13

The Grid component

The WorldGrid component renders the 9 rooms as a 3×3 CSS grid. It takes a ReadSignal<World> prop so it reactively reads the world state.

Each cell shows the room name. The room the player currently occupies is highlighted in blue. The other rooms are dimmed — the player can see the full map but knows where they are at a glance.

Create src/components/world_grid.rs with this component. It imports World from the game module — the frontend reads the game state but never owns it.

mud-engine/src/components/world_grid.rs
use crate::game::world::World;
use dioxus::prelude::*;

#[component]
pub fn WorldGrid(world: ReadSignal<World>) -> Element {
    let w = world.read();
    let player_room = w.player_room;
    let names: Vec<String> = w.rooms.iter().map(|r| r.name.clone()).collect();
    drop(w); // release borrow before rsx! — holding it would make any world.write() in handlers panic

    rsx! {
        div {
            class: "world-grid",
            for (idx, name) in names.iter().enumerate() {
                if idx == player_room {
                    div {
                        key: "{idx}",
                        class: "cell active",
                        "{name}"
                    }
                } else {
                    div {
                        key: "{idx}",
                        class: "cell",
                        "{name}"
                    }
                }
            }
        }
    }
}
🎯 How ReadSignal works

ReadSignal<World> is a prop type that wraps any reactive source (a Signal<World>, a Memo<World>, or another ReadSignal). The component calls .read() to get a Ref<World> — a guarded reference that Dioxus tracks as a dependency.

When the parent writes to the original Signal<World> (e.g., world.write().go("north")), all components that read that signal via .read() automatically re-render.

We call .read() once, clone what we need (the room names), then drop(w) to release the borrow guard before entering the rsx! block. This avoids holding a borrow across the for loop.

Step 9 / 13