| Crates.io | bevy_expected_components |
| lib.rs | bevy_expected_components |
| version | 0.1.0 |
| created_at | 2026-01-21 09:53:20.232365+00 |
| updated_at | 2026-01-21 09:53:20.232365+00 |
| description | Runtime validation for Bevy component dependencies |
| homepage | |
| repository | https://github.com/peterellisjones/bevy_expected_components |
| max_upload_size | |
| id | 2058768 |
| size | 44,375 |
Runtime validation for Bevy component dependencies. Like #[require] but panics instead of auto-inserting.
This crate adds runtime overhead. Every time a component with #[expects(...)] is inserted, the plugin checks that all expected components exist on the entity.
Enable only in development and test builds:
#[cfg(debug_assertions)]
app.add_plugins(ExpectedComponentsPlugin);
With this pattern, release builds have zero overhead.
Bevy's #[require(T)] automatically inserts missing components using Default. This doesn't work when:
#[expects(T)] solves this by panicking if expected components are missing, making bugs immediately visible during development.
[dependencies]
bevy_expected_components = "0.1"
use bevy::prelude::*;
use bevy_expected_components::prelude::*;
// PhysicsBody expects Transform and Velocity to exist when inserted
#[derive(Component, ExpectComponents)]
#[expects(Transform, Velocity)]
struct PhysicsBody;
#[derive(Component, Default)]
struct Velocity(Vec3);
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins);
// Enable validation in debug builds only
#[cfg(debug_assertions)]
app.add_plugins(ExpectedComponentsPlugin);
app.add_systems(Startup, setup);
app.run();
}
fn setup(mut commands: Commands) {
// This works - all expected components present
commands.spawn((
PhysicsBody,
Transform::default(),
Velocity::default(),
));
// This panics in debug builds - Velocity is missing!
// commands.spawn((PhysicsBody, Transform::default()));
}
When validation fails, you get a clear panic message:
my_game::PhysicsBody expects my_game::Velocity but it was not found on entity 42v3
The stack trace points to the spawn site, making debugging straightforward.
#[require]| Feature | #[require] |
#[expects] |
|---|---|---|
| Missing component | Auto-inserted via Default |
Panics |
Requires Default |
Yes | No |
| Runtime cost | Archetype lookup | Component check |
| Use case | Convenience bundles | Bug detection |
| When to use | Components with sensible defaults | Components that must be explicitly provided |
You can list multiple components in one attribute or use multiple attributes:
// Single attribute with multiple components
#[derive(Component, ExpectComponents)]
#[expects(Transform, Velocity, Health)]
struct Enemy;
// Multiple attributes (equivalent)
#[derive(Component, ExpectComponents)]
#[expects(Transform)]
#[expects(Velocity)]
#[expects(Health)]
struct Enemy;
Full paths work too:
#[derive(Component, ExpectComponents)]
#[expects(bevy::transform::components::Transform)]
struct MyComponent;
#[derive(ExpectComponents)] generates an ExpectComponents trait implementationinventory at compile timeExpectedComponentsPlugin installs on_add hooks for all registered typesValidates insertion only, not removal. If you later remove an expected component from an entity, no error occurs. This keeps the implementation simple and covers the main use case: catching mistakes at spawn time.
If you need removal protection, consider using Bevy's on_remove hooks directly or waiting for archetype invariants.
This crate may become unnecessary when Bevy adds native support for non-defaultable required components. Relevant upstream discussions:
#[require(Component(explicit))] syntax#[must_provide(Component)]Until then, this crate provides a simple, opt-in solution for development-time validation.
| bevy | bevy_expected_components |
|---|---|
| 0.18 | 0.1 |
Licensed under either of Apache License, Version 2.0 or MIT license at your option.