Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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!{} and entity!{} work together. Use ui!{} inside entity!{} 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"] }

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 Versionmevy Feature
0.15.xfeatures = ["0.15"]
0.16.x (stable)features = ["0.16"]
0.16.x (RC)features = ["0.16-rc"]
0.17.xfeatures = ["0.17"]
0.18.xfeatures = ["0.18"]

Optional Features

FeatureDescription
ui (default)Enables the ui!{} macro for CSS-like UI notation
experimentalEnables 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

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

MacroPurposeUse when you need…
code!{}Inline value constructionHex colors, Val, UiRect without function calls
ui!{}CSS-like UI bundlesMultiple UI components without boilerplate
entity!{}Entity spawning & modificationHierarchies, 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!{} nests ui!{} naturally, and code!{} 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 }

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)

SelectorWorld TypeEntity
(none)Commandsnew (spawned)
worldCommands (named)new
commandsCommands (named)new
+worldWorldnew
-worldDeferredWorldnew
*thisEntityCommandsnew
^cbuildChildBuildernew
+*thisEntityWorldMutnew

Modifying (targeting existing entities)

SelectorWorld TypeEntity
commands|entityCommandsspecific Entity
|entityCommandsspecific Entity
commands|Commandsme: Entity
|Commandsme: Entity
+world|entityWorldspecific Entity
-world|entityDeferredWorldspecific Entity
*this|entityEntityCommandsspecific Entity
^cbuild|entityChildBuilderspecific Entity
+*this|entityEntityWorldMutspecific Entity

Component Query Selectors

For targeting entities by component:

SelectorBehavior
!#MarkerSingle entity (panics if none/multiple)
!#Marker.0Entity field on component (panics if None)
#*Comp.all()Iterator over entities from component

Warning

Plain #Marker (without ! or *) is not supported. Use !#Marker for a single entity or #*Comp.all() for an iterator.

Resource Selectors

For targeting entities stored in a resource:

SelectorBehavior
@Resource.get()Safe: does nothing if resource or value is None
@*Resource.all()Iterate over entity iterator from resource
!@Resource.0Risky: 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:

SymbolBehavior
>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:

SyntaxExpands to
Bundle!Bundle::default()
Bundle{a:3,!}Bundle{a:3, ..Default::default()}
Bundle: 3Bundle::new(3)
bundle_fn: 3bundle_fn(3)
macro!: 3, 4macro!{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 inside code!{}. 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

try is entity!{}-only. The ! shorthand (for ::default()) is shared with code!{} but behaves slightly differently in each context.

Ancestors Array

Inside nested children, ancestors[] gives you parent entities:

[[[[[
    Component{ entity: ancestors[3] };
]]]]]
  • ancestors[0] = direct parent
  • ancestors[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

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:

SyntaxResult
100pxVal::Px(100.)
50%Val::Percent(50.)
3vw, 1vh, 2vmin, 4vmaxVal::Vw/Vh/VMin/VMax
#ff0000Color::Srgba(Srgba::hex("#ff0000").unwrap())
red, gray, cyanCSS color from bevy::color::palettes::css
$my_varPass 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 (like bg, 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:

SyntaxResult
100pxVal::Px(100.)
50%Val::Percent(50.)
3vwVal::Vw(3.)
1vhVal::Vh(1.)
2vminVal::VMin(2.)
4vmaxVal::VMax(4.)
@Val::Auto
let width = code!{100px};
let height = code!{50%};
let auto = code!{@};

Note

@ inside code!{} means Val::Auto. This is different from entity!{} where @ selects from resources. Don’t use @ in code!{} if you might confuse it with the entity!{} 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

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 in cen![#Marker|..] is consumed by the macro and does not trigger a component query. The entity target becomes the named entity Marker, 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 experimental feature 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 experimental feature 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 experimental feature 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:

  • mut before the type → mutable reference
  • * after the type → cloned value
  • ? after the type → returns bool (true if component exists)
  • ! after the type → adds .unwrap() automatically

Edit Component — edco![]

Note

Requires the experimental feature 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

ReferenceDescription
CSS-like Fields & Slim AliasesAll ui!{} fields with their slim shortcuts — the complete field reference
Entity Selectorsentity!{} selector syntax — how to target entities and resources
Grid Track SyntaxGrid 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:

  1. World type — which Bevy API to use (Commands, World, DeferredWorld, etc.)
  2. 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

SelectorWorld TypeEntity
(none)Commandsnew (spawned)
worldCommands (named)new
commandsCommands (named)new
+worldWorldnew
-worldDeferredWorldnew
*thisEntityCommandsnew
^cbuildChildBuildernew
+*thisEntityWorldMutnew

The prefix character indicates the world type: + = World, - = DeferredWorld, * = EntityCommands, ^ = ChildBuilder. No prefix = Commands.

Modify Selectors

SelectorWorld TypeEntity
commands|entityCommandsspecific Entity
|entityCommandsspecific Entity
commands|Commandsme: Entity
|Commandsme: Entity
+world|entityWorldspecific Entity
-world|entityDeferredWorldspecific Entity
*this|entityEntityCommandsspecific Entity
^cbuild|entityChildBuilderspecific Entity
+*this|entityEntityWorldMutspecific 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:

SelectorBehavior
!#MarkerSingle entity (panics if none/multiple)
!#Marker.0Entity 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 !#Marker for a single entity or #*Comp.all() for an iterator.

Resource Query Selectors

For targeting entities stored in resources:

SelectorBehavior
@Resource.get()Safe: does nothing if resource or value is None
@*Resource.all()Iterate over entity iterator from resource
!@Resource.0Risky: 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:

SymbolBehavior
>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

FieldSlim AliasValues
position_type(none)absolute, relative
position(none)absolute, relative
absoluteabsolute(shortcut — no value needed)
relativerelative(shortcut — no value needed)

Visibility

FieldSlim AliasValues
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.

FieldSlim AliasValues
scale(none)x or x y
rotation(none)number (radians) or numberdeg

Positions

FieldSlim AliasValues
leftlVal (px, %, vh, vw, vmin, vmax)
rightrVal
toptVal
bottombVal
x(none)Val (sets left + right)
y(none)Val (sets top + bottom)
xy(none)Val (sets left, right, top, bottom)
z_indexz, zindexnumber
z_globalzgnumber

Size

FieldSlim AliasValues
widthwVal
heighthVal
size(none)width height
min_widthmin_wVal
min_heightmin_hVal
max_widthmax_wVal
max_heightmax_hVal
aspect_ratio(none)f32
flex_basis(none)Val

Margin

FieldSlim AliasValues
marginm1 value (all edges) or edge selection
margin_topmtVal
margin_bottommbVal
margin_leftmlVal
margin_rightmrVal
margin_xmxVal (sets left + right)
margin_ymyVal (sets top + bottom)

Padding

FieldSlim AliasValues
paddingp1 value (all edges) or edge selection
padding_topptVal
padding_bottompbVal
padding_leftplVal
padding_rightprVal
padding_xpxVal (sets left + right)
padding_ypyVal (sets top + bottom)

Box Styling

FieldSlim AliasValues
background_colorbackground, bg#hex, #rgba, or CSS color name
box_shadowshadowx y blur spread color or 1-4 vals + color
borderborderedge selection (see below)
border_color(none)#hex or CSS color name
outline(none)width offset color
border_radiusround, roundedcorner 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

FieldSlim AliasValues
font_colorcolor#hex, #rgba, or CSS color name
font_sizetext_sizef32
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_heightleadingf32 (relative to font)
text_shadow(none)x y color

Images

FieldSlim AliasValues
imageimg$var (variable), #hex (color), or flip_x/flip_y
image_colorimg_color#hex or CSS color name

Flex

FieldSlim AliasValues
flex_directionflexrow, 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

FieldSlim AliasValues
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

FieldSlim AliasValues
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_gapgap_yVal
column_gapgap_xVal
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_positioncursor_pos, cursor_position(component only — no value)
focus_policyfocuspass, ignore, consume (default: pass)
scroll_positionscroll(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:

SyntaxResult
1px1px fixed track
3%3% percentage track
10fr10 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:

SyntaxResult
autoGridTrack::Auto
min_contentGridTrack::MinContent
max_contentGridTrack::MaxContent
min: 100pxGridTrack::min_content(100px)
max: 200pxGridTrack::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!{} and entity!{}, 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_content and align_items center 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/rows define the grid structure
  • grid_column: span N and grid_row: span N make items span multiple tracks
  • 1fr divides 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/rows defines the grid structure
  • grid_column: span N and grid_row: span N make items span multiple tracks
  • 1fr divides 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 leaves
  • this.insert(ui!(...)) replaces the entity’s UI components

See Also

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:

  1. Select — the first <...> selector identifies the starting point (world type + entity target)
  2. Redirect — additional <...> selectors drill down through children or components
  3. 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 !#Comp for a single entity (panics if none/multiple). Plain #Comp is 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

SelectorBehavior 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

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) (struct new() 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.image is 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

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 syntax
  • ui!{} field names and edge/corner selection
  • entity!{} 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

Featuremevy_uimevy_coremevy_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

VersionField Path
0.15BoxShadow::blur_radius (direct field)
0.16+BoxShadow::[0].blur_radius (array access)

border_radius

VersionField Path
0.15BorderRadius::top_left (direct field)
0.18+Node::border_radius.top_left (on Node)

scroll_position

VersionField Path
0.15-0.16ScrollPosition::x_offset, y_offset
0.17+ScrollPosition::x, y

line_height

VersionComponent
0.15-0.17TextFont::line_height
0.18+Separate LineHeight component

border_color

VersionStructure
0.15-0.16BorderColor(Srgba) (single value)
0.17+BorderColor { top, right, bottom, left } (per-edge)

Observer Changes

VersionTrigger TypeEntity Access
0.15Trigger::<T>trigger.entity()
0.16Trigger::<T>trigger.target()
0.17+On::<T>trigger.event_target()

ChildBuilder Changes

VersionMethods
0.15.parent_entity(), .spawn(...)
0.16+.target_entity(), .commands_mut()

ChildOf Relationship

VersionMethod
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_radius now writes to Node::border_radius instead of BorderRadius
  • line_height now uses a separate LineHeight component instead of TextFont::line_height
  • Observer entity access uses trigger.event_target() (not trigger.target() or trigger.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> to On<T>
  • Entity access changed from trigger.target() (0.16) to trigger.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:

  • BoxShadow changed from a direct struct to Vec<ShadowStyle>
  • Child relationships use ChildOf instead of set_parent()
  • Observer entity access uses trigger.target() (not trigger.entity() or trigger.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:

  • ChildBuilder uses .parent_entity() and .spawn() instead of .target_entity() and .commands_mut()
  • BorderColor is a single Srgba value, not per-edge
  • Observer entity access uses trigger.entity() (not trigger.target() or trigger.event_target())

Version Support Matrix

Featuremevy_uimevy_coremevy_ecs
0.15
0.16
0.16-rc
0.17
0.18

Tips

  1. Always pin your Bevy version — never omit the version feature.
  2. Test with your target Bevy version — macro expansions differ between versions.
  3. 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, a Val, a UiRect)
  • 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.18 feature for mevy_ui and mevy_core

Fixed

  • Hex color validation for 5-digit hex codes (e.g., #f0a#ff00aa)

Changed

  • mevy_ecs default feature now includes experimental (breaking for users who don’t want experimental macros)

0.3.1

Added

  • 0.17 feature support for mevy_ecs
  • On<T> observer trigger support for Bevy 0.17
  • trigger.event_target() for Bevy 0.17+

Fixed

  • scroll_position field path for Bevy 0.17+ (x/y instead of x_offset/y_offset)

0.3.0

Added

  • 0.16 feature support for all crates
  • 0.16-rc feature support for all crates
  • child_builder support in entity!{}
  • EntityWorldMut support in entity!{}
  • ancestors[] array for nested children
  • try conditional insertion in entity!{}
  • modify!{} shorthand macro
  • cen![], den![], wen![] alternative entity macros
  • gere![], edre![], geco![], edco![] experimental helpers

Changed

  • border_radius now maps to Node::border_radius in Bevy 0.18+
  • BoxShadow field access updated for Bevy 0.16+ (array-based)
  • BorderColor structure updated for Bevy 0.17+ (per-edge)
  • line_height now uses separate LineHeight component 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.