Introduction
mevy (pronounced “me-vee”) is a set of Rust procedural macros that make writing Bevy code less tedious. It doesn’t add plugins, resources, or systems — it just gives you better syntax for the patterns you already use.
The Problem
Bevy’s ECS and UI APIs are expressive but verbose. Consider spawning a styled panel — the same structure at three levels of verbosity:
// Raw Bevy
cmd.spawn((
Node {
width: Val::Px(300.0),
height: Val::Px(200.0),
padding: UiRect::all(Val::Px(16.0)),
border_radius: BorderRadius::all(Val::Px(12.)),
border: UiRect::all(Val::Px(2.0)),
..default()
},
BorderColor::all(Srgba::hex("#ee6677").unwrap().into()),
BackgroundColor(Srgba::hex("#1a1a2e").unwrap().into()),
));
That’s a lot of boilerplate for a simple rounded rectangle. With mevy’s CSS-like mode:
// mevy — CSS-like mode
cmd.spawn(ui!((
size: 300px 200px;
padding: 16px;
border_radius: 12px;
border: 2px #ee6677;
background: #1a1a2e;
)));
For quick UI, the slim mode cuts it even further:
// mevy — slim mode
cmd.spawn(ui!(
w:300 h:200 p:16 round:12
border:2px#ee6677 bg:#1a1a2e
)?));
Three levels. Same result. And your IDE still knows what’s going on — mevy preserves the token structure so autocomplete and type inference work normally.
What mevy Gives You
A CSS-like language for Bevy UI
Write border_radius: 12px instead of BorderRadius::all(Val::Px(12.0)). mevy translates CSS concepts into Bevy components automatically. Edge selection (border: 5px 2px) and corner selection (border_radius: 5px 2px 8px) follow the same CSS conventions you already know.
Nested entity spawning without callbacks
Bevy’s .with_children() callback is the only way to build hierarchies, but it’s hard to reference children by name. mevy’s [child_name][...] syntax lets you name children and reference them anywhere - even before they’re defined.
Inline values without ceremony
Write #ff0000 for a color, 100px for a value, [>10px 5px] for a rectangle. No function calls, no type annotations, no Srgba::hex().unwrap() boilerplate.
Design Philosophy
Macros only. mevy doesn’t change how Bevy works - it just makes the code you write cleaner.
- Token-based: Everything happens at compile time. Your IDE autocomplete, type inference, and error messages all work normally.
- Version-aware: Feature flags handle Bevy API changes between versions. You can upgrade Bevy without changing your mevy code.
- Composable:
ui!{}andentity!{}work together. Useui!{}insideentity!{}for nested UI,code!{}for inline values anywhere. - Extensible: Define your own UI components with custom fields. Reuse them across your project like CSS classes.
Supported Bevy Versions
mevy supports Bevy 0.15 through 0.18. You must pin your version in Cargo.toml:
mevy = { version = "0.3", features = ["0.18"] }
Quick Links
- Installation - How to add mevy to your project
- Quick Start - Build your first mevy-powered app
- Macros Overview - All macro families
- API Reference - Complete field and selector reference
- FAQ - Frequently asked questions
- Migration Guide - Migrate between Bevy versions
- Changelog - Release history
Installation
Add to Cargo.toml
[dependencies]
bevy = { version = "0.18", features = ["ui"] }
mevy = { version = "0.3", features = ["0.18"] }
Then import everything at once:
use bevy::prelude::*;
use mevy::*;
That’s all — entity!{}, ui!{}, and code!{} are now available.
Why the Version Feature?
Bevy changes its APIs between minor versions. mevy handles these differences for you, but it needs to know which version you’re targeting to generate the correct code. The version feature tells the macro which Bevy API to emit.
Important
Forgetting the version feature will cause a compile error. The error message will tell you exactly what to add.
Selecting a Bevy Version
| Bevy Version | mevy Feature |
|---|---|
| 0.15.x | features = ["0.15"] |
| 0.16.x (stable) | features = ["0.16"] |
| 0.16.x (RC) | features = ["0.16-rc"] |
| 0.17.x | features = ["0.17"] |
| 0.18.x | features = ["0.18"] |
Optional Features
| Feature | Description |
|---|---|
ui (default) | Enables the ui!{} macro for CSS-like UI notation |
experimental | Enables experimental helper macros (gere!, geco!, etc.) |
The experimental feature is unstable — the API may change between versions without notice. Only enable it if you need those helpers and are comfortable with potential breakage.
# With experimental features
mevy = { version = "0.3", features = ["0.18", "experimental"] }
Using Crates Separately
You can also use individual crates if you only need specific macros:
[dependencies]
mevy_core = { version = "0.1.1", features = ["0.18"] } # code!{} only
mevy_ui = { version = "0.3.2", features = ["0.18"] } # ui!{} only
use mevy_core::code; // hex colors, Val, UiRect
use mevy_ui::ui; // CSS-like UI notation
This is useful if you want to avoid the experimental default feature on mevy_ecs, or if your project only needs one macro family.
Quick Start
This guide walks you through mevy’s three macro families, from setup to a working example.
Setup
Add mevy to your Cargo.toml with your Bevy version:
mevy = { version = "0.3", features = ["0.18"] }
Then import everything:
use bevy::prelude::*;
use mevy::*;
Step 1: CSS-like UI with ui!{}
The ui!{} macro lets you write Bevy UI components using CSS-inspired syntax. Instead of constructing Node, BackgroundColor, BorderRadius, etc. by hand, you write properties:
fn startup(mut cmd: Commands) {
cmd.spawn(Camera2d::default());
cmd.spawn(ui!((
size: 200px 150px;
background: #1a1a2e;
border: 3px #ee6677;
border_radius: 8px;
justify_content: center;
align_items: center;
)));
}
The same result in slim mode — fewer keystrokes, same components:
cmd.spawn(ui!(
w:200 h:150 bg:#1a1a2e
border:3px #ee6677 round:8
justify_content:center align_items:center
)?));
See ui!{} docs for all fields and modes.
Note
The
?at the end is optional — any token works. It’s just a visual cue that the macro returns a value. Hover over the call in your IDE to see the type.
Step 2: Inline values with code!{}
The code!{} macro replaces tedious patterns with shorthand inside regular Rust code. It works anywhere — in struct fields, function arguments, let bindings, etc.:
// Hex colors
let color = code!{#FF0000};
// Values with units
let width = code!{100px};
let auto = code!{@};
// UiRect with CSS-like edge notation
let margin = code!{[>10px 5px]};
You can also chain methods on the result:
code!{BoxShadow{
color: #FF1265.mix(&#F93ECA, 0.4).with_alpha(0.2),
}}
See code!{} docs for all patterns.
Step 3: Entity hierarchy with entity!{}
The entity!{} macro handles entity spawning, children, and modification in one expression. Children are created with [name][...] and can be referenced by name anywhere — even before they’re defined:
fn startup(mut cmd: Commands) {
entity!{
Camera2d;
BackgroundColor(#0a0a0a);
[button][
ui!((
size: 120px 40px;
background: #ee6677;
border_radius: 6px;
justify_content: center;
));
Text::new("Click me");
> Pointer<Click> {
this.insert(BackgroundColor(#ff4455));
};
]
}
}
The > Pointer<Click> { ... } syntax attaches an observer directly on the entity. No need for separate observer registration.
See entity!{} docs for all features.
Step 4: Entity queries
Target and modify entities using component queries — no separate system needed:
fn update(mut cmd: Commands) {
entity!{
<world|#*Button.all()> // every entity with Button component
<Children.iter()> // then their children
BackgroundColor(#00ff00);
}
}
See Entity Queries guide for detailed patterns.
How they work together
All three macros are designed to nest: entity!{} contains ui!{} for UI, and code!{} works inside both:
entity!{
ui!((
background: code!{#ff0000}; // code!{} inside ui!{}
));
[child][
ui!(( size: 50px; ));
]
}
Slim Mode
For quick UI, use the slim shorthand — inspired by TailwindCSS:
cmd.spawn(ui!(
w:200 h:150 bg:#1a1a2e
border:3px #ee6677 round:8px
));
See ui!{} Slim Mode for all shortcuts.
What’s Next
- entity!{} docs — Entity spawning & modification
- ui!{} docs — CSS-like UI notation
- code!{} docs — Inline value construction
- Building a UI guide — Complex layouts
- Entity Queries guide — Query patterns
- FAQ — Common questions
Macros Overview
mevy provides three core macro families, each solving a different pain point in Bevy development. They’re designed to work together — use ui!{} to build UI bundles, code!{} for inline values, and entity!{} to spawn and manage entities.
Quick Reference
| Macro | Purpose | Use when you need… |
|---|---|---|
code!{} | Inline value construction | Hex colors, Val, UiRect without function calls |
ui!{} | CSS-like UI bundles | Multiple UI components without boilerplate |
entity!{} | Entity spawning & modification | Hierarchies, queries, and observers in one expression |
When to Use Which
- Just a color or value? →
code!{} - A styled UI panel? →
ui!{} - A complete entity with children? →
entity!{} - All three together? → Use them together.
entity!{}nestsui!{}naturally, andcode!{}works inside both.
How They Work Together
entity!{
<world>
// ui!{} creates a bundle of UI components
ui!((
size: 100px 100px;
background: #ff0000;
));
// code!{} creates inline values
BackgroundColor(code!{#00ff00});
// Nested children with named entities
[child][
ui!(( size: 50px 50px; ));
.observe(on_click);
]
}
Troubleshooting
“Missing bevy version” compile error
You need to specify the Bevy version feature in Cargo.toml:
mevy = { version = "0.3", features = ["0.18"] }
See Installation for the full list of supported versions.
Experimental features unexpectedly enabled
mevy_ecs has experimental as a default feature. To disable it:
mevy_ecs = { version = "0.2.4", default-features = false }
Navigation
- entity!{} — Entity spawning & modification
- ui!{} — CSS-like UI notation
- code!{} — Code replacement macro
- Experimental Helpers —
gere!,geco!,cen!,den!,wen!
entity!{} — Entity Spawning & Modification
The entity!{} macro lets you spawn entities, build hierarchies, and run queries in a single expression. It replaces Bevy’s .spawn().with_children() callback pattern and Commands::spawn() boilerplate.
The Mental Model
Think of entity!{} as a tree builder. The first entry <...> selects where to operate (which world, which entity). Everything after that operates on the selected target. Children are created with [name][...] blocks, which nest recursively.
Basic Spawning
entity!{
Transform!;
Name("Hello");
}
This spawns a new entity with Transform::default() and Name("Hello"). The ! shorthand expands to ::default() — more on that below.
Hierarchy with Children
Use [name][...] to create child entities:
entity!{
Node { size: 100px 100px; ! }
BackgroundColor(#ff0000);
[child1][
Node { size: 50px 50px; ! }
BackgroundColor(#00ff00);
]
[named_child][
Node { size: 50px 50px; ! }
BackgroundColor(#0000ff);
]
}
Children can be referenced by name anywhere — even before they’re defined. This is useful when you want to set a component on a child from a parent:
entity!{
Node { entity: named_child; } // reference before definition
[named_child][
// ...
]
}
World/Entity Selection
The first entry <...> determines where to operate and what to target:
Spawning (creating new entities)
| Selector | World Type | Entity |
|---|---|---|
| (none) | Commands | new (spawned) |
world | Commands (named) | new |
commands | Commands (named) | new |
+world | World | new |
-world | DeferredWorld | new |
*this | EntityCommands | new |
^cbuild | ChildBuilder | new |
+*this | EntityWorldMut | new |
Modifying (targeting existing entities)
| Selector | World Type | Entity |
|---|---|---|
commands|entity | Commands | specific Entity |
|entity | Commands | specific Entity |
commands| | Commands | me: Entity |
| | Commands | me: Entity |
+world|entity | World | specific Entity |
-world|entity | DeferredWorld | specific Entity |
*this|entity | EntityCommands | specific Entity |
^cbuild|entity | ChildBuilder | specific Entity |
+*this|entity | EntityWorldMut | specific Entity |
Component Query Selectors
For targeting entities by component:
| Selector | Behavior |
|---|---|
!#Marker | Single entity (panics if none/multiple) |
!#Marker.0 | Entity field on component (panics if None) |
#*Comp.all() | Iterator over entities from component |
Warning
Plain
#Marker(without!or*) is not supported. Use!#Markerfor a single entity or#*Comp.all()for an iterator.
Resource Selectors
For targeting entities stored in a resource:
| Selector | Behavior |
|---|---|
@Resource.get() | Safe: does nothing if resource or value is None |
@*Resource.all() | Iterate over entity iterator from resource |
!@Resource.0 | Risky: panics if resource or value is None |
Warning
Resource/component selectors (
@,#) only leak child entities, not the root. If you use<|!#Marker>to select a specific entity, spawned children are still available outside the macro, but the selected entity itself is not.
See Entity Selectors API for the complete selector reference.
Entity Redirection
After the initial selector, you can chain redirections to drill down through entities:
entity!{
<world|#*Parent.all()> // 1. Start with every entity with Parent
<Children.iter()> // 2. Redirect to their children
<Children.iter()> // 3. Redirect to grandchildren
BackgroundColor(#ff0000); // 4. Color them red
.despawn(); // 5. Then despawn them
}
Each <...> in the chain redirects the target. The final BackgroundColor(#ff0000) applies to all entities selected by the last redirection.
Leaking & Returning
By default, the spawned entity variable me is scoped to the macro block. Named children are always available. Symbols at the end of the macro control additional visibility:
| Symbol | Behavior |
|---|---|
> | Make me available outside the macro block |
< | Return the root entity |
@ | Wrap the entire body in a closure ` |
Leaking me
entity!{
Bundle::new(5);
[child][]
>}
// me; // the root entity (now available outside)
// child; // the named child entity (always available)
Returning
let enty = entity!{
Bundle::new(5);
<};
Closure
entity!{
Bundle::new(5);
@};
// The entire body is wrapped in: |mut world: Commands| { ... }
// Useful for system parameters that need a `Commands` argument.
Note
Resource/component selectors (
@,#) only leak child entities, not the root.
Quick Observer
Attach observers directly on entities without separate registration:
entity!{
// Single '>' — 'this' = EntityCommands
> Pointer<Click> {
this.despawn();
}
// Double '>>' — 'entity' = Entity, 'world' = &mut World (inside auto-wrapped queue)
>> Pointer<Click> {
// 'entity' and 'world' are available here automatically
// The block is wrapped in world.queue(...) by the macro
}
}
The single > variant gives you this: EntityCommands inside the block. The double >> variant gives you world: &mut World and entity: Entity, letting you queue commands to be executed later. Note that the user’s block is automatically wrapped in world.queue(...), so you don’t need to write it yourself.
Shorthand Notations
Inside entity!{}, mevy recognizes several shorthand patterns:
| Syntax | Expands to |
|---|---|
Bundle! | Bundle::default() |
Bundle{a:3,!} | Bundle{a:3, ..Default::default()} |
Bundle: 3 | Bundle::new(3) |
bundle_fn: 3 | bundle_fn(3) |
macro!: 3, 4 | macro!{3, 4} |
.method: 5 | .method(5) |
The ! at the end of a type calls ::default(). Inside a struct, ! inserts ..Default::default(). The : after a type calls its new() method. These are all syntactic sugar — the macro expands them to regular Rust.
Note
Some of these shorthands (
!,:) are also recognized insidecode!{}. The behavior is the same, but the context is different (inline values vs entity commands).
try and {..}
entity!{
try SomeBundle; // Only inserts if SomeBundle is Some
{
this.insert(Component);
this.observe(on_click);
}
}
try conditionally inserts a bundle if it’s Some. The {..} block gives you direct access to this: EntityCommands for arbitrary operations.
Note
tryisentity!{}-only. The!shorthand (for::default()) is shared withcode!{}but behaves slightly differently in each context.
Ancestors Array
Inside nested children, ancestors[] gives you parent entities:
[[[[[
Component{ entity: ancestors[3] };
]]]]]
ancestors[0]= direct parentancestors[n]= root entity of the current macro call
This is useful when you need to reference a specific ancestor from a deeply nested child.
Complete Example
fn startup(mut world: Commands) {
entity!{
Camera2d;
BackgroundColor(#0a0a0a);
ui!((
size: 200px 150px;
background: #1a1a2e;
border: 3px #ee6677;
border_radius: 8px;
justify_content: center;
align_items: center;
));
[button][
ui!((
size: 120px 40px;
background: #ee6677;
border_radius: 6px;
justify_content: center;
align_items: center;
));
Text::new("Click me");
> Pointer<Click> {
this.insert(BackgroundColor(#ff4455));
};
]
}
}
See Also
- Entity Selectors API — Full selector reference
- Entity Queries Guide — Query and modify patterns
- Migration Guide — Version-specific changes
ui!{} — CSS-like UI Notation
The ui!{} macro lets you write Bevy UI components with CSS-inspired syntax. Instead of constructing Node, BackgroundColor, BorderRadius, etc. by hand, you write properties that mevy translates into the appropriate Bevy components.
Four Modes
The macro has four modes, selected by delimiters and naming. Each serves a different purpose:
1. Tuple Inline Mode — ui!((...))
Returns a tuple of the mentioned components, ready to be inserted into an entity:
cmd.spawn(ui!((
size: 100px 100px;
background: #ff0000;
border: 5px #00ff00;
border_radius: 6px;
box_shadow: 10% 10% 3px 8px #ffaa44;
)));
Use this when you need a bundle of UI components for a single entity. The order of components in the returned tuple is based on first mention and is consistent across calls.
2. Slim Inline Mode — ui!(...)
Short syntax inspired by TailwindCSS. Fields are separated by whitespace, and : ends the current field’s value:
cmd.spawn(ui!(
w:100 h:100 bg:#fff round:6 border:5#f00
shadow:10%+10%+3px+8px#fa4
)?));
Use this for quick styling where you don’t need the full CSS-like syntax. It’s faster to type but less readable for complex layouts.
Note
The trailing
?is optional — any token works. It’s just a visual cue that the macro returns a value. Hover over the call in your IDE to see the type.
3. Function Tuple Mode — ui!{name(...)}
Defines a function that returns impl Bundle. The function can be called like any other:
ui!{neat_box(
size: 100px 100px;
background: #ffffff;
)}
// Equivalent to:
// pub fn neat_box() -> impl Bundle { ui!(( size: 100px 100px; background: #ffffff; )) }
// Usage:
cmd.spawn(neat_box());
Use this when you want to define reusable UI components — like CSS classes or React components. Call them wherever you need that styled element.
4. Edit Function Mode — ui!{name{...}}
Defines a function that mutates existing components. The parameters are &mut references to the components that will be modified:
ui!{into_red_glow{
border: _ #ffffff; // _ keeps existing width, changes color
background: #ff0000;
box_shadow: #ff0000; // only color (no values = color only)
}}
// Equivalent to:
// pub fn into_red_glow(
// border_color: &mut BorderColor,
// background_color: &mut BackgroundColor,
// box_shadow: &mut BoxShadow
// ) { ... }
Use this when you want to modify existing entities — for example, adding a glow effect on hover. The _ placeholder keeps the existing value for that field.
CSS-like Notation
Inside ui!{}, you can write:
| Syntax | Result |
|---|---|
100px | Val::Px(100.) |
50% | Val::Percent(50.) |
3vw, 1vh, 2vmin, 4vmax | Val::Vw/Vh/VMin/VMax |
#ff0000 | Color::Srgba(Srgba::hex("#ff0000").unwrap()) |
red, gray, cyan | CSS color from bevy::color::palettes::css |
$my_var | Pass a variable directly (e.g., $handle, $image) |
The # prefix for colors supports 3-digit (#f00), 4-digit (#f00a), 5-digit (#f00a0), 6-digit (#ff0000), and 8-digit (#ff000080) hex codes. CSS color names like red, gray, cyan are resolved from Bevy’s color palette.
Edge Selection (CSS-like)
For properties that apply to multiple edges (like border or margin), mevy follows CSS conventions:
border: 5px; // all 4 edges
border: 5px 2px; // vertical, horizontal
border: 5px 2px 8px; // top, horizontal, bottom
border: 5px 2px 4px 1px; // top, right, bottom, left
The number of values determines the spread: 1 value = all edges, 2 = vertical/horizontal, 3 = top/horizontal/bottom, 4 = clockwise (top, right, bottom, left).
Corner Selection (CSS-like)
For border_radius, mevy uses clockwise corner order:
border_radius: 5px; // all 4 corners
border_radius: 5px 0px; // top-left/right, bottom-left/right
border_radius: 5px 2px 8px; // top-left, top-right, bottom
border_radius: 5px 2px 4px 1px; // clockwise: TL, TR, BR, BL
Custom Fields
You can use functions returning impl Bundle as custom fields — like CSS classes:
fn neon_border(color: Color) -> Outline {
Outline {
width: Val::Px(3.0),
offset: Vec2::splat(1.0),
color,
}
}
cmd.spawn(ui!((
neat_outline: #00ff00; // calls neat_outline(#00ff00)
)));
Or call a struct’s new method:
cmd.spawn(ui!((
Outline: 3px, 2px, #ff0000; // calls Outline::new(3px, 2px, #ff0000)
)));
Note
Variables (via
$var) can only be used as values inside custom fields. Built-in field aliases (likebg,w,px) cannot reference variables directly. This is a known limitation.
Complete Field Reference
See CSS-like Fields for the complete list of all supported fields.
Slim Mode Aliases
See CSS-like Fields — each field section lists its slim alias in the second column.
code!{} — Code Replacement Macro
The code!{} macro replaces tedious patterns inside regular Rust code. It works anywhere you’d write a regular expression — in function arguments, struct fields, let bindings, etc. Unlike ui!{}, it doesn’t return components; it returns the actual values you need.
Why This Exists
In Bevy, you constantly write the same patterns:
// Before: verbose
let color = Srgba::hex("#FF1265").unwrap().into();
let width = Val::Px(100.0);
let rect = UiRect { top: Val::Px(10.0), right: Val::Px(5.0), bottom: Val::Px(10.0), left: Val::Px(5.0) };
// After: concise
let color = code!{#FF1265};
let width = code!{100px};
let rect = code!{[>10px 5px]};
The macro is purely syntactic sugar — it expands to regular Rust at compile time. Your IDE autocomplete, type inference, and error messages all work normally.
Hex Colors
Write #hexcode and it becomes a Color:
let color = code!{#FF0000};
// → Color::Srgba(Srgba::hex("#FF0000").unwrap())
let color = code!{#f00};
// → 3-digit shorthand: #ff0000
let color = code!{#ff000080};
// → 8-digit: includes alpha
Supported formats: 3-digit (#rgb), 4-digit (#rgba), 5-digit (#rgb+alpha), 6-digit (#rrggbb), 8-digit (#rrggbbaa). The compiler validates hex strings at compile time — invalid codes produce a clear error.
Chaining Methods
Since the result is a Color, you can chain methods:
code!{BoxShadow{
color: #FF1265.mix(&#F93ECA, 0.4).with_alpha(0.2),
}}
This is particularly useful for gradients, transparency adjustments, and color manipulation.
Values (Val)
Write numbers with units and they become Val:
| Syntax | Result |
|---|---|
100px | Val::Px(100.) |
50% | Val::Percent(50.) |
3vw | Val::Vw(3.) |
1vh | Val::Vh(1.) |
2vmin | Val::VMin(2.) |
4vmax | Val::VMax(4.) |
@ | Val::Auto |
let width = code!{100px};
let height = code!{50%};
let auto = code!{@};
Note
@insidecode!{}meansVal::Auto. This is different fromentity!{}where@selects from resources. Don’t use@incode!{}if you might confuse it with theentity!{}resource selector.
The @ symbol maps to Val::Auto, which is commonly used for width and height in flex layouts.
UiRect
Write [>val1 val2 ...] for CSS-like edge notation:
// Single value → all edges
code!{[>10px]}
// → UiRect { top: 10px, right: 10px, bottom: 10px, left: 10px }
// Two values → vertical, horizontal
code!{[>10px 5px]}
// → top/bottom: 10px, left/right: 5px
// Three values → top, horizontal, bottom
code!{[>10px 5px 8px]}
// Four values → top, right, bottom, left
code!{[>10px 5px 8px 3px]}
The same edge-selection conventions as CSS apply: 1 value = all, 2 = vertical/horizontal, 3 = top/horizontal/bottom, 4 = clockwise.
Default Shorthand
Use ! to insert Default::default():
code!{Node{
width: 100px,
height: 100px,
! // ..Default::default()
}}
!at end of token (no following;) →::default()!followed by;→::default()!inside a struct →..Default::default()
Multiple Expressions
Write multiple statements inside the macro:
code!{
let color1 = #FF0000;
let color2 = #00FF00;
let color3 = #0000FF;
}
This is useful for defining several values at once without repeating the macro syntax.
Complete Example
use bevy::prelude::*;
use mevy::*;
pub fn main() {
println!("{:#?}", code!{BoxShadow(vec![ShadowStyle{
color: #FF1265.mix(&#F93ECA, 0.4).with_alpha(0.2),
x_offset: 100px,
y_offset: 50%,
spread_radius: 3.1vh,
blur_radius: 40.23vmax,
}])});
}
See Also
- Macros Overview — All macro families
- Quick Start — First app with all macros
Experimental Helpers
Warning
Experimental features are unstable. The API may change between versions without notice. Do not rely on them in production code.
Enable the Feature
mevy = { version = "0.3", features = ["0.18", "experimental"] }
These helpers are useful for prototyping and daily development. They solve common patterns with minimal syntax, but since they’re experimental, their behavior may change.
Alternative Entity Macros
Shorthand versions of entity!{} for specific world types. These are always available (no feature flag needed):
cen![] — Commands Entity
cen![..] // spawn a `me: Entity`
cen![&..] // edit a `me: Entity`
cen![*..] // edit a `world: EntityCommands`
cen![#Marker|..] // entity target becomes 'Marker' (named entity, not component query)
den![] — DeferredWorld Entity
den![..] // spawn a `me: Entity`
den![&..] // edit a `me: Entity`
den![*..] // edit a `world: EntityCommands`
den![#Marker|..] // entity target becomes 'Marker' (named entity, not component query)
wen![] — World Entity
wen![..] // spawn a `me: Entity`
wen![&..] // edit a `me: Entity`
wen![*..] // edit a `world: EntityWorldMut`
wen![#Marker|..] // entity target becomes 'Marker' (named entity, not component query)
The prefix indicates the world type: c = Commands, d = DeferredWorld, w = World. The & prefix before the content selects a specific entity to modify.
Warning
The
#prefix incen![#Marker|..]is consumed by the macro and does not trigger a component query. The entity target becomes the named entityMarker, not a component selector.
modify!{} — Entity Modification Shortcut
modify!{...} is a shorthand for entity!{<|> ...} — it always targets a specific entity via me: Entity:
modify!{
Transform!;
BackgroundColor(#ff0000);
}
Use this when you have an Entity variable named me and want to modify it quickly.
Get Resource — gere![]
Note
Requires the
experimentalfeature flag.
// Get (immutable)
let time = gere![Time].unwrap();
// Get (mutable)
let mut time = gere![mut Time].unwrap();
// With auto .unwrap()
let time = gere![Time!];
The mut keyword before the type requests a mutable reference. The ! suffix (after the type) adds .unwrap() automatically.
Edit Resource — edre![]
Note
Requires the
experimentalfeature flag.
Quickly edit a resource field:
edre![Time.delta = 0.016];
This is equivalent to:
if let Some(mut data) = world.get_resource_mut::<Time>() {
data.delta = 0.016;
}
Get Component — geco![]
Note
Requires the
experimentalfeature flag.
// Get (immutable)
let val = geco![MyComponent].unwrap();
// Get (mutable)
let mut val = geco![mut MyComponent].unwrap();
// Get (cloned)
let val = geco![MyComponent*].unwrap();
// Check existence
if geco![MyComponent?] {
// component exists
}
// With auto .unwrap()
let val = geco![MyComponent!];
The suffixes modify behavior:
mutbefore the type → mutable reference*after the type → cloned value?after the type → returnsbool(true if component exists)!after the type → adds.unwrap()automatically
Edit Component — edco![]
Note
Requires the
experimentalfeature flag.
Quickly edit a component field:
edco![MyComponent.field = 100];
This is equivalent to:
if let Some(mut data) = world.get_mut::<MyComponent>(me) {
data.field = 100;
}
Deref Component
Prefix with * to dereference:
edco![*MyComponent.field = 100];
This dereferences the component before accessing the field, useful for Option<T> or Box<T> components.
API Reference
Complete reference documentation for mevy’s macro features. This page organizes the detailed references by topic.
Overview
| Reference | Description |
|---|---|
| CSS-like Fields & Slim Aliases | All ui!{} fields with their slim shortcuts — the complete field reference |
| Entity Selectors | entity!{} selector syntax — how to target entities and resources |
| Grid Track Syntax | Grid track sizing functions — CSS Grid syntax for Bevy |
Quick Navigation
ui!{} Fields
The ui!{} macro supports 80+ fields organized into categories:
- Position:
position_type,position,absolute,relative - Visibility:
hidden,visible,inherit - Transform:
scale,rotation - Size:
width,height,size,min_width,min_height,max_width,max_height,aspect_ratio,flex_basis - Margin & Padding:
margin,margin_x,margin_y,margin_left,margin_right,margin_top,margin_bottom,padding,padding_x,padding_y,padding_left,padding_right,padding_top,padding_bottom - Box Styling:
background_color,border,border_radius,box_shadow,outline - Text:
font_color,font_size,text,justify_text,line_break,line_height,text_shadow - Images:
image,image_color - Flex:
flex_direction,flex_grow,flex_shrink,flex_wrap,flex_basis - Grid:
grid_auto_rows,grid_auto_columns,grid_template_rows,grid_template_columns,grid_auto_flow,grid_row,grid_column - Other:
display,justify_items,align_items,justify_content,align_content,justify_self,align_self,overflow,overflow_clip_margin,z_index,z_global,focus_policy,scroll_position,relative_cursor_position,interaction
See CSS-like Fields for the complete reference with slim aliases.
entity!{} Selectors
The selector syntax determines where to operate and what to target:
- World Types:
Commands,World,DeferredWorld,EntityCommands,ChildBuilder,EntityWorldMut - Component Queries:
<|#*Comp.all()>,<|!#Marker>,<|!#Comp.0> - Resource Queries:
<|@Resource.get()>,<|!@Resource.0>,<|@*Resource.all()> - Redirection: Chain
<Children.iter()>to drill down through entities - Leaking:
>(leak entities),<(return root),@(capture as closure)
See Entity Selectors for the complete selector reference.
Grid Tracks
Grid tracks define the columns and rows of a grid layout:
- Basic:
1px,3%,10fr,10! - Named:
auto,min_content,max_content,min: 100px,max: 200px - Minmax:
minmax(100px 200px) - Repeated:
10: 1px,auto_fill: 1px,auto_fit: 200px - Fit Content:
10fit_px,10fit%
See Grid Track Syntax for the complete reference.
Entity Selectors
Complete reference for entity!{} world and entity selectors.
How Selectors Work
Every entity!{} call starts with a selector in <...>. The selector has two parts:
- World type — which Bevy API to use (
Commands,World,DeferredWorld, etc.) - Entity target — which entity to operate on (new, specific, or queried)
The syntax is: <world_type|entity_target>. The pipe | separates the two parts. If either part is omitted, sensible defaults apply.
World Type Selectors
Spawn Selectors
| Selector | World Type | Entity |
|---|---|---|
| (none) | Commands | new (spawned) |
world | Commands (named) | new |
commands | Commands (named) | new |
+world | World | new |
-world | DeferredWorld | new |
*this | EntityCommands | new |
^cbuild | ChildBuilder | new |
+*this | EntityWorldMut | new |
The prefix character indicates the world type: + = World, - = DeferredWorld, * = EntityCommands, ^ = ChildBuilder. No prefix = Commands.
Modify Selectors
| Selector | World Type | Entity |
|---|---|---|
commands|entity | Commands | specific Entity |
|entity | Commands | specific Entity |
commands| | Commands | me: Entity |
| | Commands | me: Entity |
+world|entity | World | specific Entity |
-world|entity | DeferredWorld | specific Entity |
*this|entity | EntityCommands | specific Entity |
^cbuild|entity | ChildBuilder | specific Entity |
+*this|entity | EntityWorldMut | specific Entity |
When modifying, the part before | is the world type and the part after is the entity. If either is empty, a default is used: empty before | = Commands, empty after | = me: Entity.
Component Query Selectors
For targeting entities by component:
| Selector | Behavior |
|---|---|
!#Marker | Single entity (panics if none/multiple) |
!#Marker.0 | Entity field on component (panics if None) |
#*Comp.all() | Iterator over entities from component |
The # prefix means “find entities by component”. The ! prefix (placed before #) means “single entity” (panics if none/multiple). The * suffix (placed after #) means “iterator over entities”.
Warning
Plain
#Marker(without!or*) is not supported. Use!#Markerfor a single entity or#*Comp.all()for an iterator.
Resource Query Selectors
For targeting entities stored in resources:
| Selector | Behavior |
|---|---|
@Resource.get() | Safe: does nothing if resource or value is None |
@*Resource.all() | Iterate over entity iterator from resource |
!@Resource.0 | Risky: panics if resource or value is None |
The @ prefix means “find entities from a resource”. Safe selectors (get()) silently skip if the resource or value is missing. Risky selectors (!) panic.
Warning
#Marker.get()is not supported as a component query. It will be treated as a named entity, not a component selector.
Redirection
After the initial selector, chain redirections to drill down:
entity!{
<world|#*Marker.all()> // initial: every Entity with Marker
<Children.get(0).cloned()!> // redirect: first child
<Children.iter()> // redirect: all children
.despawn(); // apply to all
}
Each <...> in the chain redirects the target. The final operations apply to whatever the last redirection selected.
Leaking Symbols
At the end of the macro:
| Symbol | Behavior |
|---|---|
> | Leak entities into scope |
< | Return root entity |
@ | Capture as closure |
Warning
Resource/component selectors (
@,#) only leak child entities, not the root.
CSS-like Fields & Slim Aliases
Complete list of all fields supported by ui!{}, with their slim aliases and value patterns.
How to Use This Reference
This page documents every field you can use inside ui!{}. Each field maps to one or more Bevy components. Use full field names in CSS-like mode (ui!((...))) and slim aliases in slim mode (ui!(...)).
The Values column describes what you can write after the field name:
Val— any numeric value with units (px, %, vh, vw, vmin, vmax)#hex— any hex color (#f00,#ff0000,#ff000080)edge selection— CSS-like multi-value notation (see Edge Selection)corner selection— CSS-like corner notation (see Corner Selection)track list— CSS Grid track syntax (see Grid Track Syntax)
Position
| Field | Slim Alias | Values |
|---|---|---|
position_type | (none) | absolute, relative |
position | (none) | absolute, relative |
absolute | absolute | (shortcut — no value needed) |
relative | relative | (shortcut — no value needed) |
Visibility
| Field | Slim Alias | Values |
|---|---|---|
hidden | (none) | (shortcut — no value needed) |
visible | (none) | (shortcut — no value needed) |
inherit | (none) | (shortcut — no value needed) |
Transform
Warning
Transform affects UI layout differently than you might expect. Scale and rotation are applied after layout, not before. This means a rotated element won’t affect its siblings’ layout.
| Field | Slim Alias | Values |
|---|---|---|
scale | (none) | x or x y |
rotation | (none) | number (radians) or numberdeg |
Positions
| Field | Slim Alias | Values |
|---|---|---|
left | l | Val (px, %, vh, vw, vmin, vmax) |
right | r | Val |
top | t | Val |
bottom | b | Val |
x | (none) | Val (sets left + right) |
y | (none) | Val (sets top + bottom) |
xy | (none) | Val (sets left, right, top, bottom) |
z_index | z, zindex | number |
z_global | zg | number |
Size
| Field | Slim Alias | Values |
|---|---|---|
width | w | Val |
height | h | Val |
size | (none) | width height |
min_width | min_w | Val |
min_height | min_h | Val |
max_width | max_w | Val |
max_height | max_h | Val |
aspect_ratio | (none) | f32 |
flex_basis | (none) | Val |
Margin
| Field | Slim Alias | Values |
|---|---|---|
margin | m | 1 value (all edges) or edge selection |
margin_top | mt | Val |
margin_bottom | mb | Val |
margin_left | ml | Val |
margin_right | mr | Val |
margin_x | mx | Val (sets left + right) |
margin_y | my | Val (sets top + bottom) |
Padding
| Field | Slim Alias | Values |
|---|---|---|
padding | p | 1 value (all edges) or edge selection |
padding_top | pt | Val |
padding_bottom | pb | Val |
padding_left | pl | Val |
padding_right | pr | Val |
padding_x | px | Val (sets left + right) |
padding_y | py | Val (sets top + bottom) |
Box Styling
| Field | Slim Alias | Values |
|---|---|---|
background_color | background, bg | #hex, #rgba, or CSS color name |
box_shadow | shadow | x y blur spread color or 1-4 vals + color |
border | border | edge selection (see below) |
border_color | (none) | #hex or CSS color name |
outline | (none) | width offset color |
border_radius | round, rounded | corner selection (see below) |
Border Edge Selection
border: 5px; // all 4 edges
border: 5px 2px; // vertical, horizontal
border: 5px 2px 8px; // top, horizontal, bottom
border: 5px 2px 4px 1px; // top, right, bottom, left
Border Radius Corner Selection
border_radius: 5px; // all 4 corners
border_radius: 5px 0px; // top-left/right, bottom-left/right
border_radius: 5px 2px 8px; // top-left, top-right, bottom
border_radius: 5px 2px 4px 1px; // clockwise: TL, TR, BR, BL
Text Styling
| Field | Slim Alias | Values |
|---|---|---|
font_color | color | #hex, #rgba, or CSS color name |
font_size | text_size | f32 |
text | (none) | f32 (font size), or enum variants: Left, Center, Right, Justified (justify), or WordBoundary, AnyCharacter, WordOrCharacter, NoWrap (line break; defaults to WordBoundary) |
justify_text | (none) | left, center, right, justified (converted to PascalCase: JustifyText::* or Justify::* depending on version; defaults to left) |
line_break | (none) | word_boundary, any_character, word_or_character, no_wrap (converted to LineBreak::*; defaults to word_boundary) |
line_height | leading | f32 (relative to font) |
text_shadow | (none) | x y color |
Images
| Field | Slim Alias | Values |
|---|---|---|
image | img | $var (variable), #hex (color), or flip_x/flip_y |
image_color | img_color | #hex or CSS color name |
Flex
| Field | Slim Alias | Values |
|---|---|---|
flex_direction | flex | row, column, row_reverse, column_reverse (converted to FlexDirection::*) |
flex_grow | (none) | f32 |
flex_shrink | (none) | f32 |
flex_wrap | (none) | no_wrap, wrap, wrap_reverse (converted to FlexWrap::*) |
Grid
| Field | Slim Alias | Values |
|---|---|---|
grid_auto_flow | (none) | row, column, row_dense, column_dense (converted to GridAutoFlow::*) |
grid_row | (none) | span n, start n, end n, or start n end n (note: 3+ value syntax has a known bug in the code) |
grid_column | (none) | span n, start n, end n, or start n end n (note: 3+ value syntax has a known bug in the code) |
grid_auto_rows | (none) | track list (e.g. 1px 3% 10fr min_content) |
grid_auto_columns | (none) | track list |
grid_template_rows | (none) | repetition:track list |
grid_template_columns | (none) | repetition:track list |
Other
| Field | Slim Alias | Values |
|---|---|---|
display | (none) | flex, grid, block, none (converted to Display::*) |
justify_items | (none) | default, start, end, center, baseline, stretch |
align_items | (none) | default, start, end, center, baseline, stretch |
justify_content | (none) | default, start, end, flex_start, flex_end, center, stretch, space_between, space_evenly, space_around |
align_content | (none) | default, start, end, flex_start, flex_end, center, stretch, space_between, space_evenly, space_around |
justify_self | (none) | auto, start, end, center, baseline, stretch |
align_self | (none) | auto, start, end, center, baseline, stretch |
row_gap | gap_y | Val |
column_gap | gap_x | Val |
gap | (none) | gap_x gap_y |
overflow | (none) | visible, clip, hidden, scroll (converted to PascalCase: OverflowAxis::*; no value = Overflow::DEFAULT; or x y separately) |
overflow_clip_margin | (none) | (no value = DEFAULT), border_box, padding_box, content_box, or border_box 5 (visual_box + optional margin) |
relative_cursor_position | cursor_pos, cursor_position | (component only — no value) |
focus_policy | focus | pass, ignore, consume (default: pass) |
scroll_position | scroll | (no value = adds component), x y (optional) |
interaction | (none) | (component only — no value) |
Grid Track Syntax
mevy supports an extended syntax for CSS Grid track sizing in ui!{}. Grid tracks define the columns and rows of a grid layout, controlling their size and behavior.
How Grid Tracks Work
CSS Grid uses tracks (rows and columns) to define the structure of a grid. Each track has a size (like 100px or 1fr) and may have a minimum and maximum size. mevy translates CSS-like track syntax into Bevy’s GridTrack types.
Basic Tracks
These are the simplest track sizing options — a single value that defines the track size:
| Syntax | Result |
|---|---|
1px | 1px fixed track |
3% | 3% percentage track |
10fr | 10 fraction track |
10! | 10px (exclamation = px) |
Fixed tracks have an exact pixel size. Percentage tracks are relative to the container. Fraction (fr) tracks divide remaining space proportionally.
Named Functions
Instead of raw values, mevy also accepts named sizing functions that describe the track’s behavior:
| Syntax | Result |
|---|---|
auto | GridTrack::Auto |
min_content | GridTrack::MinContent |
max_content | GridTrack::MaxContent |
min: 100px | GridTrack::min_content(100px) |
max: 200px | GridTrack::max_content(200px) |
auto sizes to the content. min_content sizes to the minimum needed. max_content sizes to the maximum needed. The min: and max: variants let you set a minimum or maximum size constraint.
Minmax
The minmax(min max) function creates a track with both a minimum and maximum size constraint:
minmax(100px 200px)
// -> min: 100px, max: 200px
minmax(auto 1fr)
// -> min: auto, max: 1fr
minmax(min_content max_content)
// -> min: min_content, max: max_content
minmax(min max) creates a track that can grow and shrink between the two values. The minimum is the smallest the track can be; the maximum is how large it can grow.
Repeated Tracks
Tracks can be repeated using the N: track syntax or named repetition keywords.
Fixed Repetition
Use N: track to create N identical tracks:
10: 1px
// -> 10 tracks of 1px
3: 3%
// -> 3 tracks of 3%
The N: track syntax repeats a track N times. Useful for creating evenly-sized columns or rows.
Auto Fill / Auto Fit
These keywords create tracks dynamically based on available space:
auto_fill: 1px
auto_fill: 10fr
auto_fit: 200px
auto_fit: min_content
auto_fill creates as many tracks as will fit, including empty ones. auto_fit creates tracks only if there’s content for them — empty tracks are removed. Both are useful for responsive layouts.
Fit Content
fit_content creates tracks sized to their content, with a minimum size constraint:
10fit_px -> FitContentPx(10)
10fit! -> FitContentPx(10)
10fit% -> FitContentPercent(10)
fit_content creates a track sized to its content, with a minimum size. The fit suffix is attached to a number; fit_px or fit! uses pixel values, fit% uses percentages.
Grid Placement
grid_row and grid_column use GridPlacement to position items within the grid. No value defaults to GridPlacement::DEFAULT:
Span
grid_row: span 3; // span 3 tracks starting from current position
grid_column: span 2; // span 2 tracks starting from current position
Start Only
grid_row: start 1; // start at track 1, span 1 track
grid_column: start 2; // start at track 2, span 1 track
Start + End
grid_row: start 1 end 3; // start at track 1, end at track 3
grid_column: start 2 end 5; // start at track 2, end at track 5
End Only
grid_row: end 3; // end at track 3, span 1 track
grid_column: end 5; // end at track 5, span 1 track
Usage Examples
Here are practical examples of each track type in context:
Auto Rows
Use grid_auto_rows to define how rows are created when items don’t specify a row:
ui!((
grid_auto_rows: 1px 3% 10fr min_content;
));
Auto Columns
Similar to auto rows, but for columns:
ui!((
grid_auto_columns: auto_fill: 1px;
));
Template Rows
Define a fixed grid structure with grid_template_rows. The N: prefix repeats a track N times:
ui!((
grid_template_rows: 10: 1px 3: 3%;
));
Grid Placement
Use grid_row and grid_column to control which tracks an item spans:
// Span
grid_row: span 3;
grid_column: span 2;
// Start/End
grid_row: start 1 end 3;
grid_column: start 2 end 5;
Guides
Practical guides for common mevy patterns. Each guide builds on concepts introduced earlier, so start with the Quick Start if you’re new.
- Building a UI — Compose complex layouts with
ui!{}andentity!{}, from panels to grids to reusable components - Entity Queries — Query and modify entities with
entity!{}, from basic component queries to chained redirections - Custom Fields — Create reusable UI components with custom fields, the mevy equivalent of CSS classes
- Bevy Version Compatibility — Understand how mevy handles Bevy API differences across versions
- Migration Guide — Step-by-step migration between Bevy versions
- FAQ — Common questions and answers
Building a UI
This guide shows how to compose complex UI layouts with ui!{} and entity!{}. Each example builds on the previous one, showing how the macros work together to create real UI patterns.
Tip
All examples use the dark color palette from the mevy branding. Feel free to adapt the colors to your project.
Basic Panel
A panel is a styled container with a title, content area, and action buttons. This example shows the core patterns:
ui!{}creates styled containers (returns a bundle of UI components)[name][...]creates named children (referenced by name anywhere)justify_contentandalign_itemscenter content within the container
fn spawn_panel(mut cmd: Commands) {
entity!{
<cmd>
// Outer panel
ui!((
size: 300px 200px;
background: #1a1a2e;
border: 2px #ee6677;
border_radius: 12px;
padding: 16px;
gap: 8px;
flex_direction: column;
));
// Title
[title][
ui!((
size: 100% auto;
justify_content: center;
));
Text::new("My Panel");
]
// Content area
[content][
ui!((
size: 100% 1fr;
background: #16213e;
border_radius: 8px;
justify_content: center;
align_items: center;
));
Text::new("Content goes here");
]
// Footer with buttons
[footer][
ui!((
size: 100% auto;
justify_content: center;
gap: 8px;
));
[ok_button][
ui!((
size: 80px 32px;
background: #ee6677;
border_radius: 6px;
justify_content: center;
align_items: center;
));
Text::new("OK");
> Pointer<Click> {
this.insert(BackgroundColor(#ff8899));
};
]
[cancel_button][
ui!((
size: 80px 32px;
background: #444;
border_radius: 6px;
justify_content: center;
align_items: center;
));
Text::new("Cancel");
]
]
}
}
Grid Layout
Grid layouts use display: grid and track definitions to create complex multi-column layouts. Key concepts:
grid_template_columns/rowsdefine the grid structuregrid_column: span Nandgrid_row: span Nmake items span multiple tracks1frdivides remaining space proportionally
fn spawn_grid(mut cmd: Commands) {
entity!{
<cmd>
ui!((
size: 100% 100%;
display: grid;
grid_template_columns: 1fr 2fr 1fr;
grid_template_rows: auto 1fr auto;
gap: 8px;
padding: 16px;
));
[header][
ui!((
grid_column: span 3;
size: 100% 40px;
background: #ee6677;
border_radius: 8px;
justify_content: center;
align_items: center;
));
Text::new("Header");
]
[sidebar][
ui!((
grid_row: span 2;
size: 100% 100%;
background: #16213e;
border_radius: 8px;
));
Text::new("Sidebar");
]
[main][
ui!((
grid_column: span 2;
grid_row: span 2;
size: 100% 100%;
background: #1a1a2e;
border_radius: 8px;
));
Text::new("Main Content");
]
[footer][
ui!((
grid_column: span 3;
size: 100% 30px;
background: #333;
border_radius: 8px;
justify_content: center;
align_items: center;
));
Text::new("Footer");
]
}
}
Key patterns:
grid_template_columns/rowsdefines the grid structuregrid_column: span Nandgrid_row: span Nmake items span multiple tracks1frdivides remaining space proportionally
Reusable Components
Define reusable components with ui!{} function mode. This is like CSS classes or React components — define a styled element once, use it everywhere:
// Define a button prefab
ui!{
mevy_button(
size: 120px 40px;
background: #ee6677;
border_radius: 6px;
justify_content: center;
align_items: center;
)?
}
// Define a card prefab
ui!{
mevy_card(
size: 200px 150px;
background: #1a1a2e;
border: 2px #333;
border_radius: 12px;
padding: 12px;
gap: 8px;
flex_direction: column;
)?
}
// Use them
fn spawn_with_prefabs(mut cmd: Commands) {
entity!{
[card][
mevy_card();
Text::new("Card Title");
[btn][
mevy_button();
Text::new("Click");
]
]
}
}
The ? at the end is optional — any token works. It’s just a visual cue that the macro returns a value.
Hover Effects
Attach observers directly on entities to create interactive UI. The > Pointer<Click> syntax is shorthand for attaching an observer — no separate registration needed:
entity!{
[hoverable][
ui!((
size: 100px 100px;
background: #ee6677;
border_radius: 8px;
));
> Pointer<Over> {
this.insert(ui!((
background: #ff8899;
box_shadow: 0px 0px 10px #ee6677;
)));
};
> Pointer<Out> {
this.insert(ui!((
background: #ee6677;
box_shadow: 0px 0px 0px #000;
)));
};
]
}
Key patterns:
> Pointer<Over>fires when the pointer enters the entity> Pointer<Out>fires when the pointer leavesthis.insert(ui!(...))replaces the entity’s UI components
See Also
- ui!{} Documentation — All fields and modes
- Custom Fields Guide — Create reusable components
- API Reference — Complete field reference
Entity Queries
This guide shows how to query and modify entities with entity!{}. Entity queries let you target and modify multiple entities at once, without writing separate query systems.
The Mental Model
Entity queries work by selecting entities and then applying operations to them:
- Select — the first
<...>selector identifies the starting point (world type + entity target) - Redirect — additional
<...>selectors drill down through children or components - Apply — operations after the selectors target whatever the last selector selected
Think of it like a pipeline: data flows through each selector, and the final operations apply to whatever emerges.
Basic Query
Target all entities with a component:
fn cleanup(mut cmd: Commands) {
entity!{
<world|#*Dead.all()>
.despawn();
}
}
The <world|#*Dead.all()> selector targets every entity with the Dead component. The .despawn() call removes them all.
Note
Use
#*Comp.all()for an iterator over all entities with a component. Use!#Compfor a single entity (panics if none/multiple). Plain#Compis not supported.
Chained Queries
Chain multiple selectors to drill down through entities:
fn update_children(mut cmd: Commands) {
entity!{
<world|#*Parent.all()> // 1. All entities with Parent
<Children.iter()> // 2. Their children
<Children.iter()> // 3. Grandchildren
BackgroundColor(#ff0000); // 4. Color them red
}
}
Each <...> in the chain redirects the target. The final BackgroundColor(#ff0000) applies to all entities selected by the last redirection.
Conditional Query
Target a specific entity from a component:
fn update_target(mut cmd: Commands) {
entity!{
<world|!#Player> // Single entity from Player component
Transform {
translation: Vec3::ZERO,
!
};
}
}
!#Player returns a single entity (panics if none/multiple). The ! in the struct inserts ..Default::default().
Resource-based Query
Target entities stored in a resource:
fn update_from_resource(mut cmd: Commands) {
entity!{
<world|@GameEntities.get()> // Safe: does nothing if resource or value is None
[children][
Visibility::Visible;
]
}
}
fn update_from_resource_risky(mut cmd: Commands) {
entity!{
<world|!@GameEntities.0> // Risky: panics if resource or value is None
[children][
Visibility::Visible;
]
}
}
The @ prefix means “get entities from a resource”. Safe selectors (@Resource.get()) silently skip if the resource or value is missing. Risky selectors (!@Resource.0) panic.
Modify with Specific Entity
fn modify_specific(mut cmd: Commands, player: Entity) {
entity!{
<world|player>
Transform {
translation: Vec3::new(10.0, 0.0, 0.0),
!
};
[child][
Name("Child of player");
]
}
}
The |player part selects a specific entity to modify. Everything after that operates on that entity.
World-based Queries
Use World directly for synchronous queries:
fn update_with_world(mut world: World) {
entity!{
<+world|>
<|#*OldComponent.all()>
<Children.iter()>
BackgroundColor(#00ff00);
}
}
The <+world|> selector uses World directly instead of Commands. This is useful when you need to query the world synchronously.
Deferring to Commands
When using World, results are queued to Commands:
fn update_deferred(mut world: World) {
entity!{
<+world|>
<|#*Target.all()>
.despawn();
// This is queued to Commands automatically
}
}
Operations on World are deferred to Commands automatically, so you don’t need to worry about borrow checker conflicts.
! Default Shorthand
Use ! to insert Default::default():
entity!{
<world|#*Marker.all()>
Transform!; // = Transform::default()
Node{ padding:10px, ! }; // = Node{ padding:10px, ..default() }
BackgroundColor(#ff0000);
}
The ! at the end of a type calls ::default(). Inside a struct, ! inserts ..Default::default().
try Conditional Insertion
entity!{
<world|#*Target.all()>
try SomeBundle; // Only inserts if SomeBundle is Some
}
try conditionally inserts a bundle if it’s Some. Useful for optional components that may or may not be present.
Free Code Block
Use {..} for custom logic with this: EntityCommands:
entity!{
<world|#*Target.all()>
{
this.insert(Component);
this.observe(on_click);
}
}
The {..} block gives you direct access to this: EntityCommands for arbitrary operations that don’t fit the shorthand syntax.
Safe vs Risky Selectors
| Selector | Behavior on None |
|---|---|
<|@Resource.get()> | Safe: does nothing |
<|!@Resource.0> | Risky: panics |
<|#*Comp.all()> | Always works (may be empty) |
<|!#Marker> | Risky: panics if none/multiple |
Use safe selectors when the entity might not exist (error handling). Use risky selectors when you’re certain the entity exists (performance and clarity).
See Also
- Entity Selectors API — Full selector reference
- entity!{} Documentation — Complete macro reference
- Migration Guide — Version-specific query behavior
Custom Fields
Custom fields let you define reusable UI components in ui!{}. They’re the mevy equivalent of CSS classes or React components — define a styled element once, use it everywhere.
How Custom Fields Work
A custom field is any function that returns impl Bundle. When you write my_field: value; inside ui!{}, mevy resolves the field name:
- Lowercase field name → calls
my_field(value)(function call) - Capitalized field name → calls
MyField::new(value)(structnew()method)
This means your custom field function name determines how it’s called.
Function-based Custom Fields
Any function returning impl Bundle can be used as a field:
fn neon_border(color: Color) -> Outline {
Outline {
width: Val::Px(3.0),
offset: Vec2::splat(2.0),
color,
}
}
fn glow_box(color: Color) -> BoxShadow {
BoxShadow(vec![ShadowStyle {
color,
x_offset: Val::Px(0.0),
y_offset: Val::Px(0.0),
blur_radius: Val::Px(20.0),
spread_radius: Val::Px(5.0),
}])
}
fn spawn_glowy_card(mut cmd: Commands) {
cmd.spawn(ui!((
size: 200px 150px;
background: #1a1a2e;
neon_border: #00ff00; // calls neon_border(#00ff00)
glow_box: #00ff00; // calls glow_box(#00ff00)
justify_content: center;
align_items: center;
)));
}
The function name becomes the field name. The value after : is passed as the argument.
Struct new() Method
You can also target a struct’s new method:
// Outline::new(width, offset, color)
cmd.spawn(ui!((
Outline: 3px, 2px, #ff0000;
)));
// Any struct with `pub fn new(...)` works:
cmd.spawn(ui!((
MyCustomBundle: arg1, arg2, #ff0000;
)));
The field name (capitalized) is treated as a type name, and : followed by values calls its new() method. This is useful for Bevy’s built-in bundles like Outline, BoxShadow, etc.
Edit Function Mode
For mutating existing components, use the edit function mode:
ui!{
// This defines a function that takes &mut references
into_glow{
box_shadow: _ _ 10px 5px #ff0000; // _ keeps existing values
background: #ff0000;
}
}
// Usage on existing entities:
entity!{
<world|#*HoverTarget.all()>
into_glow(); // applies the edits
}
The edit function mode (ui!{name{...}}) defines a function that takes &mut references to the components it modifies. The _ placeholder keeps the existing value for that field.
Prefab Pattern
Combine function mode with named components for full prefab support:
// Define prefabs
ui!{
mevy_button(
size: 120px 40px;
background: #ee6677;
border_radius: 6px;
justify_content: center;
align_items: center;
gap: 8px;
)?
}
ui!{
mevy_card(
size: 200px 150px;
background: #1a1a2e;
border: 2px #333;
border_radius: 12px;
padding: 12px;
gap: 8px;
flex_direction: column;
)?
}
ui!{
mevy_input(
size: 100% 40px;
background: #16213e;
border: 1px #444;
border_radius: 6px;
padding_x: 12px;
)?
}
// Use them
fn spawn_form(mut cmd: Commands) {
entity!{
[card][
mevy_card();
Text::new("Login");
[input][
mevy_input();
Text::new("Username");
]
[btn][
mevy_button();
Text::new("Submit");
]
]
}
}
This is the prefab pattern: define a styled element once, call it like a function anywhere. It’s the mevy equivalent of CSS classes or UI component libraries.
Custom Field with Variables
Use $var to pass variables directly (single identifier only):
let my_image = handle;
cmd.spawn(ui!((
image: $my_image; // passes the variable directly
image_color: #ff0000; // hex color
)));
The $ prefix tells mevy to pass the value as-is, without parsing it as a hex color or CSS name.
Note
Only single identifiers work with
$—$path.to.imageis not supported. Use the full path directly:image: path.to.image;.
Limitations
Note
Variables can only be used in custom fields. Built-in field aliases (like
bg,w,px) cannot reference variables directly. This is a known limitation that may be addressed in future versions.
See Also
- ui!{} Documentation — All modes including edit function mode
- CSS-like Fields — Complete field reference
- Building a UI — UI composition patterns
Bevy Version Compatibility
mevy supports Bevy 0.15 through 0.18 via Cargo features. All API differences are handled automatically — you don’t need to change your code when switching versions.
Why Version Features?
Bevy changes its APIs between minor versions. For example, BoxShadow changed from a direct struct to Vec<ShadowStyle> in 0.16, and BorderColor changed from a single value to per-edge in 0.17. mevy handles these differences for you, but it needs to know which version you’re targeting to generate the correct code.
Required Feature
You must specify the Bevy version feature:
[dependencies]
mevy = { version = "0.3", features = ["0.18"] }
What Stays the Same
The following patterns are stable across all supported versions:
code!{}hex color and Val syntaxui!{}field names and edge/corner selectionentity!{}selector syntax- All shorthand aliases (
w,h,bg,px, etc.)
You can write the same mevy code for Bevy 0.15 and 0.18 — just change the version feature.
Version Compatibility Matrix
| Feature | mevy_ui | mevy_core | mevy_ecs |
|---|---|---|---|
0.15 | ✅ | ✅ | ✅ |
0.16 | ✅ | ✅ | ✅ |
0.16-rc | ✅ | ✅ | ✅ |
0.17 | ✅ | ✅ | ✅ |
0.18 | ✅ | ✅ | ✅ |
Version Differences
These are the API changes mevy handles automatically. You don’t need to do anything — just set the correct version feature.
box_shadow
| Version | Field Path |
|---|---|
| 0.15 | BoxShadow::blur_radius (direct field) |
| 0.16+ | BoxShadow::[0].blur_radius (array access) |
border_radius
| Version | Field Path |
|---|---|
| 0.15 | BorderRadius::top_left (direct field) |
| 0.18+ | Node::border_radius.top_left (on Node) |
scroll_position
| Version | Field Path |
|---|---|
| 0.15-0.16 | ScrollPosition::x_offset, y_offset |
| 0.17+ | ScrollPosition::x, y |
line_height
| Version | Component |
|---|---|
| 0.15-0.17 | TextFont::line_height |
| 0.18+ | Separate LineHeight component |
border_color
| Version | Structure |
|---|---|
| 0.15-0.16 | BorderColor(Srgba) (single value) |
| 0.17+ | BorderColor { top, right, bottom, left } (per-edge) |
Observer Changes
| Version | Trigger Type | Entity Access |
|---|---|---|
| 0.15 | Trigger::<T> | trigger.entity() |
| 0.16 | Trigger::<T> | trigger.target() |
| 0.17+ | On::<T> | trigger.event_target() |
ChildBuilder Changes
| Version | Methods |
|---|---|
| 0.15 | .parent_entity(), .spawn(...) |
| 0.16+ | .target_entity(), .commands_mut() |
ChildOf Relationship
| Version | Method |
|---|---|
| 0.15 | .set_parent(parent) |
| 0.16+ | .insert(ChildOf(parent)) |
See Also
- Installation — Setup and version selection
- Migration Guide — Step-by-step migration between versions
- FAQ — Common questions about version compatibility
Migration Guide
This guide helps you migrate between Bevy versions when using mevy. The key principle: mevy handles API differences automatically. You only need to change the version feature in Cargo.toml.
Bevy 0.18
Update your Cargo.toml:
mevy = { version = "0.3", features = ["0.18"] }
All entity!{} features work with Bevy 0.18.
API differences mevy handles automatically (for mevy_ui and mevy_core):
border_radiusnow writes toNode::border_radiusinstead ofBorderRadiusline_heightnow uses a separateLineHeightcomponent instead ofTextFont::line_height- Observer entity access uses
trigger.event_target()(nottrigger.target()ortrigger.entity())
Bevy 0.17
Update your Cargo.toml:
mevy = { version = "0.3", features = ["0.17"] }
No code changes needed. Key changes mevy handles automatically:
- Observer triggers changed from
Trigger<T>toOn<T> - Entity access changed from
trigger.target()(0.16) totrigger.event_target()(0.17+)
Bevy 0.16
Update your Cargo.toml:
mevy = { version = "0.3", features = ["0.16"] }
No code changes needed. Key changes mevy handles automatically:
BoxShadowchanged from a direct struct toVec<ShadowStyle>- Child relationships use
ChildOfinstead ofset_parent() - Observer entity access uses
trigger.target()(nottrigger.entity()ortrigger.event_target())
Bevy 0.15
Update your Cargo.toml:
mevy = { version = "0.3", features = ["0.15"] }
No code changes needed. Key differences in Bevy 0.15:
ChildBuilderuses.parent_entity()and.spawn()instead of.target_entity()and.commands_mut()BorderColoris a singleSrgbavalue, not per-edge- Observer entity access uses
trigger.entity()(nottrigger.target()ortrigger.event_target())
Version Support Matrix
| Feature | mevy_ui | mevy_core | mevy_ecs |
|---|---|---|---|
0.15 | ✅ | ✅ | ✅ |
0.16 | ✅ | ✅ | ✅ |
0.16-rc | ✅ | ✅ | ✅ |
0.17 | ✅ | ✅ | ✅ |
0.18 | ✅ | ✅ | ✅ |
Tips
- Always pin your Bevy version — never omit the version feature.
- Test with your target Bevy version — macro expansions differ between versions.
- Check the changelog for breaking changes between mevy versions.
FAQ
Frequently asked questions about mevy.
Do I need to enable all features?
No. Specify only the Bevy version feature (e.g., features = ["0.18"]). The ui feature is enabled by default. Only enable experimental if you need the experimental helper macros.
Can I use mevy with any Bevy version?
mevy supports Bevy 0.15 through 0.18. You must pin your Bevy version in Cargo.toml:
mevy = { version = "0.3", features = ["0.18"] }
Without a version feature, compilation will fail with a compile_error.
What happens if I forget the version feature?
You’ll get a compile_error! at the macro expansion site telling you to specify the version feature. The error message will be clear about what’s missing.
Does mevy add runtime overhead?
No. mevy is purely compile-time token manipulation. All macros expand to regular Rust code — no plugins, no resources, no systems, no runtime dependency. The generated code is identical to what you’d write by hand.
Are experimental features safe for production?
No. Experimental features are unstable and may change without notice. The core macros (entity!{}, ui!{}, code!{}) are stable and production-ready. Only use experimental features for prototyping.
Can I use the individual crates separately?
Yes:
mevy_core = { version = "0.1.1", features = ["0.18"] }
mevy_ui = { version = "0.3.2", features = ["0.18"] }
mevy_ecs = { version = "0.2.4", features = ["0.18"] }
use mevy_core::code;
use mevy_ui::ui;
use mevy_ecs::entity;
This is useful if you only need specific macros or want to avoid the experimental default feature on mevy_ecs.
Does mevy_ecs support Bevy 0.18?
Yes — all macros (entity!{}, ui!{}, code!{}) work with Bevy 0.18.
Can I use variables inside ui!{} built-in fields?
Currently, variables can only be used in custom fields (via $var). Built-in field aliases (like bg, w, px) cannot reference variables directly. This is a known limitation.
Is there LSP autocomplete support?
Yes. mevy is token-based, preserving the original token structure. LSP autocomplete works inside entity!{}, ui!{}, and code!{} — you get suggestions for Bevy types, methods, and components just like in regular Rust code.
How does mevy compare to raw Bevy UI?
Significantly less boilerplate:
// Raw Bevy
cmd.spawn((Node { width: Val::Px(100.0), height: Val::Px(100.0), ..default() },
BackgroundColor(Srgba::hex("#ff0000").unwrap().into()),
BorderRadius::all(Val::Px(6.0))));
// mevy
cmd.spawn(ui!((size: 100px 100px; background: #ff0000; border_radius: 6px;)));
When should I use ui!{} vs code!{}?
ui!{}— when you need multiple UI components for an entity (a styled panel, button, etc.)code!{}— when you need a single value (a color, aVal, aUiRect)entity!{}— when you need to spawn or modify entities (with or without UI)
They’re designed to work together: entity!{} nests ui!{}, and code!{} works inside both. See Macros Overview for more.
Changelog
All notable changes to mevy are documented here.
0.3.2 — 2026-06-20
Added
- Comprehensive documentation site with mdBook
- Grid track syntax support in
ui!{}(auto_fill,auto_fit,fit_content,minmax) 0.18feature formevy_uiandmevy_core
Fixed
- Hex color validation for 5-digit hex codes (e.g.,
#f0a→#ff00aa)
Changed
mevy_ecsdefault feature now includesexperimental(breaking for users who don’t want experimental macros)
0.3.1
Added
0.17feature support formevy_ecsOn<T>observer trigger support for Bevy 0.17trigger.event_target()for Bevy 0.17+
Fixed
scroll_positionfield path for Bevy 0.17+ (x/yinstead ofx_offset/y_offset)
0.3.0
Added
0.16feature support for all crates0.16-rcfeature support for all crateschild_buildersupport inentity!{}EntityWorldMutsupport inentity!{}ancestors[]array for nested childrentryconditional insertion inentity!{}modify!{}shorthand macrocen![],den![],wen![]alternative entity macrosgere![],edre![],geco![],edco![]experimental helpers
Changed
border_radiusnow maps toNode::border_radiusin Bevy 0.18+BoxShadowfield access updated for Bevy 0.16+ (array-based)BorderColorstructure updated for Bevy 0.17+ (per-edge)line_heightnow uses separateLineHeightcomponent in Bevy 0.18+
[0.2.x]
Added
- Initial
entity!{}macro with hierarchy spawning - Initial
ui!{}macro with CSS-like notation - Initial
code!{}macro with hex/Val/UiRect shorthand - Slim mode for
ui!{}
Migration
See Migration Guide for details on migrating between Bevy versions.