#bevy-ui #css #bevy #html #slider

bevy_extended_ui

Create simply ui's with css and html for bevy

47 releases (9 stable)

Uses new Rust 2024

new 1.7.0-beta.1 Jun 13, 2026
1.6.0-beta.2 May 30, 2026
1.5.0 Mar 16, 2026
1.0.0 Dec 29, 2025
0.2.2 Jul 22, 2025

#84 in GUI

Apache-2.0

1.5MB
41K SLoC

Logo

bevy_extended_ui


Crates.io Downloads license Build codecov

Since I've been writing a game in the Bevy engine lately, I created this crate. In my game, I need more complex UI elements that Bevy doesn't currently support. These include sliders, choice boxes, check boxes, radio buttons, and so on. So I set about implementing these elements using the standard bevy_ui system. I'd like to make this project available to you so that you can use elements other than just nodes, buttons, or images. If you're missing a widget and know how to create it, it would be great if you could add it. Otherwise, feel free to create a ticket.

Features

There are many features in this crate. You can see all current supported widgets here. Available features:

  • Full HTML support.
  • CSS support but not all CSS properties.
  • Hot reload support. (bevy:file_watcher feature needed)
  • HTML Bind support for interacting with the code.
  • Font support for family and weight.
  • Animation support (@keyframes).
  • Breakpoint support (@media for window size).
  • Validation for widgets like required fields.
  • CSS * support.
  • Custom Cursor or system cursor support.
  • Form Widget for validation and submission.
  • Dialog system with Bevy modals and native system dialogs.
  • Customizable theme.

There are many other things, but currently you can use the core (HTML / CSS) features.

Toolchains

This project supports both stable and nightly Rust.

  • Default: stable via rust-toolchain.toml
  • Nightly: cargo +nightly-2025-08-07 ... or rustup override set nightly-2025-08-07
  • Optional: use rust-toolchain-nightly.toml as a drop-in replacement if you want nightly by default

Windows build prerequisites

You can build on Windows with either MSVC or GNU:

  • MSVC path: use x86_64-pc-windows-msvc and install Visual Studio 2017+ (or Build Tools for Visual Studio) with Desktop development with C++ so link.exe is available.
  • GNU path (no Visual Studio): use x86_64-pc-windows-gnu and install MinGW tools (for example via MSYS2 with the mingw-w64-ucrt-x86_64-toolchain package), then ensure C:\msys64\ucrt64\bin is on PATH.

If Cargo fails with error: linker 'link.exe' not found, you are building with MSVC but the Visual C++ tools are not available on PATH.

How to use?

Add this to your Cargo.toml:

[dependencies]
bevy_extended_ui = "1.5.0"
bevy_extended_ui_macros = "1.5.0"

Features

Feature Description
default Enables css-breakpoints, fluent, providers, svg, and extended-dialog.
wasm-default Web preset: wasm-breakpoints + clipboard-wasm with legacy WASM CSS/style pipeline compatibility.
css-breakpoints Desktop breakpoints via primary window.
wasm-breakpoints WASM breakpoints via browser viewport.
fluent Enables Fluent Language support.
properties-lang Enables Java Properties Language support.
clipboard-wasm Enables WASM clipboard support web.
svg Optional SVG image support for UI images/icons (rasterized to Bevy Image); not enabled by default.
providers Enables custom HTML providers (e.g. theme-provider).
extended-dialog Enables the dialog system with BevyApp and desktop System providers.
extended-framework Enables the experimental Angular-like component base (*.component.html + *.component.rs).

Then, you add the plugin to your main.rs or on any point at a build function:

fn build(&mut app: App) {
    app.add_plugin(ExtendedUiPlugin);
}

Then you create an HTML file:

<html lang="en">
  <head>
    <meta name="test" />
    <meta charset="UTF-8" />
    <title>Title</title>
    <!-- You can link your CSS file here. -->
    <link rel="stylesheet" href="base.css" />
    <!-- <link rel="stylesheet" href="base2.css"> You can add more CSS files.-->
  </head>
  <body>
    <!-- You can use HTML bindings here. like the onclick attribute. -->
    <button onclick="test_click">Button</button>
  </body>
</html>

And finally,

use bevy_extended_ui::old::registry::UiRegistry;

fn build(&mut app: App) {
    app.add_systems(Startup, |mut reg: ResMut<UiRegistry>, asset_server: Res<AssetServer>| {
        let handle: Handle<HtmlAsset> = asset_server.load("YOUR_ASSETS_LOCATION/test.html");
        reg.add_and_use("test".to_string(), HtmlSource::from_handle(handle));
    });
}

#[html_fn("test_click")]
fn test_click(In(event): In<HtmlEvent>) {
    println!("Button clicked!");
}

Note that currently you can use this binding:

  • onclick
  • onchange Only for <select>, <fieldset>, <input>, <date-picker>, <checkbox>, <slider> and <colorpicker> elements.
  • action on <form> for submit handlers with collected input data
  • oninit
  • onmouseover
  • onmouseout
  • onfoucs
  • onscroll
  • onkeydown
  • onkeyup
  • ondragstart
  • ondrag
  • ondragstop

Extended framework (experimental)

To enable the Angular-like base, turn on the Cargo feature:

[dependencies]
bevy_extended_ui = { version = "1.5.0", features = ["extended-framework"] }

Alias also available: extended_framework.

Base folder layout:

assets/
  index.html
  ui/
    bevy_ang/
      main.component.html
      main.component.css

src/
  packages/
    main.component.rs

Behavior in extended-framework mode:

  • Startup requires assets/index.html exactly as entrypoint.
  • UiRegistry/ExtendedRegistryPlugin are disabled in this mode (legacy path).
  • Components are resolved by tag name from *.component.rs definitions: template_name, template_file, styles.
  • template_file must match the component rust filename (main.component.rs -> main.component.html).
  • Component styles are injected into the compiled index template automatically.

Dialog system

extended-dialog is enabled by default. It provides:

  • DialogProvider::BevyApp for in-window modals (floating panel or bottom sheet)
  • DialogProvider::System for native desktop dialogs (Linux/macOS/Windows)
  • Modal types: Default, Failure, Question, Blank

HTML dialog widget usage:

<button id="open-system-dialog">Open System Dialog</button>
<button id="open-bevy-dialog">Open Bevy Dialog</button>

<dialog trigger="open-system-dialog" renderer="system" type="error">
  Error message text for system dialog.
</dialog>

<dialog trigger="open-bevy-dialog" renderer="bevy-app" type="info">
  <div>
    <p>Hello World</p>
    <img src="/extended_ui/icons/color.png" />
  </div>
</dialog>

Supported dialog attributes:

  • renderer: bevy-app | system
  • type: warn | error | info | blank
  • trigger (alias: triggger): id of the widget that opens the dialog

WASM support

Now we have wasm support but still not fully tested! Here is an example to show you how to use it:

fn main() {
    #[cfg(target_arch = "wasm32")]
    console_error_panic_hook::set_once();

    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                title: "Bevy WASM".into(),
                canvas: Some("#bevy".into()),
                fit_canvas_to_parent: true,
                prevent_default_event_handling: true,
                ..default()
            }),
            ..default()
        }).set(AssetPlugin {
            meta_check: AssetMetaCheck::Never,
            ..default()
        }))
        .add_plugins(ExtendedUiPlugin)
        .add_systems(Startup, load_ui)
        .run();
}

use bevy_extended_ui::old::registry::UiRegistry;

fn load_ui(mut reg: ResMut<UiRegistry>, asset_server: Res<AssetServer>) {
    let handle: Handle<HtmlAsset> = asset_server.load("test.html");
    reg.add_and_use("test-ui".to_string(), HtmlSource::from_handle(handle));
}

and the index HTML:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Bevy Web</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
        background: #333;
      }
      #container {
        width: 100%;
        height: 100%;
      }
      canvas {
        width: 100%;
        height: 100%;
        display: block;
      }
    </style>
    <link data-trunk rel="copy-dir" href="assets" />
  </head>
  <body>
    <div id="container">
      <canvas id="bevy"></canvas>
    </div>
  </body>
</html>

It was tested with the crate trunk, which worked well. This trunk.toml file was used:

[build]
dist = "dist"

[[copy]]
source = "assets"
dest = "assets"

[serve]
no_spa = true

Animation support

Basic @keyframes usage example:

@keyframes button-pulse {
  0% {
    transform: scale(1);
    background-color: #4c8bf5;
  }
  50% {
    transform: scale(1.05);
    background-color: #72a1ff;
  }
  100% {
    transform: scale(1);
    background-color: #4c8bf5;
  }
}

.cta-button {
  animation: button-pulse 1.4s ease-in-out infinite alternate;
}

Template data (reactive bindings)

There is no built-in loop/templating engine. Instead, the HTML parser collects {{...}} placeholders into HtmlInnerContent, and you replace them in Rust based on your own model.

See:

  • examples/reactive_binding.rs
  • examples/template_iteration.rs

The iteration example renders a list by joining items into a single string (with \n line breaks) and substituting {{inventory.list}} at runtime.

You can now use @keyframes in your CSS. There is now a limit tested; this means that you can use any CSS property.

Breakpoint support

Basic @media usage for breakpoints:

.desktop-panel {
  display: flex;
}

@media (max-width: 900px) {
  .desktop-panel {
    display: none;
  }
}

Breakpoint runtime source is feature-based:

  • css-breakpoints (default): tracks Bevy PrimaryWindow size.
  • wasm-breakpoints: tracks browser viewport (window.innerWidth/innerHeight) for WASM and overrides css-breakpoints when enabled.
  • wasm-default: enables wasm-breakpoints and clipboard-wasm as a web preset.

What comes next?

Next, I'd like to build a website that's structured like React documentation. There you'll always be able to see all the patches and how to apply them! If anyone has any ideas, I'd be happy to hear them.

Compatibility

Note: This project is currently under construction and not suitable for large projects!. The new component system is still in development and may change in the future. Do not use it in production!

Bevy version bevy_extended_ui version
0.19.0 1.6.1 - main
0.18.1 1.2.0 - 1.6.0
0.17.0 1.0.0 - 1.1.0
0.16.0 0.1.0 - 0.2.2

Note: WASM is not correctly supported in version 1.4.0 there is a layout bug. I'm working on this bug, but this needs time! Version 1.4.1 and above ar fixed for WASM.

Note: Version 0.1.0–0.3.0 are deprecated and will not be supported. If you’re interested in a version for bevy 0.16, then create an issue!

Link to Widget attributes
Link to Event Rules
Link to Language Rules
Link to CSS Properties
Link to Patches

Dependencies

~92–145MB
~2.5M SLoC