#bevy-ui #menu #bevy #ui #gamepad-input #bevyui


A simple way of quickly creating nested menus in bevy that can be navigated with keys, gamepads and pointers

5 releases

0.2.0 Jul 11, 2023
0.1.6 Jul 11, 2023
0.1.5 Dec 4, 2022
0.1.1 Nov 18, 2022
0.1.0 Nov 18, 2022

#1494 in Game dev

48 downloads per month

MIT license

1.5K SLoC

License: MIT Doc Crate

🏃‍♂️ Bevy Quickmenu

Every game needs menus. Be it for the settings, for a pause screen, or for the main menu when the user enters the game.

While Bevy UI allows building menus, it is a cumbersome process especially if nested menus are needed. Even worse, though, is the effort required if you want your menu to be accessible with a keyboard, a gamepad, or the mouse.

Bevy Quickmenu offers all that. It is a super lightweight way of building in-game menus that can be controlled by all input devices. It even offers a simple way of having hover states. Everything can also be customized.

  • Super simple menu plugin for bevy, building upon Bevy UI
  • Keyboard, Mouse, Gamepad input is processed
  • Support mouse hover states in simplified Stylesheet
  • Many customizations possible (see examples/custom.rs)


Add to Cargo.toml:

bevy_quickmenu = "0.1.5"

Version Compatibility

Bevy Version Crates Version
0.11.0 0.2.0
0.10.0 0.1.6
0.9.0 0.1.5


Demo Gif

Quick Examples

An explanation of the required components


A generic type that hosts the state of your menu (e.g. which items are selected, and so on). Whenever this state changes, the menu is automatically redrawn.


(Conforms to ActionTrait): This enum defines all the actions your user can take. Such as SoundOn, SoundOff etc. When a user performs an action (by selecting the corresponding menu entry), the handle method is called on your ActionTrait implementation. ActionTrait has two generic types: Your State as well as a Event which you can define. This allows you to handle your action:

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
enum Actions {
    Control(usize, ControlDevice),

impl ActionTrait for Actions {
    type State = CustomState;
    type Event = MyEvent;
    fn handle(&self, state: &mut CustomState, event_writer: &mut EventWriter<MyEvent>) {
        match self {
            Actions::Close => event_writer.send(MyEvent::CloseSettings),
            Actions::SoundOn => state.sound_on = true,
            Actions::SoundOff => state.sound_on = false,
            Actions::Control(p, d) => {
                state.controls.insert(*p, *d);


(Conforms to the ScreenTrait). Each page or screen in your menu is defined by this enum. Note that menu screens are not nested!. Instead the ScreenTrait has a resolve function that allows you to return the corresponding menu definition for the given enum:

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
enum Screens {

impl ScreenTrait for Screens {
    type Action = Actions;
    fn resolve(&self, state: &mut CustomState) -> Menu<Screens> {
        match self {
            Screens::Root => root_menu(state),
            Screens::Controls => controls_menu(state),
            Screens::Sound => sound_menu(state),
            Screens::Player(p) => player_controls_menu(state, *p),


A menu is just a function that returns a list of MenuItem to be displayed. Each menu needs to have a distinct id. The example shows how the root and the sound menu are defined.

fn root_menu(_state: &mut CustomState) -> Menu<Screens> {
            MenuItem::action("Back", Actions::Close).with_icon(MenuIcon::Back),
            MenuItem::screen("Sound", Screens::Sound).with_icon(MenuIcon::Sound),
            MenuItem::screen("Controls", Screens::Controls).with_icon(MenuIcon::Controls),

fn sound_menu(state: &mut CustomState) -> Menu<Screens> {
            MenuItem::label("Toggles sound and music"),
            MenuItem::action("On", Actions::SoundOn).checked(state.sound_on),
            MenuItem::action("Off", Actions::SoundOff).checked(!state.sound_on),


In order to give you some flexibility, the menu item allows you to return five different types:

  • MenuItem::label: A small text label that cannot be selected
  • MenuItem::headline: A big text label that cannot be selected
  • MenuItem::action: A action that is performed when the user selects it
  • MenuItem::screen: Dive into a screen when the user selects this
  • MenuItem::image: A single image (including an optional Style)

In addition, a menu-item can have one of a couple of pre-defined icons or a custom icon

MenuItem::screen("Controls", Screens::Controls).with_icon(MenuIcon::Controls)
MenuItem::screen("Save", Screens::Save).with_icon(MenuIcon::Other(icons.save.clone()))

MenuItems can also be checked or unchecked:

MenuItem::action("On", Actions::SoundOn).checked(state.sound_on)
MenuItem::action("Off", Actions::SoundOff).checked(!state.sound_on)

Displaying a Menu

Here's a the annoated setup function from the example:

impl Plugin for SettingsPlugin {
    fn build(&self, app: &mut App) {
            // Register a event that can be called from your action handler
            // The plugin
            // Some systems

fn setup(mut commands: Commands) {

Removing a Menu

In order to remove a menu, there's the bevy_quickmenu::cleanup function. Usually, it is best to use it with the event that Bevy Quickmenu allows you to register:

enum BasicEvent {

impl ActionTrait for Actions {
    fn handle(&self, state: &mut BasicState, event_writer: &mut EventWriter<BasicEvent>) {
        match self {
            Actions::Close => event_writer.send(BasicEvent::Close),

fn event_reader(mut commands: Commands, mut event_reader: EventReader<BasicEvent>) {
    for event in event_reader.iter() {
        match event {
            BasicEvent::Close => bevy_quickmenu::cleanup(&mut commands),

Screenshot from the customized screen



~1M SLoC