bevy_state_ui
A simple UI library for Bevy that renders UI directly from application state.
Instead of manually managing UI entities, you declare your state and its render function, and bevy_state_ui will automatically keep the UI in sync whenever the state changes.
Features
- Declarative: define your UI based on a state struct.
- Efficient: only re-renders UI when the state actually changes (PartialEq comparison).
- Familiar: integrates seamlessly with Bevy's ECS and UI system.
- Simple: minimal API, easy to get started.
Installation
Add to your Cargo.toml:
[]
= "0.8"
Example
Here's a minimal app with clickable text that increments a counter:
Run it with:
Why use this instead of plain Bevy UI?
Normally in Bevy, building UI means:
- Spawning a hierarchy of
Node/Text/Buttonentities in startup systems. - Keeping track of
EntityIDs orQuerysto update them later. - Writing update logic that mutates styles, colors, and text when game state changes.
This often leads to boilerplate and imperative code. For example:
- You check a
Res<State>inside systems. - You manually update the
BackgroundColorof a button whenstate.hoveredchanges. - You may need to despawn/respawn UI trees when state transitions are large.
With bevy_state_ui, you flip the model:
- Define your UI as a pure function of state (
impl StateRender for State). - The library automatically detects when state changes (via PartialEq comparison).
- The old UI is despawned and re-rendered from the new state.
This means:
✅ Less boilerplate ✅ More predictable UI (no stale entity state) ✅ A workflow similar to React/Elm/SwiftUI for Bevy
If your mental model of UI is "render(state) → tree of UI nodes", this library gives you exactly that.
How it works
- You define a state struct that implements:
Resource(so it can live in the ECS world).Clone + PartialEq(to detect when the state actually changes).Debug(for optional debug logging).StateRender(your declarative UI description).
- Register the plugin:
app.add_plugins(BevyStateUiPlugin::<MyState>::default()) - The plugin runs a system each frame that:
- Uses Bevy's
is_changed()as a fast path (zero cost when nothing was mutated). - Compares the current state against a stored previous value via
PartialEq. - If the state truly changed, despawns the previous UI root and calls your
renderfunction. - This two-layer approach avoids false re-renders from
DerefMutaccess that doesn't change the value.
- Uses Bevy's
This lets you think of UI as a pure function of state, much like React, Elm, or SwiftUI.