diff --git a/Cargo.toml b/Cargo.toml
index 28d4fd08ebc7400dc3ad56615863e9d9e4b3ac17..02ee2caf477c6d7415862b45acaf81e0a63bf22f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@ members = ["kayak_ui_macros", "kayak_font"]
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-bevy = { git = "https://github.com/bevyengine/bevy", version = "0.9" }
+bevy = { version = "0.9" }
 bytemuck = "1.12"
 dashmap = "5.4"
 kayak_font = { path = "./kayak_font", version = "0.1" }
@@ -30,6 +30,7 @@ resources = "1.1"
 
 [dev-dependencies]
 fastrand = "1.8"
+bevy-inspector-egui = "0.14"
 
 [[example]]
 name = "tabs"
diff --git a/book/src/chapter_1.md b/book/src/chapter_1.md
index 419a831e8cc512470796b046c890b72cb608f99f..1f820f085398a6084c627b6091a804b909e21ff0 100644
--- a/book/src/chapter_1.md
+++ b/book/src/chapter_1.md
@@ -39,7 +39,8 @@ fn startup(
             />
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+    
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
@@ -98,7 +99,8 @@ fn startup(
     widget_context.add_widget(None, app_entity);
 
     // Add widget context as resource.
-    commands.insert_resource(widget_context);
+    
+ commands.spawn(UICameraBundle::new(widget_context));
 }
 fn main() {
     App::new()
diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs
index e57e0db694de47c4df736b186279026e4bb580c0..096ee9f9f901cbb67fa6ec05682308352a500a5c 100644
--- a/examples/bevy_scene.rs
+++ b/examples/bevy_scene.rs
@@ -38,7 +38,7 @@ struct WorldCamera;
 fn set_active_tile_target(
     mut tile: Query<&mut ActiveTile>,
     cursor: Res<Input<MouseButton>>,
-    event_context: Res<EventDispatcher>,
+    event_context: Query<&EventDispatcher, With<GameUI>>,
     camera_transform: Query<&GlobalTransform, With<WorldCamera>>,
     windows: Res<Windows>,
 ) {
@@ -47,7 +47,7 @@ fn set_active_tile_target(
         return;
     }
 
-    if event_context.contains_cursor() {
+    if event_context.single().contains_cursor() {
         // This is the important bit:
         // If the cursor is over a part of the UI, then we should not allow clicks to pass through to the world
         return;
@@ -79,14 +79,14 @@ fn move_active_tile(mut tile: Query<(&mut Transform, &ActiveTile)>) {
 
 /// A system that moves the ghost tile to the cursor's position
 fn move_ghost_tile(
-    event_context: Res<EventDispatcher>,
+    event_context: Query<&EventDispatcher, With<GameUI>>,
     mut tile: Query<&mut Transform, With<GhostTile>>,
     mut cursor_moved: EventReader<CursorMoved>,
     camera_transform: Query<&GlobalTransform, With<WorldCamera>>,
     windows: Res<Windows>,
 ) {
     for _ in cursor_moved.iter() {
-        if !event_context.contains_cursor() {
+        if !event_context.single().contains_cursor() {
             let world_pos = cursor_to_world(&windows, camera_transform.single());
             let tile_pos = world_to_tile(world_pos);
             let mut ghost = tile.single_mut();
@@ -144,7 +144,6 @@ fn cursor_to_world(windows: &Windows, camera_transform: &GlobalTransform) -> Vec
     let size = Vec2::new(window.width(), window.height());
 
     let mut pos = window.cursor_position().unwrap_or_default();
-    pos.y = size.y - pos.y;
     pos -= size / 2.0;
 
     let point = camera_transform.compute_matrix() * pos.extend(0.0).extend(1.0);
@@ -165,6 +164,9 @@ fn ghost_color(color: Color) -> Color {
     c
 }
 
+#[derive(Component)]
+pub struct GameUI;
+
 fn startup(
     mut commands: Commands,
     mut font_mapping: ResMut<FontMapping>,
@@ -172,9 +174,8 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
 
     let handle_change_color = OnEvent::new(
         move |In((event_dispatcher_context, _, event, _)): In<(
@@ -254,7 +255,7 @@ fn startup(
         </KayakAppBundle>
     }
 
-    commands.insert_resource(widget_context);
+    commands.spawn((UICameraBundle::new(widget_context), GameUI));
 }
 
 fn main() {
diff --git a/examples/clipping.rs b/examples/clipping.rs
index b58313779fd5860584b02df79743aaa7aac29da1..ee5a3032f5133edca1ad952381d1f22f4c866f88 100644
--- a/examples/clipping.rs
+++ b/examples/clipping.rs
@@ -8,11 +8,10 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("lato-light.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let image = asset_server.load("panel.png");
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
     let nine_patch_styles = KStyle {
@@ -56,7 +55,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque.
             </NinePatchBundle>
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/conditional_widget.rs b/examples/conditional_widget.rs
index a6de4d662b0dcc7e9fa79b595a9c8c38a45e39f7..b668f17ce4655b6706002df9a4dcf201fb0b84fb 100644
--- a/examples/conditional_widget.rs
+++ b/examples/conditional_widget.rs
@@ -114,9 +114,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<MyWidget, MyWidgetState>();
     widget_context.add_widget_system(
@@ -129,7 +129,8 @@ fn startup(
             <MyWidgetBundle />
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/context.rs b/examples/context.rs
index 8147520f443f647dcce9a9f6fd38ec5701bb37a3..aa3696f59c8a94ccbcd584620bfba509ce0e9688 100644
--- a/examples/context.rs
+++ b/examples/context.rs
@@ -330,9 +330,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<ThemeDemo, EmptyState>();
     widget_context.add_widget_data::<ThemeButton, EmptyState>();
     widget_context.add_widget_data::<ThemeSelector, EmptyState>();
@@ -376,7 +376,8 @@ fn startup(
             </WindowBundle>
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/demo.rs b/examples/demo.rs
index 2d63f7266a47d4e5c7a5c337df6791f6a5e3837b..0ba501e2ec1b438fcfab777ade91007f1ae7384e 100644
--- a/examples/demo.rs
+++ b/examples/demo.rs
@@ -22,6 +22,7 @@ impl Widget for MyWidget {}
 
 fn startup(mut commands: Commands) {
     let mut context = KayakRootContext::new();
+    context.add_plugin(KayakWidgetsContextPlugin);
     context.add_widget_system(
         MyWidget::default().get_name(),
         widget_update::<MyWidget, EmptyState>,
@@ -46,7 +47,7 @@ fn startup(mut commands: Commands) {
     });
     context.add_widget(None, app_entity);
 
-    commands.insert_resource(context);
+    commands.spawn(UICameraBundle::new(context));
 }
 
 // Note this example shows prop changing not state changing which is quite different.
diff --git a/examples/hello_world.rs b/examples/hello_world.rs
index 87cc694a572034eac121139c0e44e9ac769a1cce..e57e6f3aa380cfb6c15bd0a26b8483334b3dd0b9 100644
--- a/examples/hello_world.rs
+++ b/examples/hello_world.rs
@@ -8,9 +8,8 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
         <KayakAppBundle>
@@ -23,7 +22,8 @@ fn startup(
             />
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs
index 647e31db7f387e8a0b2710ef869f6e15222e8389..1b472f8eb962ed39ece9044244278655b1d0005b 100644
--- a/examples/hello_world_no_macro.rs
+++ b/examples/hello_world_no_macro.rs
@@ -6,8 +6,8 @@ fn startup(
     asset_server: Res<AssetServer>,
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
-    commands.spawn(UICameraBundle::new());
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
 
     let app_entity = widget_context.spawn_widget(&mut commands, None);
     // Create default app bundle
@@ -19,7 +19,7 @@ fn startup(
     let mut children = KChildren::new();
 
     // Create the text child
-    let text_entity = widget_context.spawn_widget(&mut commands, Some(app_entity));
+    let text_entity = widget_context.spawn_widget(&mut commands, None);
     commands.entity(text_entity).insert(TextWidgetBundle {
         text: TextProps {
             content: "Hello World".into(),
@@ -38,7 +38,8 @@ fn startup(
     widget_context.add_widget(None, app_entity);
 
     // Add widget context as resource.
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 fn main() {
     App::new()
diff --git a/examples/image.rs b/examples/image.rs
index 03cac2b74d6477463abaceb386ec106d642d1e08..1b490ee054dc4fe355df9255c716da4f60ffb527 100644
--- a/examples/image.rs
+++ b/examples/image.rs
@@ -8,11 +8,10 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let image = asset_server.load("generic-rpg-vendor.png");
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
         <KayakAppBundle>
@@ -30,7 +29,8 @@ fn startup(
             />
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/main_menu.rs b/examples/main_menu.rs
index a67d67bf27b210bc85056115b8f60092606cb979..a1ec90cd1e51d239f9b45a14e9249b9db4031f24 100644
--- a/examples/main_menu.rs
+++ b/examples/main_menu.rs
@@ -122,9 +122,8 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("lato-light.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<MenuButton, ButtonState>();
     widget_context.add_widget_system(
         MenuButton::default().get_name(),
@@ -214,7 +213,7 @@ fn startup(
         </KayakAppBundle>
     }
 
-    commands.insert_resource(widget_context);
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/multi_context.rs b/examples/multi_context.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/examples/multi_context.rs
@@ -0,0 +1 @@
+
diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs
index 2ddf562dbaf355cb75515031e0a41dc8a9e3dd7f..5ff1e0d7c10a73cb3123e6208d41a93dac369a1c 100644
--- a/examples/nine_patch.rs
+++ b/examples/nine_patch.rs
@@ -8,11 +8,10 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let image = asset_server.load("panel.png");
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
     // The border prop splits up the image into 9 quadrants like so:
@@ -52,7 +51,7 @@ fn startup(
         </KayakAppBundle>
     }
 
-    commands.insert_resource(widget_context);
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/quads.rs b/examples/quads.rs
index 5e6523107cc2ecd3aff5dc1ada1fb9717cf7652b..e0afabedbe25817f52a5b68a6e396174380d72f9 100644
--- a/examples/quads.rs
+++ b/examples/quads.rs
@@ -90,9 +90,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_system(
         MyQuad::default().get_name(),
         widget_update::<MyQuad, EmptyState>,
@@ -125,7 +125,7 @@ fn startup(
         </KayakAppBundle>
     }
 
-    commands.insert_resource(widget_context);
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/render_target.rs b/examples/render_target.rs
index 763d5c69723a430fdd5d3bf07aa6fd5c79028d92..52760fc168b68ca270b51aba49ec48fa1381cea6 100644
--- a/examples/render_target.rs
+++ b/examples/render_target.rs
@@ -16,6 +16,9 @@ use kayak_ui::{
 #[derive(Component)]
 struct MainPassCube;
 
+#[derive(Component)]
+struct MainUI;
+
 fn startup(
     mut commands: Commands,
     mut font_mapping: ResMut<FontMapping>,
@@ -53,37 +56,43 @@ fn startup(
 
     let image_handle = images.add(image);
 
-    commands.spawn(UICameraBundle {
-        camera: Camera {
-            priority: -1,
-            target: RenderTarget::Image(image_handle.clone()),
-            ..Camera::default()
-        },
-        camera_ui: CameraUIKayak {
-            clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default,
-        },
-        ..UICameraBundle::new()
-    });
-
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
         <KayakAppBundle
             styles={KStyle {
-                padding: Edge::all(Units::Stretch(1.0)).into(),
+                padding: Edge::new(
+                    Units::Stretch(1.0),
+                    Units::Stretch(0.0),
+                    Units::Stretch(1.0),
+                    Units::Stretch(0.0),
+                ).into(),
                 ..Default::default()
             }}
         >
             <TextWidgetBundle
                 text={TextProps {
                     size: 150.0,
-                    content: "Hello World".into(),
+                    content: "Hello Cube!".into(),
+                    alignment: Alignment::Middle,
                     ..Default::default()
                 }}
             />
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle {
+        camera: Camera {
+            priority: -1,
+            target: RenderTarget::Image(image_handle.clone()),
+            ..Camera::default()
+        },
+        camera_ui: CameraUIKayak {
+            clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default,
+        },
+        ..UICameraBundle::new(widget_context)
+    });
 
     // Setup 3D scene
     // Light
@@ -123,6 +132,23 @@ fn startup(
             .looking_at(Vec3::default(), Vec3::Y),
         ..default()
     });
+
+    // Spawn another UI in 2D space!
+    let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
+    let parent_id = None;
+    rsx! {
+        <KayakAppBundle>
+            <TextWidgetBundle
+                text={TextProps {
+                    size: 100.0,
+                    content: "Hello World!".into(),
+                    ..Default::default()
+                }}
+            />
+        </KayakAppBundle>
+    }
+    commands.spawn((UICameraBundle::new(widget_context), MainUI));
 }
 
 /// Rotates the outer cube (main pass)
@@ -133,6 +159,18 @@ fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Ma
     }
 }
 
+fn depsawn_ui(
+    mut commands: Commands,
+    keyboard_input: Res<Input<KeyCode>>,
+    ui_query: Query<(Entity, &KayakRootContext), With<MainUI>>,
+) {
+    if keyboard_input.pressed(KeyCode::Escape) {
+        if let Ok((entity, _)) = ui_query.get_single() {
+            commands.entity(entity).despawn_descendants();
+        }
+    }
+}
+
 fn main() {
     App::new()
         .add_plugins(DefaultPlugins)
@@ -140,5 +178,6 @@ fn main() {
         .add_plugin(KayakWidgets)
         .add_startup_system(startup)
         .add_system(cube_rotator_system)
+        .add_system(depsawn_ui)
         .run()
 }
diff --git a/examples/scrolling.rs b/examples/scrolling.rs
index baef12e0230ffbca9b0d17c29ff47b0bd0546deb..c4c23871bbcb5d7153edafebf6531b9250585c11 100644
--- a/examples/scrolling.rs
+++ b/examples/scrolling.rs
@@ -1,67 +1,68 @@
-use bevy::prelude::*;
-use kayak_ui::prelude::{widgets::*, *};
-
-fn startup(
-    mut commands: Commands,
-    mut font_mapping: ResMut<FontMapping>,
-    asset_server: Res<AssetServer>,
-) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
-
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
-
-    let mut widget_context = KayakRootContext::new();
-    let parent_id = None;
-
-    let lorem_ipsum = r#"
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. Proin tempus ligula a mi molestie aliquam. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam venenatis consequat ultricies. Sed ac orci purus. Nullam velit nisl, dapibus vel mauris id, dignissim elementum sapien. Vestibulum faucibus sapien ut erat bibendum, id lobortis nisi luctus. Mauris feugiat at lectus at pretium. Pellentesque vitae finibus ante. Nulla non ex neque. Cras varius, lorem facilisis consequat blandit, lorem mauris mollis massa, eget consectetur magna sem vel enim. Nam aliquam risus pulvinar, volutpat leo eget, eleifend urna. Suspendisse in magna sed ligula vehicula volutpat non vitae augue. Phasellus aliquam viverra consequat. Nam rhoncus molestie purus, sed laoreet neque imperdiet eget. Sed egestas metus eget sodales congue.
-                                    
- Sed vel ante placerat, posuere lacus sit amet, tempus enim. Cras ullamcorper ex vitae metus consequat, a blandit leo semper. Nunc lacinia porta massa, a tempus leo laoreet nec. Sed vel metus tincidunt, scelerisque ex sit amet, lacinia dui. In sollicitudin pulvinar odio vitae hendrerit. Maecenas mollis tempor egestas. Nulla facilisi. Praesent nisi turpis, accumsan eu lobortis vestibulum, ultrices id nibh. Suspendisse sed dui porta, mollis elit sed, ornare sem. Cras molestie est libero, quis faucibus leo semper at.
-                                    
- Nulla vel nisl rutrum, fringilla elit non, mollis odio. Donec convallis arcu neque, eget venenatis sem mattis nec. Nulla facilisi. Phasellus risus elit, vehicula sit amet risus et, sodales ultrices est. Quisque vulputate felis orci, non tristique leo faucibus in. Duis quis velit urna. Sed rhoncus dolor vel commodo aliquet. In sed tempor quam. Nunc non tempus ipsum. Praesent mi lacus, vehicula eu dolor eu, condimentum venenatis diam. In tristique ligula a ligula dictum, eu dictum lacus consectetur. Proin elementum egestas pharetra. Nunc suscipit dui ac nisl maximus, id congue velit volutpat. Etiam condimentum, mauris ac sodales tristique, est augue accumsan elit, ut luctus est mi ut urna. Mauris commodo, tortor eget gravida lacinia, leo est imperdiet arcu, a ullamcorper dui sapien eget erat.
-                                
- Vivamus pulvinar dui et elit volutpat hendrerit. Praesent luctus dolor ut rutrum finibus. Fusce ut odio ultrices, laoreet est at, condimentum turpis. Morbi at ultricies nibh. Mauris tempus imperdiet porta. Proin sit amet tincidunt eros. Quisque rutrum lacus ac est vehicula dictum. Pellentesque nec augue mi.
-                                
- Vestibulum rutrum imperdiet nisl, et consequat massa porttitor vel. Ut velit justo, vehicula a nulla eu, auctor eleifend metus. Ut egestas malesuada metus, sit amet pretium nunc commodo ac. Pellentesque gravida, nisl in faucibus volutpat, libero turpis mattis orci, vitae tincidunt ligula ligula ut tortor. Maecenas vehicula lobortis odio in molestie. Curabitur dictum elit sed arcu dictum, ut semper nunc cursus. Donec semper felis non nisl tincidunt elementum.
-    "#.to_string();
-
-    rsx! {
-        <KayakAppBundle>
-            <WindowBundle
-                window={KWindow {
-                    title: "Simple scrolling example".into(),
-                    draggable: true,
-                    initial_position: Vec2::new(10.0, 10.0),
-                    size: Vec2::new(512.0, 512.0),
-                    ..KWindow::default()
-                }}
-            >
-                <ScrollContextProviderBundle>
-                    <ScrollBoxBundle>
-                        <TextWidgetBundle
-                            text={TextProps {
-                                content: lorem_ipsum,
-                                size: 14.0,
-                                ..Default::default()
-                            }}
-                        />
-                    </ScrollBoxBundle>
-                </ScrollContextProviderBundle>
-            </WindowBundle>
-        </KayakAppBundle>
-    }
-    commands.insert_resource(widget_context);
-}
-
-fn main() {
-    App::new()
-        .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
-        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
-        .add_plugin(KayakContextPlugin)
-        .add_plugin(KayakWidgets)
-        .add_startup_system(startup)
-        .run()
-}
+use bevy::prelude::*;
+use kayak_ui::prelude::{widgets::*, *};
+
+fn startup(
+    mut commands: Commands,
+    mut font_mapping: ResMut<FontMapping>,
+    asset_server: Res<AssetServer>,
+) {
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+
+    // Camera 2D forces a clear pass in bevy.
+    // We do this because our scene is not rendering anything else.
+    commands.spawn(Camera2dBundle::default());
+
+    let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
+    let parent_id = None;
+
+    let lorem_ipsum = r#"
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. Proin tempus ligula a mi molestie aliquam. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam venenatis consequat ultricies. Sed ac orci purus. Nullam velit nisl, dapibus vel mauris id, dignissim elementum sapien. Vestibulum faucibus sapien ut erat bibendum, id lobortis nisi luctus. Mauris feugiat at lectus at pretium. Pellentesque vitae finibus ante. Nulla non ex neque. Cras varius, lorem facilisis consequat blandit, lorem mauris mollis massa, eget consectetur magna sem vel enim. Nam aliquam risus pulvinar, volutpat leo eget, eleifend urna. Suspendisse in magna sed ligula vehicula volutpat non vitae augue. Phasellus aliquam viverra consequat. Nam rhoncus molestie purus, sed laoreet neque imperdiet eget. Sed egestas metus eget sodales congue.
+                                    
+ Sed vel ante placerat, posuere lacus sit amet, tempus enim. Cras ullamcorper ex vitae metus consequat, a blandit leo semper. Nunc lacinia porta massa, a tempus leo laoreet nec. Sed vel metus tincidunt, scelerisque ex sit amet, lacinia dui. In sollicitudin pulvinar odio vitae hendrerit. Maecenas mollis tempor egestas. Nulla facilisi. Praesent nisi turpis, accumsan eu lobortis vestibulum, ultrices id nibh. Suspendisse sed dui porta, mollis elit sed, ornare sem. Cras molestie est libero, quis faucibus leo semper at.
+                                    
+ Nulla vel nisl rutrum, fringilla elit non, mollis odio. Donec convallis arcu neque, eget venenatis sem mattis nec. Nulla facilisi. Phasellus risus elit, vehicula sit amet risus et, sodales ultrices est. Quisque vulputate felis orci, non tristique leo faucibus in. Duis quis velit urna. Sed rhoncus dolor vel commodo aliquet. In sed tempor quam. Nunc non tempus ipsum. Praesent mi lacus, vehicula eu dolor eu, condimentum venenatis diam. In tristique ligula a ligula dictum, eu dictum lacus consectetur. Proin elementum egestas pharetra. Nunc suscipit dui ac nisl maximus, id congue velit volutpat. Etiam condimentum, mauris ac sodales tristique, est augue accumsan elit, ut luctus est mi ut urna. Mauris commodo, tortor eget gravida lacinia, leo est imperdiet arcu, a ullamcorper dui sapien eget erat.
+                                
+ Vivamus pulvinar dui et elit volutpat hendrerit. Praesent luctus dolor ut rutrum finibus. Fusce ut odio ultrices, laoreet est at, condimentum turpis. Morbi at ultricies nibh. Mauris tempus imperdiet porta. Proin sit amet tincidunt eros. Quisque rutrum lacus ac est vehicula dictum. Pellentesque nec augue mi.
+                                
+ Vestibulum rutrum imperdiet nisl, et consequat massa porttitor vel. Ut velit justo, vehicula a nulla eu, auctor eleifend metus. Ut egestas malesuada metus, sit amet pretium nunc commodo ac. Pellentesque gravida, nisl in faucibus volutpat, libero turpis mattis orci, vitae tincidunt ligula ligula ut tortor. Maecenas vehicula lobortis odio in molestie. Curabitur dictum elit sed arcu dictum, ut semper nunc cursus. Donec semper felis non nisl tincidunt elementum.
+    "#.to_string();
+
+    rsx! {
+        <KayakAppBundle>
+            <WindowBundle
+                window={KWindow {
+                    title: "Simple scrolling example".into(),
+                    draggable: true,
+                    initial_position: Vec2::new(10.0, 10.0),
+                    size: Vec2::new(512.0, 512.0),
+                    ..KWindow::default()
+                }}
+            >
+                <ScrollContextProviderBundle>
+                    <ScrollBoxBundle>
+                        <TextWidgetBundle
+                            text={TextProps {
+                                content: lorem_ipsum,
+                                size: 14.0,
+                                ..Default::default()
+                            }}
+                        />
+                    </ScrollBoxBundle>
+                </ScrollContextProviderBundle>
+            </WindowBundle>
+        </KayakAppBundle>
+    }
+
+    commands.spawn(UICameraBundle::new(widget_context));
+}
+
+fn main() {
+    App::new()
+        .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
+        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
+        .add_plugin(KayakContextPlugin)
+        .add_plugin(KayakWidgets)
+        .add_startup_system(startup)
+        .run()
+}
diff --git a/examples/simple_state.rs b/examples/simple_state.rs
index 8f5be82375345457e67bb2680ab776cbfd65fe49..0e2e8e2d0768e06dae6320fd45dca1b90bb6e389 100644
--- a/examples/simple_state.rs
+++ b/examples/simple_state.rs
@@ -88,9 +88,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<CurrentCount, CurrentCountState>();
     widget_context.add_widget_system(
@@ -126,7 +126,8 @@ fn startup(
             </WindowContextProviderBundle>
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs
index 8df527c3ac4a7cb9f23ea5c882e1eea1de44bdcc..677c1e3abc280e0f198b5eb1dcccfdee365648aa 100644
--- a/examples/tabs/tabs.rs
+++ b/examples/tabs/tabs.rs
@@ -20,9 +20,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<Tab, EmptyState>();
     widget_context.add_widget_data::<TabContextProvider, EmptyState>();
     widget_context.add_widget_data::<TabButton, EmptyState>();
@@ -87,7 +87,7 @@ fn startup(
         </KayakAppBundle>
     }
 
-    commands.insert_resource(widget_context);
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/text.rs b/examples/text.rs
index 3d6ae7eab5139c412d727463ae441c3917f20bbc..b2f82d38f691c2012a5a13f6e94013457b9da7e9 100644
--- a/examples/text.rs
+++ b/examples/text.rs
@@ -65,11 +65,14 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    // Camera 2D forces a clear pass in bevy.
+    // We do this because our scene is not rendering anything else.
+    commands.spawn(Camera2dBundle::default());
 
-    commands.spawn(UICameraBundle::new());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
     widget_context.add_widget_system(
@@ -80,7 +83,8 @@ fn startup(
     rsx! {
         <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn update_resource(keyboard_input: Res<Input<KeyCode>>, mut my_resource: ResMut<MyResource>) {
diff --git a/examples/text_box.rs b/examples/text_box.rs
index 812b046fceb3287bd1f2a6cd8acdd05affe8f7f9..4894d90d772284f888a7fba1c02b492326a122b3 100644
--- a/examples/text_box.rs
+++ b/examples/text_box.rs
@@ -93,9 +93,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
 
     widget_context.add_widget_data::<TextBoxExample, TextBoxExampleState>();
     widget_context.add_widget_system(
@@ -119,7 +119,8 @@ fn startup(
             </WindowBundle>
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs
index 8d78df60949993cf5495aef0edaddf2512bb7fc2..87298e0a34b1487dd2983858fc842d589dc7a9e0 100644
--- a/examples/texture_atlas.rs
+++ b/examples/texture_atlas.rs
@@ -8,8 +8,6 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let image_handle = asset_server.load("texture_atlas.png");
 
     //texture_atlas.png uses 16 pixel sprites and is 272x128 pixels
@@ -31,6 +29,7 @@ fn startup(
     let flower_index = columns * 5 + 15;
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
     let atlas_styles = KStyle {
@@ -69,7 +68,7 @@ fn startup(
         </KayakAppBundle>
     }
 
-    commands.insert_resource(widget_context);
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs
index 36687d9424d38083ae852198ee2443ca9de0adbf..0e50bea1313e7655e2bd1742f11e9716f2d02130 100644
--- a/examples/todo/todo.rs
+++ b/examples/todo/todo.rs
@@ -1,4 +1,5 @@
 use bevy::prelude::*;
+use bevy_inspector_egui::WorldInspectorPlugin;
 use kayak_ui::prelude::{widgets::*, *};
 
 mod input;
@@ -57,9 +58,9 @@ fn startup(
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
     commands.spawn(Camera2dBundle::default());
-    commands.spawn(UICameraBundle::new());
 
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<TodoItemsProps, EmptyState>();
     widget_context.add_widget_data::<TodoInputProps, EmptyState>();
 
@@ -97,13 +98,15 @@ fn startup(
             </WindowBundle>
         </KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
     App::new()
         .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
         .add_plugins(DefaultPlugins)
+        .add_plugin(WorldInspectorPlugin::new())
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
         .insert_non_send_resource(TodoList::new())
diff --git a/examples/vec.rs b/examples/vec.rs
index 9e412a831ebb5d31228d5e75916a6ce51f6c0a18..4d9daf3316a2afd0a860358793400a86bde43b02 100644
--- a/examples/vec.rs
+++ b/examples/vec.rs
@@ -61,9 +61,8 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    commands.spawn(UICameraBundle::new());
-
     let mut widget_context = KayakRootContext::new();
+    widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
     widget_context.add_widget_system(
@@ -74,7 +73,8 @@ fn startup(
     rsx! {
         <KayakAppBundle><MyWidgetBundle /></KayakAppBundle>
     }
-    commands.insert_resource(widget_context);
+
+    commands.spawn(UICameraBundle::new(widget_context));
 }
 
 fn main() {
diff --git a/kayak_font/Cargo.toml b/kayak_font/Cargo.toml
index 934feb903726461ff740225269ef898a521444b0..1d24693eb10705332968dd199b9abcd55e0e61ff 100644
--- a/kayak_font/Cargo.toml
+++ b/kayak_font/Cargo.toml
@@ -22,8 +22,8 @@ unicode-segmentation = "1.10.0"
 # Provides UAX #14 line break segmentation
 xi-unicode = "0.3"
 
-bevy = { git = "https://github.com/bevyengine/bevy", version = "0.9", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] }
+bevy = { version = "0.9", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] }
 
 [dev-dependencies]
-bevy = { git = "https://github.com/bevyengine/bevy", version = "0.9" }
+bevy = { version = "0.9" }
 bytemuck = "1.12.0"
diff --git a/kayak_font/src/layout/glyph.rs b/kayak_font/src/layout/glyph.rs
index cfd33e3b8350e560c87784bb42f5831eb0239781..a7d7f8429634d18e409ad23cbfa7cdb29f96151f 100644
--- a/kayak_font/src/layout/glyph.rs
+++ b/kayak_font/src/layout/glyph.rs
@@ -1,5 +1,7 @@
+use bevy::reflect::{FromReflect, Reflect};
+
 /// Layout information for a renderable glyph.
-#[derive(Default, Debug, Clone, Copy, PartialEq)]
+#[derive(Default, Reflect, FromReflect, Debug, Clone, Copy, PartialEq)]
 pub struct GlyphRect {
     pub position: (f32, f32),
     pub size: (f32, f32),
diff --git a/kayak_font/src/layout/grapheme.rs b/kayak_font/src/layout/grapheme.rs
index 23cf11850eb7f027f9965489989a2e3697bb527b..df0ce15308f14e0dc548cdb1558b17415aa106cc 100644
--- a/kayak_font/src/layout/grapheme.rs
+++ b/kayak_font/src/layout/grapheme.rs
@@ -1,9 +1,11 @@
 use std::cmp::Ordering;
 
+use bevy::reflect::{FromReflect, Reflect};
+
 /// A representation of a grapheme cluster, as defined by [Unicode UAX #29].
 ///
 /// [Unicode UAX #29]: https://unicode.org/reports/tr29/
-#[derive(Default, Debug, Copy, Clone, PartialEq)]
+#[derive(Default, Debug, Reflect, FromReflect, Copy, Clone, PartialEq)]
 pub struct Grapheme {
     /// The index of the starting char within this grapheme, relative to the entire text content.
     pub char_index: usize,
diff --git a/kayak_font/src/layout/line.rs b/kayak_font/src/layout/line.rs
index bd6a688970c517c33916373dcd904e427aca3a3c..41e07030599b773aa1bb757189c6f3bbf2482f72 100644
--- a/kayak_font/src/layout/line.rs
+++ b/kayak_font/src/layout/line.rs
@@ -1,10 +1,12 @@
+use bevy::reflect::{FromReflect, Reflect};
+
 use crate::layout::grapheme::Grapheme;
 use std::cmp::Ordering;
 use std::ops::Index;
 use std::slice::SliceIndex;
 
 /// Contains details for a calculated line of text.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Reflect, FromReflect, Debug, PartialEq)]
 pub struct Line {
     grapheme_index: usize,
     graphemes: Vec<Grapheme>,
diff --git a/kayak_font/src/layout/text.rs b/kayak_font/src/layout/text.rs
index fd646a3a1ce8fde5282eecd50deb0b6e2c70aaf5..8d5a59031799cf9979a846bcf526b518e2dd116d 100644
--- a/kayak_font/src/layout/text.rs
+++ b/kayak_font/src/layout/text.rs
@@ -1,8 +1,10 @@
+use bevy::reflect::{FromReflect, Reflect};
+
 use crate::{GlyphRect, Line, RowCol};
 use std::cmp::Ordering;
 
 /// The text alignment.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Reflect, FromReflect, Debug, PartialEq, Eq)]
 pub enum Alignment {
     Start,
     Middle,
@@ -10,7 +12,7 @@ pub enum Alignment {
 }
 
 /// Properties to control text layout.
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Reflect, FromReflect, Debug, PartialEq)]
 pub struct TextProperties {
     /// The font size (in pixels).
     pub font_size: f32,
@@ -39,7 +41,7 @@ impl Default for TextProperties {
 /// Calculated text layout.
 ///
 /// This can be retrieved using [`measure`](crate::KayakFont::measure).
-#[derive(Clone, Debug, Default, PartialEq)]
+#[derive(Clone, Reflect, FromReflect, Debug, Default, PartialEq)]
 pub struct TextLayout {
     glyphs: Vec<GlyphRect>,
     lines: Vec<Line>,
diff --git a/src/calculate_nodes.rs b/src/calculate_nodes.rs
index 5f37f783ad27d02b2e6c3025a59d59c55a39c4a7..1a6257e85e29379719e0d037548199e0b47c8184 100644
--- a/src/calculate_nodes.rs
+++ b/src/calculate_nodes.rs
@@ -1,5 +1,5 @@
 use bevy::{
-    prelude::{Assets, Commands, Entity, Query, Res, ResMut, With},
+    prelude::{Assets, Commands, Entity, In, Query, Res, With},
     utils::HashMap,
 };
 use kayak_font::KayakFont;
@@ -15,35 +15,32 @@ use crate::{
 };
 
 pub fn calculate_nodes(
+    In(mut context): In<KayakRootContext>,
     mut commands: Commands,
-    mut context: ResMut<KayakRootContext>,
     fonts: Res<Assets<KayakFont>>,
     font_mapping: Res<FontMapping>,
     query: Query<Entity, With<DirtyNode>>,
     all_styles_query: Query<&KStyle>,
     node_query: Query<(Entity, &Node)>,
-) {
+) -> KayakRootContext {
     let mut new_nodes = HashMap::<Entity, (Node, bool)>::default();
-    // This is the maximum recursion depth for this method.
-    // Recursion involves recalculating layout which should be done sparingly.
-    // const MAX_RECURSION_DEPTH: usize = 2;
 
     context.current_z = 0.0;
 
     let initial_styles = KStyle::initial();
     let default_styles = KStyle::new_default();
 
-    // Jump out early.
-    // if query.is_empty() {
-    //     return;
-    // }
     if let Ok(tree) = context.tree.clone().try_read() {
         if tree.root_node.is_none() {
-            return;
+            return context;
         }
 
         for dirty_entity in query.iter() {
             let dirty_entity = WrappedIndex(dirty_entity);
+            if !tree.contains(dirty_entity) {
+                continue;
+            }
+
             let styles = all_styles_query
                 .get(dirty_entity.0)
                 .unwrap_or(&default_styles);
@@ -170,14 +167,15 @@ pub fn calculate_nodes(
             commands.entity(entity).insert(node);
         }
     }
+
+    context
 }
 
 pub fn calculate_layout(
+    In(context): In<KayakRootContext>,
     mut commands: Commands,
-    mut context: ResMut<KayakRootContext>,
     nodes_no_entity_query: Query<&'static Node>,
-) {
-    let context = context.as_mut();
+) -> KayakRootContext {
     if let Ok(tree) = context.tree.try_read() {
         // tree.dump();
         let node_tree = &*tree;
@@ -200,6 +198,8 @@ pub fn calculate_layout(
             }
         }
     }
+
+    context
 }
 
 fn create_primitive(
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
index 4f509abca81886413bc4551e9164efe241701bc0..f432acedb4b8a8aa7a0ea9deb165dd981dcb0c70 100644
--- a/src/camera/camera.rs
+++ b/src/camera/camera.rs
@@ -10,6 +10,8 @@ use bevy::{
     },
 };
 
+use crate::{context::KayakRootContext, event_dispatcher::EventDispatcher};
+
 use super::ortho::UIOrthographicProjection;
 
 /// Kayak UI's default UI camera.
@@ -39,17 +41,19 @@ pub struct UICameraBundle {
     pub transform: Transform,
     pub global_transform: GlobalTransform,
     pub camera_ui: CameraUIKayak,
+    pub context: KayakRootContext,
+    pub event_disaptcher: EventDispatcher,
 }
 
 impl Default for UICameraBundle {
     fn default() -> Self {
-        Self::new()
+        Self::new(KayakRootContext::default())
     }
 }
 
 impl UICameraBundle {
     pub const UI_CAMERA: &'static str = "KAYAK_UI_CAMERA";
-    pub fn new() -> Self {
+    pub fn new(kayak_root_context: KayakRootContext) -> Self {
         // we want 0 to be "closest" and +far to be "farthest" in 2d, so we offset
         // the camera's translation by far and use a right handed coordinate system
         let far = 1000.0;
@@ -85,6 +89,8 @@ impl UICameraBundle {
             camera_ui: CameraUIKayak {
                 clear_color: ClearColorConfig::None,
             },
+            context: kayak_root_context,
+            event_disaptcher: EventDispatcher::new(),
         }
     }
 }
diff --git a/src/children.rs b/src/children.rs
index c2e2de6915d696c5fcc711d0fe562bcc4fe90ff1..d5dd84503177b45045a2eab4141f3a26cd90078d 100644
--- a/src/children.rs
+++ b/src/children.rs
@@ -3,7 +3,8 @@ use bevy::prelude::*;
 use crate::prelude::KayakWidgetContext;
 
 /// Defers widgets being added to the widget tree.
-#[derive(Component, Debug, Default, Clone, PartialEq, Eq)]
+#[derive(Component, Reflect, Debug, Default, Clone, PartialEq, Eq)]
+#[reflect(Component)]
 pub struct KChildren {
     inner: Vec<Entity>,
 }
diff --git a/src/context.rs b/src/context.rs
index f1bba5ef0b550e792c232d7ada4b49068e0a852c..4cac17487e7d4c9e2b924d9c588b43f0d2909f98 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock};
 use bevy::{
     ecs::{event::ManualEventReader, system::CommandQueue},
     prelude::*,
-    utils::HashMap,
+    utils::{HashMap, HashSet},
 };
 use morphorm::Hierarchy;
 
@@ -12,21 +12,27 @@ use crate::{
     children::KChildren,
     clone_component::{clone_state, clone_system, EntityCloneSystems, PreviousWidget},
     context_entities::ContextEntities,
+    cursor::PointerEvents,
     event_dispatcher::EventDispatcher,
     focus_tree::FocusTree,
+    input::query_world,
     layout::{LayoutCache, Rect},
     layout_dispatcher::LayoutEventDispatcher,
     node::{DirtyNode, WrappedIndex},
     prelude::KayakWidgetContext,
     render_primitive::RenderPrimitive,
-    styles::KStyle,
+    styles::{
+        Corner, Edge, KCursorIcon, KPositionType, KStyle, LayoutType, RenderCommand, StyleProp,
+        Units,
+    },
     tree::{Change, Tree},
     widget_state::WidgetState,
-    Focusable, WindowSize,
+    Focusable, KayakUIPlugin, WindowSize,
 };
 
 /// A tag component representing when a widget has been mounted(added to the tree).
-#[derive(Component)]
+#[derive(Component, Reflect, Default)]
+#[reflect(Component)]
 pub struct Mounted;
 
 const UPDATE_DEPTH: u32 = 0;
@@ -58,7 +64,7 @@ type WidgetSystems = HashMap<
 ///     }).id();
 ///     // Stores the kayak app widget in the widget context's tree.
 ///     widget_context.add_widget(None, app_entity);
-///     commands.insert_resource(widget_context);
+///     commands.spawn(UICameraBundle::new(widget_context));
 /// }
 ///
 /// fn main() {
@@ -69,7 +75,7 @@ type WidgetSystems = HashMap<
 ///     .add_startup_system(setup);
 /// }
 /// ```
-#[derive(Resource)]
+#[derive(Component)]
 pub struct KayakRootContext {
     pub tree: Arc<RwLock<Tree>>,
     pub(crate) layout_cache: Arc<RwLock<LayoutCache>>,
@@ -83,6 +89,7 @@ pub struct KayakRootContext {
     pub(crate) widget_state: WidgetState,
     pub(crate) order_tree: Arc<RwLock<Tree>>,
     pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>,
+    pub(crate) uninitilized_systems: HashSet<String>,
 }
 
 impl Default for KayakRootContext {
@@ -107,7 +114,21 @@ impl KayakRootContext {
             widget_state: Default::default(),
             index: Default::default(),
             order_tree: Default::default(),
+            uninitilized_systems: Default::default(),
+        }
+    }
+
+    /// Adds a kayak plugin and runs the build function on the context.
+    pub fn add_plugin(&mut self, plugin: impl KayakUIPlugin) {
+        plugin.build(self)
+    }
+
+    /// Retreives the current entity that has focus or None if nothing is focused.
+    pub fn get_current_focus(&self) -> Option<Entity> {
+        if let Ok(tree) = self.focus_tree.try_read() {
+            return tree.current().and_then(|a| Some(a.0));
         }
+        None
     }
 
     /// Get's the layout for th given widget index.
@@ -141,10 +162,12 @@ impl KayakRootContext {
         update: impl IntoSystem<(KayakWidgetContext, Entity, Entity), bool, Params>,
         render: impl IntoSystem<(KayakWidgetContext, Entity), bool, Params2>,
     ) {
+        let type_name = type_name.into();
         let update_system = Box::new(IntoSystem::into_system(update));
         let render_system = Box::new(IntoSystem::into_system(render));
         self.systems
-            .insert(type_name.into(), (update_system, render_system));
+            .insert(type_name.clone(), (update_system, render_system));
+        self.uninitilized_systems.insert(type_name);
     }
 
     /// Let's the widget context know what data types are used for a given widget.
@@ -278,8 +301,6 @@ impl KayakRootContext {
             return vec![];
         }
 
-        // self.node_tree.dump();
-
         let render_primitives = if let Ok(mut layout_cache) = self.layout_cache.try_write() {
             recurse_node_tree_to_build_primitives(
                 &node_tree,
@@ -346,12 +367,23 @@ fn recurse_node_tree_to_build_primitives(
         };
 
         match &render_primitive {
-            RenderPrimitive::Text { content, .. } => {
+            RenderPrimitive::Text {
+                content, layout, ..
+            } => {
                 log::trace!(
-                    "Text node: {}-{} is equal to: {}",
+                    "Text node: {}-{} is equal to: {}, {:?}",
                     widget_names.get(current_node.0).unwrap().0,
                     current_node.0.index(),
                     content,
+                    layout,
+                );
+            }
+            RenderPrimitive::Clip { layout } => {
+                log::trace!(
+                    "Clip node: {}-{} is equal to: {:?}",
+                    widget_names.get(current_node.0).unwrap().0,
+                    current_node.0.index(),
+                    layout,
                 );
             }
             RenderPrimitive::Empty => {
@@ -439,87 +471,104 @@ fn recurse_node_tree_to_build_primitives(
 }
 
 fn update_widgets_sys(world: &mut World) {
-    let mut context = world.remove_resource::<KayakRootContext>().unwrap();
-    let tree_iterator = if let Ok(tree) = context.tree.read() {
-        tree.down_iter().collect::<Vec<_>>()
-    } else {
-        panic!("Failed to acquire read lock.");
-    };
+    let mut context_data = Vec::new();
 
-    // let change_tick = world.increment_change_tick();
-
-    let old_focus = if let Ok(mut focus_tree) = context.focus_tree.try_write() {
-        let current = focus_tree.current();
-        focus_tree.clear();
-        if let Ok(tree) = context.tree.read() {
-            if let Some(root_node) = tree.root_node {
-                focus_tree.add(root_node, &tree);
+    query_world::<Query<(Entity, &mut KayakRootContext)>, _, _>(
+        |mut query| {
+            for (entity, mut kayak_root_context) in query.iter_mut() {
+                context_data.push((entity, std::mem::take(&mut *kayak_root_context)));
             }
-        }
-        current
-    } else {
-        None
-    };
-
-    let mut new_ticks = HashMap::new();
-
-    // dbg!("Updating widgets!");
-    update_widgets(
+        },
         world,
-        &context.tree,
-        &context.layout_cache,
-        &mut context.systems,
-        tree_iterator,
-        &context.context_entities,
-        &context.focus_tree,
-        &context.clone_systems,
-        &context.cloned_widget_entities,
-        &context.widget_state,
-        &mut new_ticks,
-        &context.order_tree,
-        &context.index,
     );
 
-    if let Some(old_focus) = old_focus {
-        if let Ok(mut focus_tree) = context.focus_tree.try_write() {
-            if focus_tree.contains(old_focus) {
-                focus_tree.focus(old_focus);
+    for (camera_entity, mut context) in context_data.drain(..) {
+        for system_id in context.uninitilized_systems.drain() {
+            if let Some(system) = context.systems.get_mut(&system_id) {
+                system.0.initialize(world);
+                system.1.initialize(world);
             }
         }
-    }
 
-    // dbg!("Finished updating widgets!");
-    let tick = world.read_change_tick();
+        let tree_iterator = if let Ok(tree) = context.tree.read() {
+            tree.down_iter().collect::<Vec<_>>()
+        } else {
+            panic!("Failed to acquire read lock.");
+        };
 
-    for (key, system) in context.systems.iter_mut() {
-        if let Some(new_tick) = new_ticks.get(key) {
-            system.0.set_last_change_tick(*new_tick);
-            system.1.set_last_change_tick(*new_tick);
+        // let change_tick = world.increment_change_tick();
+
+        let old_focus = if let Ok(mut focus_tree) = context.focus_tree.try_write() {
+            let current = focus_tree.current();
+            focus_tree.clear();
+            if let Ok(tree) = context.tree.read() {
+                if let Some(root_node) = tree.root_node {
+                    focus_tree.add(root_node, &tree);
+                }
+            }
+            current
         } else {
-            system.0.set_last_change_tick(tick);
-            system.1.set_last_change_tick(tick);
+            None
+        };
+
+        let mut new_ticks = HashMap::new();
+
+        // dbg!("Updating widgets!");
+        update_widgets(
+            camera_entity,
+            world,
+            &context.tree,
+            &context.layout_cache,
+            &mut context.systems,
+            tree_iterator,
+            &context.context_entities,
+            &context.focus_tree,
+            &context.clone_systems,
+            &context.cloned_widget_entities,
+            &context.widget_state,
+            &mut new_ticks,
+            &context.order_tree,
+            &context.index,
+        );
+
+        if let Some(old_focus) = old_focus {
+            if let Ok(mut focus_tree) = context.focus_tree.try_write() {
+                if focus_tree.contains(old_focus) {
+                    focus_tree.focus(old_focus);
+                }
+            }
         }
-        // system.apply_buffers(world);
-    }
 
-    // Clear out indices
-    if let Ok(mut indices) = context.index.try_write() {
-        // for (entity, value) in indices.iter_mut() {
-        //     if tree.root_node.unwrap().0.id() != entity.id() {
-        //         *value = 0;
-        //     }
-        // }
-        indices.clear();
-    }
+        // dbg!("Finished updating widgets!");
+        let tick = world.read_change_tick();
 
-    // if let Ok(order_tree) = context.order_tree.try_read() {
-    //     order_tree.dump();
-    // }
+        for (key, system) in context.systems.iter_mut() {
+            if let Some(new_tick) = new_ticks.get(key) {
+                system.0.set_last_change_tick(*new_tick);
+                system.1.set_last_change_tick(*new_tick);
+            } else {
+                system.0.set_last_change_tick(tick);
+                system.1.set_last_change_tick(tick);
+            }
+            // system.apply_buffers(world);
+        }
+
+        // Clear out indices
+        if let Ok(mut indices) = context.index.try_write() {
+            // for (entity, value) in indices.iter_mut() {
+            //     if tree.root_node.unwrap().0.id() != entity.id() {
+            //         *value = 0;
+            //     }
+            // }
+            indices.clear();
+        }
 
-    world.insert_resource(context);
+        world.entity_mut(camera_entity).insert(context);
+    }
 }
 
 fn update_widgets(
+    camera_entity: Entity,
     world: &mut World,
     tree: &Arc<RwLock<Tree>>,
     layout_cache: &Arc<RwLock<LayoutCache>>,
@@ -535,6 +584,16 @@ fn update_widgets(
     index: &Arc<RwLock<HashMap<Entity, usize>>>,
 ) {
     for entity in widgets.iter() {
+        // A small hack to add parents to widgets
+        let mut command_queue = CommandQueue::default();
+        {
+            let mut commands = Commands::new(&mut command_queue, &world);
+            if let Some(mut entity_commands) = commands.get_entity(entity.0) {
+                entity_commands.set_parent(camera_entity);
+            }
+        }
+        command_queue.apply(world);
+
         if let Some(entity_ref) = world.get_entity(entity.0) {
             if let Some(widget_type) = entity_ref.get::<WidgetName>() {
                 let widget_context = KayakWidgetContext::new(
@@ -544,6 +603,7 @@ fn update_widgets(
                     widget_state.clone(),
                     order_tree.clone(),
                     index.clone(),
+                    Some(camera_entity),
                 );
                 widget_context.copy_from_point(tree, *entity);
                 let children_before = widget_context.get_children(entity.0);
@@ -623,7 +683,7 @@ fn update_widgets(
                         tree.merge(&widget_context, *entity, diff, UPDATE_DEPTH);
 
                         // if had_removal {
-                        //     tree.dump();
+                        // tree.dump();
                         // }
 
                         for child in widget_context.child_iter(*entity) {
@@ -641,6 +701,7 @@ fn update_widgets(
                     vec![]
                 };
                 update_widgets(
+                    camera_entity,
                     world,
                     tree,
                     layout_cache,
@@ -657,6 +718,31 @@ fn update_widgets(
                 );
                 // }
             }
+        } else {
+            // In this case the entity we are trying to process no longer exists.
+            // The approach taken here removes said entities from the tree.
+            let mut despawn_list = Vec::default();
+            if let Ok(mut tree) = tree.write() {
+                for child in tree.down_iter_at(*entity, true) {
+                    despawn_list.push(child.0);
+                    if let Ok(mut order_tree) = order_tree.try_write() {
+                        // had_removal = true;
+                        log::trace!(
+                            "Removing entity! {:?} inside of: {:?}",
+                            child.0.index(),
+                            entity.0.index()
+                        );
+                        order_tree.remove(child);
+                    }
+                }
+
+                for entity in despawn_list.drain(..) {
+                    tree.remove(WrappedIndex(entity));
+                    if let Some(entity_mut) = world.get_entity_mut(entity) {
+                        entity_mut.despawn();
+                    }
+                }
+            }
         }
 
         if let Some(entity_ref) = world.get_entity(entity.0) {
@@ -708,7 +794,13 @@ fn update_widget(
                 panic!("Couldn't get write lock!")
             };
 
-        let widget_update_system = &mut systems.get_mut(&widget_type).unwrap().0;
+        let widget_update_system = &mut systems
+            .get_mut(&widget_type)
+            .expect(&format!(
+                "Wasn't able to find render/update systems for widget: {}!",
+                widget_type
+            ))
+            .0;
         let old_tick = widget_update_system.get_last_change_tick();
         let should_rerender =
             widget_update_system.run((widget_context.clone(), entity.0, old_props_entity), world);
@@ -857,16 +949,6 @@ fn update_widget(
     (widget_context, should_update_children)
 }
 
-fn init_systems(world: &mut World) {
-    let mut context = world.remove_resource::<KayakRootContext>().unwrap();
-    for system in context.systems.values_mut() {
-        system.0.initialize(world);
-        system.1.initialize(world);
-    }
-
-    world.insert_resource(context);
-}
-
 /// The default Kayak Context plugin
 /// Creates systems and resources for kayak.
 pub struct KayakContextPlugin;
@@ -877,7 +959,6 @@ pub struct CustomEventReader<T: bevy::ecs::event::Event>(pub ManualEventReader<T
 impl Plugin for KayakContextPlugin {
     fn build(&self, app: &mut App) {
         app.insert_resource(WindowSize::default())
-            .insert_resource(EventDispatcher::new())
             .insert_resource(CustomEventReader(ManualEventReader::<
                 bevy::window::CursorMoved,
             >::default()))
@@ -896,40 +977,68 @@ impl Plugin for KayakContextPlugin {
             .add_plugin(crate::camera::KayakUICameraPlugin)
             .add_plugin(crate::render::BevyKayakUIRenderPlugin)
             .register_type::<Node>()
-            .add_startup_system_to_stage(StartupStage::PostStartup, init_systems.at_end())
             .add_system_to_stage(CoreStage::Update, crate::input::process_events)
             .add_system_to_stage(CoreStage::PostUpdate, update_widgets_sys.at_start())
             .add_system_to_stage(CoreStage::PostUpdate, calculate_ui.at_end())
             .add_system(crate::window_size::update_window_size);
+
+        // Register reflection types.
+        // A bit annoying..
+        app.register_type::<KStyle>()
+            .register_type::<KChildren>()
+            .register_type::<WidgetName>()
+            .register_type::<StyleProp<Color>>()
+            .register_type::<StyleProp<Corner<f32>>>()
+            .register_type::<StyleProp<Edge<f32>>>()
+            .register_type::<StyleProp<Units>>()
+            .register_type::<StyleProp<KCursorIcon>>()
+            .register_type::<StyleProp<String>>()
+            .register_type::<StyleProp<f32>>()
+            .register_type::<StyleProp<LayoutType>>()
+            .register_type::<StyleProp<Edge<Units>>>()
+            .register_type::<StyleProp<PointerEvents>>()
+            .register_type::<StyleProp<KPositionType>>()
+            .register_type::<StyleProp<RenderCommand>>()
+            .register_type::<StyleProp<i32>>();
     }
 }
 
 fn calculate_ui(world: &mut World) {
     // dbg!("Calculating nodes!");
-    let mut node_system = IntoSystem::into_system(calculate_nodes);
-    node_system.initialize(world);
 
-    let mut layout_system = IntoSystem::into_system(calculate_layout);
-    layout_system.initialize(world);
+    let mut context_data = Vec::new();
 
-    for _ in 0..3 {
-        node_system.run((), world);
-        node_system.apply_buffers(world);
+    query_world::<Query<(Entity, &mut EventDispatcher, &mut KayakRootContext)>, _, _>(
+        |mut query| {
+            for (entity, mut event_dispatcher, mut kayak_root_context) in query.iter_mut() {
+                context_data.push((
+                    entity,
+                    std::mem::take(&mut *event_dispatcher),
+                    std::mem::take(&mut *kayak_root_context),
+                ));
+            }
+        },
+        world,
+    );
 
-        layout_system.run((), world);
-        layout_system.apply_buffers(world);
-        world.resource_scope::<KayakRootContext, _>(|world, mut context| {
-            LayoutEventDispatcher::dispatch(&mut context, world);
-        });
-    }
+    for (entity, event_dispatcher, mut context) in context_data.drain(..) {
+        let mut node_system = IntoSystem::into_system(calculate_nodes);
+        node_system.initialize(world);
+        let mut layout_system = IntoSystem::into_system(calculate_layout);
+        layout_system.initialize(world);
 
-    world.resource_scope::<KayakRootContext, _>(|world, mut context| {
-        world.resource_scope::<EventDispatcher, _>(|world, event_dispatcher| {
-            if event_dispatcher.hovered.is_none() {
-                context.current_cursor = CursorIcon::Default;
-                return;
-            }
+        for _ in 0..3 {
+            context = node_system.run(context, world);
+            node_system.apply_buffers(world);
 
+            context = layout_system.run(context, world);
+            layout_system.apply_buffers(world);
+            LayoutEventDispatcher::dispatch(&mut context, world);
+        }
+
+        if event_dispatcher.hovered.is_none() {
+            context.current_cursor = CursorIcon::Default;
+        } else {
             let hovered = event_dispatcher.hovered.unwrap();
             if let Some(entity) = world.get_entity(hovered.0) {
                 if let Some(node) = entity.get::<crate::node::Node>() {
@@ -937,15 +1046,16 @@ fn calculate_ui(world: &mut World) {
                     context.current_cursor = icon.0;
                 }
             }
-        });
 
-        if let Some(ref mut windows) = world.get_resource_mut::<Windows>() {
-            if let Some(window) = windows.get_primary_mut() {
-                window.set_cursor_icon(context.current_cursor);
+            if let Some(ref mut windows) = world.get_resource_mut::<Windows>() {
+                if let Some(window) = windows.get_primary_mut() {
+                    window.set_cursor_icon(context.current_cursor);
+                }
             }
         }
-    });
 
+        world.entity_mut(entity).insert((event_dispatcher, context));
+    }
     // dbg!("Finished calculating nodes!");
 
     // dbg!("Dispatching layout events!");
@@ -954,9 +1064,17 @@ fn calculate_ui(world: &mut World) {
 
 /// A simple component that stores the type name of a widget
 /// This is used by Kayak in order to find out which systems to run.
-#[derive(Component, Debug, Clone, PartialEq, Eq)]
+#[derive(Component, Reflect, Debug, Clone, PartialEq, Eq)]
+#[reflect(Component)]
 pub struct WidgetName(pub String);
 
+impl Default for WidgetName {
+    fn default() -> Self {
+        log::warn!("You did not specify a widget name for a widget!");
+        Self("NO_NAME".to_string())
+    }
+}
+
 impl From<String> for WidgetName {
     fn from(value: String) -> Self {
         WidgetName(value)
diff --git a/src/cursor.rs b/src/cursor.rs
index 2b4539050e4b86d5e3c0aef5be631a9656073f4f..b3df333cc497d41e653d6f4e4973ea23acf92010 100644
--- a/src/cursor.rs
+++ b/src/cursor.rs
@@ -1,47 +1,49 @@
-/// Controls how the cursor interacts on a given node
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum PointerEvents {
-    /// Allow all pointer events on this node and its children
-    All,
-    /// Allow pointer events on this node but not on its children
-    SelfOnly,
-    /// Allow pointer events on this node's children but not on itself
-    ChildrenOnly,
-    /// Disallow all pointer events on this node and its children
-    None,
-}
-
-impl Default for PointerEvents {
-    fn default() -> Self {
-        Self::All
-    }
-}
-
-#[derive(Default, Debug, Copy, Clone, PartialEq)]
-pub struct CursorEvent {
-    pub pressed: bool,
-    pub just_pressed: bool,
-    pub just_released: bool,
-    pub position: (f32, f32),
-}
-
-/// An event created on scroll
-#[derive(Default, Debug, Copy, Clone, PartialEq)]
-pub struct ScrollEvent {
-    /// The amount scrolled
-    pub delta: ScrollUnit,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum ScrollUnit {
-    /// A scroll unit that goes by a "line of text"
-    Line { x: f32, y: f32 },
-    /// A scroll unit that goes by individual pixels
-    Pixel { x: f32, y: f32 },
-}
-
-impl Default for ScrollUnit {
-    fn default() -> Self {
-        ScrollUnit::Pixel { x: 0.0, y: 0.0 }
-    }
-}
+use bevy::reflect::{FromReflect, Reflect};
+
+/// Controls how the cursor interacts on a given node
+#[derive(Debug, Reflect, FromReflect, Copy, Clone, PartialEq, Eq)]
+pub enum PointerEvents {
+    /// Allow all pointer events on this node and its children
+    All,
+    /// Allow pointer events on this node but not on its children
+    SelfOnly,
+    /// Allow pointer events on this node's children but not on itself
+    ChildrenOnly,
+    /// Disallow all pointer events on this node and its children
+    None,
+}
+
+impl Default for PointerEvents {
+    fn default() -> Self {
+        Self::All
+    }
+}
+
+#[derive(Default, Debug, Copy, Clone, PartialEq)]
+pub struct CursorEvent {
+    pub pressed: bool,
+    pub just_pressed: bool,
+    pub just_released: bool,
+    pub position: (f32, f32),
+}
+
+/// An event created on scroll
+#[derive(Default, Debug, Copy, Clone, PartialEq)]
+pub struct ScrollEvent {
+    /// The amount scrolled
+    pub delta: ScrollUnit,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum ScrollUnit {
+    /// A scroll unit that goes by a "line of text"
+    Line { x: f32, y: f32 },
+    /// A scroll unit that goes by individual pixels
+    Pixel { x: f32, y: f32 },
+}
+
+impl Default for ScrollUnit {
+    fn default() -> Self {
+        ScrollUnit::Pixel { x: 0.0, y: 0.0 }
+    }
+}
diff --git a/src/event_dispatcher.rs b/src/event_dispatcher.rs
index 41d922785ceefc242192e835d033b12a545ebe5d..ac64268c891159b2d5a294f7f75ba48fba21e5ef 100644
--- a/src/event_dispatcher.rs
+++ b/src/event_dispatcher.rs
@@ -1,5 +1,5 @@
 use bevy::{
-    prelude::{Entity, KeyCode, Resource, World},
+    prelude::{Component, Entity, KeyCode, World},
     utils::{HashMap, HashSet},
 };
 
@@ -43,7 +43,7 @@ impl Default for EventState {
     }
 }
 
-#[derive(Resource, Debug, Clone)]
+#[derive(Component, Debug, Clone, Default)]
 pub struct EventDispatcher {
     is_mouse_pressed: bool,
     next_mouse_pressed: bool,
@@ -177,11 +177,11 @@ impl EventDispatcher {
     /// Process and dispatch a set of [InputEvents](crate::InputEvent)
     pub(crate) fn process_events(
         &mut self,
-        input_events: Vec<InputEvent>,
+        input_events: &Vec<InputEvent>,
         context: &mut KayakRootContext,
         world: &mut World,
     ) {
-        let events = { self.build_event_stream(&input_events, context, world) };
+        let events = { self.build_event_stream(input_events, context, world) };
         self.dispatch_events(events, context, world);
     }
 
@@ -245,6 +245,7 @@ impl EventDispatcher {
                             context.widget_state.clone(),
                             context.order_tree.clone(),
                             context.index.clone(),
+                            None,
                         );
                         node_event.run_on_change(world, widget_context);
                     }
@@ -384,36 +385,38 @@ impl EventDispatcher {
                     let (current, depth) = stack.pop().unwrap();
                     let mut enter_children = true;
 
-                    if world.entity(current.0).contains::<OnEvent>() {
-                        for input_event in input_events {
-                            // --- Process Event --- //
-                            if matches!(input_event.category(), InputEventCategory::Mouse) {
-                                // A widget's PointerEvents style will determine how it and its children are processed
-                                let pointer_events = Self::resolve_pointer_events(current, world);
-
-                                match pointer_events {
-                                    PointerEvents::All | PointerEvents::SelfOnly => {
-                                        let events = self.process_pointer_events(
-                                            input_event,
-                                            (current, depth),
-                                            &mut states,
-                                            world,
-                                            context,
-                                            false,
-                                        );
-                                        event_stream.extend(events);
-
-                                        if matches!(pointer_events, PointerEvents::SelfOnly) {
-                                            enter_children = false;
+                    if let Some(entity_ref) = world.get_entity(current.0) {
+                        if entity_ref.contains::<OnEvent>() {
+                            for input_event in input_events {
+                                // --- Process Event --- //
+                                if matches!(input_event.category(), InputEventCategory::Mouse) {
+                                    // A widget's PointerEvents style will determine how it and its children are processed
+                                    let pointer_events =
+                                        Self::resolve_pointer_events(current, world);
+
+                                    match pointer_events {
+                                        PointerEvents::All | PointerEvents::SelfOnly => {
+                                            let events = self.process_pointer_events(
+                                                input_event,
+                                                (current, depth),
+                                                &mut states,
+                                                world,
+                                                context,
+                                                false,
+                                            );
+                                            event_stream.extend(events);
+
+                                            if matches!(pointer_events, PointerEvents::SelfOnly) {
+                                                enter_children = false;
+                                            }
                                         }
+                                        PointerEvents::None => enter_children = false,
+                                        PointerEvents::ChildrenOnly => {}
                                     }
-                                    PointerEvents::None => enter_children = false,
-                                    PointerEvents::ChildrenOnly => {}
                                 }
                             }
                         }
                     }
-
                     // --- Push Children to Stack --- //
                     if enter_children {
                         if let Some(children) = node_tree.children.get(&current) {
diff --git a/src/focus_tree.rs b/src/focus_tree.rs
index 038e8c66a4b1cdfdf7140167ae8295cf93f496ec..b3dfbc066679cd5026d51183506b7e6cfc25dfc1 100644
--- a/src/focus_tree.rs
+++ b/src/focus_tree.rs
@@ -1,8 +1,12 @@
-use bevy::{prelude::Component, utils::HashMap};
+use bevy::{
+    prelude::{Component, Reflect, ReflectComponent},
+    utils::HashMap,
+};
 
 use crate::{node::WrappedIndex, prelude::Tree};
 
-#[derive(Component, Default, Clone, Copy)]
+#[derive(Component, Reflect, Default, Clone, Copy)]
+#[reflect(Component)]
 pub struct Focusable;
 
 #[derive(Debug, Default, PartialEq, Eq)]
diff --git a/src/input.rs b/src/input.rs
index cf6f5ee0da84cd770d3c05206c602f248b5105f3..c798500d9346e9d47337ac71644a160370fd8bee 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -105,16 +105,35 @@ pub(crate) fn process_events(world: &mut World) {
         world,
     );
 
-    world.resource_scope::<EventDispatcher, _>(|world, mut event_dispatcher| {
-        world.resource_scope::<KayakRootContext, _>(|world, mut context| {
-            event_dispatcher.process_events(input_events, &mut context, world);
-        });
-    });
+    // TODO: find a faster way of doing this.
+    let mut context_data = Vec::new();
+
+    query_world::<Query<(Entity, &mut EventDispatcher, &mut KayakRootContext)>, _, _>(
+        |mut query| {
+            for (entity, mut event_dispatcher, mut kayak_root_context) in query.iter_mut() {
+                context_data.push((
+                    entity,
+                    std::mem::take(&mut *event_dispatcher),
+                    std::mem::take(&mut *kayak_root_context),
+                ));
+            }
+        },
+        world,
+    );
+
+    for (entity, mut event_dispatcher, mut context) in context_data.drain(..) {
+        event_dispatcher.process_events(&input_events, &mut context, world);
+
+        world.entity_mut(entity).insert((event_dispatcher, context));
+    }
 }
 
-fn query_world<T: bevy::ecs::system::SystemParam + 'static, F, R>(mut f: F, world: &mut World) -> R
+pub(crate) fn query_world<T: bevy::ecs::system::SystemParam + 'static, F, R>(
+    f: F,
+    world: &mut World,
+) -> R
 where
-    F: FnMut(<T::Fetch as bevy::ecs::system::SystemParamFetch<'_, '_>>::Item) -> R,
+    F: FnOnce(<T::Fetch as bevy::ecs::system::SystemParamFetch<'_, '_>>::Item) -> R,
 {
     let mut system_state = bevy::ecs::system::SystemState::<T>::new(world);
     let r = {
diff --git a/src/layout.rs b/src/layout.rs
index 8f49ce57135f0da52018c11c4277e02bf73e2650..3cffb5bb5922cae2a0067882d2bfa9ff034bff81 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,13 +1,16 @@
 use std::collections::hash_map::Iter;
 use std::collections::HashMap;
 
-use bevy::prelude::{Entity, Query};
+use bevy::{
+    prelude::{Entity, Query},
+    reflect::{FromReflect, Reflect},
+};
 use morphorm::Cache;
 pub use morphorm::GeometryChanged;
 
 use crate::node::WrappedIndex;
 
-#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[derive(Debug, Reflect, FromReflect, Default, Clone, Copy, PartialEq)]
 pub struct Rect {
     pub posx: f32,
     pub posy: f32,
diff --git a/src/lib.rs b/src/lib.rs
index 541553089de76b2996129c687039bcf45ebc1c1e..3af641501c0722340bf7f21038dfb47d5c4f3947 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,6 +29,7 @@ mod widget_state;
 pub mod widgets;
 mod window_size;
 
+use context::KayakRootContext;
 pub use window_size::WindowSize;
 
 pub use camera::*;
@@ -63,3 +64,7 @@ pub mod prelude {
 }
 
 pub use focus_tree::Focusable;
+
+pub trait KayakUIPlugin {
+    fn build(&self, context: &mut KayakRootContext);
+}
diff --git a/src/node.rs b/src/node.rs
index d10ba9eb9c668b656dadc2e2847fb1b925b984f1..9c655a2613d988c6712df13a0f1fa37c8ebc1f8f 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -1,4 +1,7 @@
-use bevy::prelude::{Component, Entity, Query};
+use bevy::{
+    prelude::{Component, Entity, Query, Reflect, ReflectComponent},
+    reflect::FromReflect,
+};
 
 use crate::{
     render_primitive::RenderPrimitive,
@@ -9,7 +12,8 @@ use crate::{
 pub struct DirtyNode;
 
 /// A widget node used for building the layout tree
-#[derive(Debug, Clone, PartialEq, Component)]
+#[derive(Debug, Reflect, Clone, PartialEq, Component)]
+#[reflect(Component)]
 pub struct Node {
     /// The list of children directly under this node
     pub children: Vec<WrappedIndex>,
@@ -107,7 +111,7 @@ impl NodeBuilder {
     }
 }
 
-#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+#[derive(Debug, Reflect, FromReflect, Clone, Copy, Hash, PartialEq, Eq)]
 pub struct WrappedIndex(pub Entity);
 
 impl<'a> morphorm::Node<'a> for WrappedIndex {
@@ -117,7 +121,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.layout_type {
                 StyleProp::Default => Some(morphorm::LayoutType::default()),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::LayoutType::default()),
             };
         }
@@ -128,7 +132,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.position_type {
                 StyleProp::Default => Some(morphorm::PositionType::default()),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::PositionType::default()),
             };
         }
@@ -139,7 +143,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.width {
                 StyleProp::Default => Some(morphorm::Units::Stretch(1.0)),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Stretch(1.0)),
             };
         }
@@ -150,7 +154,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.height {
                 StyleProp::Default => Some(morphorm::Units::Stretch(1.0)),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Stretch(1.0)),
             };
         }
@@ -161,7 +165,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.min_width {
                 StyleProp::Default => Some(morphorm::Units::Pixels(0.0)),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -172,7 +176,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.min_height {
                 StyleProp::Default => Some(morphorm::Units::Pixels(0.0)),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -183,7 +187,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.max_width {
                 StyleProp::Default => Some(morphorm::Units::Auto),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -194,7 +198,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.max_height {
                 StyleProp::Default => Some(morphorm::Units::Auto),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -206,10 +210,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.left {
                 StyleProp::Default => match node.resolved_styles.offset {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.left),
+                    StyleProp::Value(prop) => Some(prop.left.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -221,10 +225,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.right {
                 StyleProp::Default => match node.resolved_styles.offset {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.right),
+                    StyleProp::Value(prop) => Some(prop.right.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -236,10 +240,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.top {
                 StyleProp::Default => match node.resolved_styles.offset {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.top),
+                    StyleProp::Value(prop) => Some(prop.top.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -251,10 +255,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.bottom {
                 StyleProp::Default => match node.resolved_styles.offset {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.bottom),
+                    StyleProp::Value(prop) => Some(prop.bottom.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -298,10 +302,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.padding_left {
                 StyleProp::Default => match node.resolved_styles.padding {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.left),
+                    StyleProp::Value(prop) => Some(prop.left.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -313,10 +317,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.padding_right {
                 StyleProp::Default => match node.resolved_styles.padding {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.right),
+                    StyleProp::Value(prop) => Some(prop.right.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -328,10 +332,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.padding_top {
                 StyleProp::Default => match node.resolved_styles.padding {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.top),
+                    StyleProp::Value(prop) => Some(prop.top.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -343,10 +347,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
             return match node.resolved_styles.padding_bottom {
                 StyleProp::Default => match node.resolved_styles.padding {
                     StyleProp::Default => Some(morphorm::Units::Auto),
-                    StyleProp::Value(prop) => Some(prop.bottom),
+                    StyleProp::Value(prop) => Some(prop.bottom.into()),
                     _ => Some(morphorm::Units::Auto),
                 },
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -357,7 +361,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.row_between {
                 StyleProp::Default => Some(morphorm::Units::Auto),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
@@ -368,7 +372,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex {
         if let Ok(node) = store.get(self.0) {
             return match node.resolved_styles.col_between {
                 StyleProp::Default => Some(morphorm::Units::Auto),
-                StyleProp::Value(prop) => Some(prop),
+                StyleProp::Value(prop) => Some(prop.into()),
                 _ => Some(morphorm::Units::Auto),
             };
         }
diff --git a/src/render/extract.rs b/src/render/extract.rs
index 8312995bceb85f40d015104cc376003d904a4af1..e0a4ded369621cd31000a147291965289020d94e 100644
--- a/src/render/extract.rs
+++ b/src/render/extract.rs
@@ -5,7 +5,7 @@ use crate::{
     styles::Corner,
 };
 use bevy::{
-    prelude::{Assets, Color, Commands, Image, Plugin, Query, Rect, Res, Vec2},
+    prelude::{Assets, Camera, Color, Commands, Entity, Image, Plugin, Query, Rect, Res, Vec2},
     render::{Extract, RenderApp, RenderStage},
     window::Windows,
 };
@@ -31,7 +31,7 @@ impl Plugin for BevyKayakUIExtractPlugin {
 
 pub fn extract(
     mut commands: Commands,
-    context: Extract<Res<KayakRootContext>>,
+    context_query: Extract<Query<(Entity, &KayakRootContext, &Camera)>>,
     fonts: Extract<Res<Assets<KayakFont>>>,
     font_mapping: Extract<Res<FontMapping>>,
     node_query: Extract<Query<&Node>>,
@@ -39,46 +39,61 @@ pub fn extract(
     images: Extract<Res<Assets<Image>>>,
     windows: Extract<Res<Windows>>,
 ) {
-    // dbg!("STARTED");
-    let render_primitives = context.build_render_primitives(&node_query, &widget_names);
-    // dbg!("FINISHED");
-
-    let dpi = if let Some(window) = windows.get_primary() {
-        window.scale_factor() as f32
-    } else {
-        1.0
-    };
-
-    // dbg!(&render_primitives);
+    let mut render_primitives = Vec::new();
+    for (entity, context, camera) in context_query.iter() {
+        let dpi = match &camera.target {
+            bevy::render::camera::RenderTarget::Window(window_id) => {
+                if let Some(window) = windows.get(*window_id) {
+                    window.scale_factor() as f32
+                } else {
+                    1.0
+                }
+            }
+            _ => 1.0,
+        };
+        let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names);
+        render_primitives.extend(new_render_primitives.drain(..).map(|r| (entity, dpi, r)));
+    }
 
     let mut extracted_quads = Vec::new();
-    for render_primitive in render_primitives {
+    for (camera_entity, dpi, render_primitive) in render_primitives {
         match render_primitive {
             RenderPrimitive::Text { .. } => {
-                let text_quads = font::extract_texts(&render_primitive, &fonts, &font_mapping, dpi);
+                let text_quads = font::extract_texts(
+                    camera_entity,
+                    &render_primitive,
+                    &fonts,
+                    &font_mapping,
+                    dpi,
+                );
                 extracted_quads.extend(text_quads);
             }
             RenderPrimitive::Image { .. } => {
-                let image_quads = image::extract_images(&render_primitive, dpi);
+                let image_quads = image::extract_images(camera_entity, &render_primitive, dpi);
                 extracted_quads.extend(image_quads);
             }
             RenderPrimitive::Quad { .. } => {
-                let quad_quads = super::quad::extract_quads(&render_primitive, 1.0);
+                let quad_quads = super::quad::extract_quads(camera_entity, &render_primitive, 1.0);
                 extracted_quads.extend(quad_quads);
             }
             RenderPrimitive::NinePatch { .. } => {
                 let nine_patch_quads =
-                    nine_patch::extract_nine_patch(&render_primitive, &images, dpi);
+                    nine_patch::extract_nine_patch(camera_entity, &render_primitive, &images, dpi);
                 extracted_quads.extend(nine_patch_quads);
             }
             RenderPrimitive::TextureAtlas { .. } => {
-                let texture_atlas_quads =
-                    texture_atlas::extract_texture_atlas(&render_primitive, &images, dpi);
+                let texture_atlas_quads = texture_atlas::extract_texture_atlas(
+                    camera_entity,
+                    &render_primitive,
+                    &images,
+                    dpi,
+                );
                 extracted_quads.extend(texture_atlas_quads);
             }
             RenderPrimitive::Clip { layout } => {
                 extracted_quads.push(ExtractQuadBundle {
                     extracted_quad: ExtractedQuad {
+                        camera_entity,
                         rect: Rect {
                             min: Vec2::new(layout.posx, layout.posy) * dpi,
                             max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height)
diff --git a/src/render/font/extract.rs b/src/render/font/extract.rs
index ffbd8ea3391773c302f16a74b8940127a959acac..e6051ccf5b7c580325259c9dc4973a65faa5676e 100644
--- a/src/render/font/extract.rs
+++ b/src/render/font/extract.rs
@@ -1,6 +1,6 @@
 use bevy::{
     math::Vec2,
-    prelude::{Assets, Rect, Res},
+    prelude::{Assets, Entity, Rect, Res},
 };
 use kayak_font::KayakFont;
 
@@ -13,6 +13,7 @@ use crate::{
 use super::font_mapping::FontMapping;
 
 pub fn extract_texts(
+    camera_entity: Entity,
     render_primitive: &RenderPrimitive,
     fonts: &Res<Assets<KayakFont>>,
     font_mapping: &Res<FontMapping>,
@@ -49,6 +50,7 @@ pub fn extract_texts(
 
         extracted_texts.push(ExtractQuadBundle {
             extracted_quad: ExtractedQuad {
+                camera_entity,
                 font_handle: Some(font_handle.clone()),
                 rect: Rect {
                     min: position,
diff --git a/src/render/font/mod.rs b/src/render/font/mod.rs
index 39515cb5e9b037184b24b7c4c797c51a3a3c88c8..86e02cb7a3fa388c1405cb3bf9ca3da4072cbb1f 100644
--- a/src/render/font/mod.rs
+++ b/src/render/font/mod.rs
@@ -1,5 +1,4 @@
-use bevy::prelude::{Assets, Plugin, Res, ResMut};
-use kayak_font::KayakFont;
+use bevy::prelude::{Added, Entity, Plugin, Query, ResMut};
 
 mod extract;
 mod font_mapping;
@@ -21,13 +20,9 @@ impl Plugin for TextRendererPlugin {
 
 fn process_loaded_fonts(
     mut font_mapping: ResMut<FontMapping>,
-    _fonts: Res<Assets<KayakFont>>,
-    context_resource: Res<KayakRootContext>,
+    context_query: Query<Entity, Added<KayakRootContext>>,
 ) {
-    // if let Some(context = context_resource.as_ref() {
-    if context_resource.is_added() {
+    for _ in context_query.iter() {
         font_mapping.mark_all_as_new();
     }
-    // font_mapping.add_loaded_to_kayak(&fonts, &context);
-    // }
 }
diff --git a/src/render/image/extract.rs b/src/render/image/extract.rs
index 0af91427e23d714a9c3d4fc74bf11c4714d61d0a..933271bd47cfe16a3ae02d2521c73d6975235516 100644
--- a/src/render/image/extract.rs
+++ b/src/render/image/extract.rs
@@ -3,9 +3,17 @@ use crate::{
     render_primitive::RenderPrimitive,
     styles::Corner,
 };
-use bevy::{math::Vec2, prelude::Rect, render::color::Color};
+use bevy::{
+    math::Vec2,
+    prelude::{Entity, Rect},
+    render::color::Color,
+};
 
-pub fn extract_images(render_command: &RenderPrimitive, _dpi: f32) -> Vec<ExtractQuadBundle> {
+pub fn extract_images(
+    camera_entity: Entity,
+    render_command: &RenderPrimitive,
+    _dpi: f32,
+) -> Vec<ExtractQuadBundle> {
     let (border_radius, layout, handle) = match render_command {
         RenderPrimitive::Image {
             border_radius,
@@ -17,6 +25,7 @@ pub fn extract_images(render_command: &RenderPrimitive, _dpi: f32) -> Vec<Extrac
 
     vec![ExtractQuadBundle {
         extracted_quad: ExtractedQuad {
+            camera_entity,
             rect: Rect {
                 min: Vec2::new(layout.posx, layout.posy),
                 max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height),
diff --git a/src/render/mod.rs b/src/render/mod.rs
index 001531bf9a14511a8d1b17fe5542f1eadd583997..67dbf1cfd342bc94164963625f34aa1027682ca9 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -1,5 +1,5 @@
 use bevy::{
-    prelude::{App, Commands, Entity, Plugin, Query, With},
+    prelude::{App, Camera, Commands, Entity, Plugin, Query, With},
     render::{
         render_graph::{RenderGraph, RunGraphOnViewNode, SlotInfo, SlotType},
         render_phase::{DrawFunctions, RenderPhase},
@@ -167,11 +167,13 @@ fn get_ui_graph(render_app: &mut App) -> RenderGraph {
 
 pub fn extract_core_pipeline_camera_phases(
     mut commands: Commands,
-    active_camera: Extract<Query<Entity, With<CameraUIKayak>>>,
+    active_cameras: Extract<Query<(Entity, &Camera), With<CameraUIKayak>>>,
 ) {
-    if let Ok(entity) = active_camera.get_single() {
-        commands
-            .get_or_spawn(entity)
-            .insert(RenderPhase::<TransparentUI>::default());
+    for (entity, camera) in &active_cameras {
+        if camera.is_active {
+            commands
+                .get_or_spawn(entity)
+                .insert(RenderPhase::<TransparentUI>::default());
+        }
     }
 }
diff --git a/src/render/nine_patch/extract.rs b/src/render/nine_patch/extract.rs
index a4d0b6356f3050ab963d8beb7a4fdb3927fb9a30..56c6f4ba1b513c071e20ff596133c5fa4f9e1a4e 100644
--- a/src/render/nine_patch/extract.rs
+++ b/src/render/nine_patch/extract.rs
@@ -5,11 +5,12 @@ use crate::{
 };
 use bevy::{
     math::Vec2,
-    prelude::{Assets, Rect, Res},
+    prelude::{Assets, Entity, Rect, Res},
     render::{color::Color, texture::Image},
 };
 
 pub fn extract_nine_patch(
+    camera_entity: Entity,
     render_primitive: &RenderPrimitive,
     images: &Res<Assets<Image>>,
     dpi: f32,
@@ -42,6 +43,7 @@ pub fn extract_nine_patch(
         * dpi;
 
     let extracted_quad_template = ExtractedQuad {
+        camera_entity,
         rect: Rect {
             min: Vec2::ZERO,
             max: Vec2::ZERO,
diff --git a/src/render/quad/extract.rs b/src/render/quad/extract.rs
index 9f5f6228383bfd064a6bb7b8b3a916466b205d9e..a5ec9a1df23c5ed1ae3f188979c4d34b484756a9 100644
--- a/src/render/quad/extract.rs
+++ b/src/render/quad/extract.rs
@@ -3,9 +3,16 @@ use crate::{
     render_primitive::RenderPrimitive,
     styles::Corner,
 };
-use bevy::{math::Vec2, prelude::Rect};
+use bevy::{
+    math::Vec2,
+    prelude::{Entity, Rect},
+};
 
-pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<ExtractQuadBundle> {
+pub fn extract_quads(
+    camera_entity: Entity,
+    render_primitive: &RenderPrimitive,
+    dpi: f32,
+) -> Vec<ExtractQuadBundle> {
     let (background_color, border_color, layout, border_radius, mut border) = match render_primitive
     {
         RenderPrimitive::Quad {
@@ -30,6 +37,7 @@ pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<Extrac
         // Border
         ExtractQuadBundle {
             extracted_quad: ExtractedQuad {
+                camera_entity,
                 rect: Rect {
                     min: Vec2::new(layout.posx, layout.posy),
                     max: Vec2::new(
@@ -57,6 +65,7 @@ pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<Extrac
         },
         ExtractQuadBundle {
             extracted_quad: ExtractedQuad {
+                camera_entity,
                 rect: Rect {
                     min: Vec2::new(layout.posx + border.left, layout.posy + border.top),
                     max: Vec2::new(
diff --git a/src/render/texture_atlas/extract.rs b/src/render/texture_atlas/extract.rs
index ebc669285be5f95ad787bce98a289e91c95d9d5c..9b1c750c9e02c0669009d890c90334ced90a1275 100644
--- a/src/render/texture_atlas/extract.rs
+++ b/src/render/texture_atlas/extract.rs
@@ -5,11 +5,12 @@ use crate::{
 };
 use bevy::{
     math::Vec2,
-    prelude::{Assets, Rect, Res},
+    prelude::{Assets, Entity, Rect, Res},
     render::{color::Color, texture::Image},
 };
 
 pub fn extract_texture_atlas(
+    camera_entity: Entity,
     render_primitive: &RenderPrimitive,
     images: &Res<Assets<Image>>,
     dpi: f32,
@@ -44,6 +45,7 @@ pub fn extract_texture_atlas(
 
     let quad = ExtractQuadBundle {
         extracted_quad: ExtractedQuad {
+            camera_entity,
             rect: Rect {
                 min: Vec2::new(layout.posx, layout.posy),
                 max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height),
diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs
index e1b8ff444900ae3c442ddfcc2c7ae382abc34666..d8a71309ee8d32b1f574106dfd0a401a57f3d9a2 100644
--- a/src/render/unified/pipeline.rs
+++ b/src/render/unified/pipeline.rs
@@ -339,6 +339,7 @@ pub enum UIQuadType {
 
 #[derive(Debug, Component, Clone)]
 pub struct ExtractedQuad {
+    pub camera_entity: Entity,
     pub rect: Rect,
     pub color: Color,
     pub vertex_index: usize,
@@ -525,7 +526,7 @@ pub fn queue_quads(
     mut pipelines: ResMut<SpecializedRenderPipelines<UnifiedPipeline>>,
     mut pipeline_cache: ResMut<PipelineCache>,
     mut extracted_sprites: Query<(Entity, &ExtractedQuad)>,
-    mut views: Query<&mut RenderPhase<TransparentUI>>,
+    mut views: Query<(Entity, &mut RenderPhase<TransparentUI>)>,
     mut image_bind_groups: ResMut<ImageBindGroups>,
     unified_pipeline: Res<UnifiedPipeline>,
     gpu_images: Res<RenderAssets<Image>>,
@@ -557,8 +558,11 @@ pub fn queue_quads(
         let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key);
 
         let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
-        for mut transparent_phase in views.iter_mut() {
+        for (camera_entity, mut transparent_phase) in views.iter_mut() {
             for (entity, quad) in extracted_sprites.iter_mut() {
+                if quad.camera_entity != camera_entity {
+                    continue;
+                }
                 if let Some(image_handle) = quad.image.as_ref() {
                     if let Some(gpu_image) = gpu_images.get(image_handle) {
                         image_bind_groups
diff --git a/src/render_primitive.rs b/src/render_primitive.rs
index 3a3d95caa8abaaaf5669ddc4233137b2e7ad4df4..9c3fa2d6820b8bc656199be97f0ffffe9d9e30aa 100644
--- a/src/render_primitive.rs
+++ b/src/render_primitive.rs
@@ -2,10 +2,13 @@ use crate::{
     layout::Rect,
     styles::{Corner, Edge, KStyle, RenderCommand},
 };
-use bevy::prelude::{Color, Handle, Image, Vec2};
+use bevy::{
+    prelude::{Color, Handle, Image, Vec2},
+    reflect::Reflect,
+};
 use kayak_font::{TextLayout, TextProperties};
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Reflect, Clone, PartialEq)]
 pub enum RenderPrimitive {
     Empty,
     Clip {
diff --git a/src/styles/corner.rs b/src/styles/corner.rs
index dfbfcdbbfeb720ca06d25819392e2e33fa379f54..fa02cdd1f73f6542bce7e84fced2185c68ab332a 100644
--- a/src/styles/corner.rs
+++ b/src/styles/corner.rs
@@ -1,234 +1,236 @@
-use std::ops::{Mul, MulAssign};
-
-/// A struct for defining properties related to the corners of widgets
-///
-/// This is useful for things like border radii, etc.
-#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
-pub struct Corner<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    /// The value of the top-left corner
-    pub top_left: T,
-    /// The value of the top-right corner
-    pub top_right: T,
-    /// The value of the bottom-left corner
-    pub bottom_left: T,
-    /// The value of the bottom-right corner
-    pub bottom_right: T,
-}
-
-impl<T> Corner<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    /// Creates a new `Corner` with values individually specified for each corner
-    ///
-    /// # Arguments
-    ///
-    /// * `top_left`: The top-left corner value
-    /// * `top_right`: The top_-right corner value
-    /// * `bottom_left`: The bottom_-left corner value
-    /// * `bottom_right`: The bottom_-right corner value
-    ///
-    pub fn new(top_left: T, top_right: T, bottom_left: T, bottom_right: T) -> Self {
-        Self {
-            top_left,
-            top_right,
-            bottom_left,
-            bottom_right,
-        }
-    }
-
-    /// Creates a new `Corner` with matching top corners and matching bottom corners
-    ///
-    /// # Arguments
-    ///
-    /// * `top`: The value of the top corners
-    /// * `bottom`: The value of the bottom corners
-    ///
-    /// ```
-    /// # use kayak_core::styles::Corner;
-    /// // Creates a `Corner` with only the top corners rounded
-    /// let corner_radius = Corner::vertical(10.0, 0.0);
-    ///
-    /// // Creates a `Corner` with only the bottom corners rounded
-    /// let corner_radius = Corner::vertical(0.0, 10.0);
-    /// ```
-    pub fn vertical(top: T, bottom: T) -> Self {
-        Self {
-            top_left: top,
-            top_right: top,
-            bottom_left: bottom,
-            bottom_right: bottom,
-        }
-    }
-
-    /// Creates a new `Corner` with matching left corners and matching right corners
-    ///
-    /// # Arguments
-    ///
-    /// * `left`: The value of the left corners
-    /// * `right`: The value of the right corners
-    ///
-    /// ```
-    /// # use kayak_core::styles::Corner;
-    /// // Creates a `Corner` with only the left corners rounded
-    /// let corner_radius = Corner::horizontal(10.0, 0.0);
-    ///
-    /// // Creates a `Corner` with only the right corners rounded
-    /// let corner_radius = Corner::horizontal(0.0, 10.0);
-    /// ```
-    pub fn horizontal(left: T, right: T) -> Self {
-        Self {
-            top_left: left,
-            top_right: right,
-            bottom_left: left,
-            bottom_right: right,
-        }
-    }
-
-    /// Creates a new `Corner` with all corners having the same value
-    ///
-    /// # Arguments
-    ///
-    /// * `value`: The value of all corners
-    ///
-    pub fn all(value: T) -> Self {
-        Self {
-            top_left: value,
-            top_right: value,
-            bottom_left: value,
-            bottom_right: value,
-        }
-    }
-
-    /// Converts this `Corner` into a tuple matching `(Top Left, Top Right, Bottom Left, Bottom Right)`
-    pub fn into_tuple(self) -> (T, T, T, T) {
-        (
-            self.top_left,
-            self.top_right,
-            self.bottom_left,
-            self.bottom_right,
-        )
-    }
-}
-
-impl<T> From<Corner<T>> for (T, T, T, T)
-where
-    T: Copy + Default + PartialEq,
-{
-    /// Creates a tuple matching the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)`
-    fn from(edge: Corner<T>) -> Self {
-        edge.into_tuple()
-    }
-}
-
-impl<T> From<T> for Corner<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    fn from(value: T) -> Self {
-        Corner::all(value)
-    }
-}
-
-impl<T> From<(T, T, T, T)> for Corner<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    /// Converts the tuple according to the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)`
-    fn from(value: (T, T, T, T)) -> Self {
-        Corner::new(value.0, value.1, value.2, value.3)
-    }
-}
-
-impl<T> Mul<T> for Corner<T>
-where
-    T: Copy + Default + PartialEq + Mul<Output = T>,
-{
-    type Output = Self;
-
-    fn mul(self, rhs: T) -> Self::Output {
-        Self {
-            top_left: self.top_left * rhs,
-            top_right: self.top_right * rhs,
-            bottom_left: self.bottom_left * rhs,
-            bottom_right: self.bottom_right * rhs,
-        }
-    }
-}
-
-impl<T> Mul<Corner<T>> for Corner<T>
-where
-    T: Copy + Default + PartialEq + Mul<Output = T>,
-{
-    type Output = Self;
-
-    fn mul(self, rhs: Corner<T>) -> Self::Output {
-        Self {
-            top_left: rhs.top_left * self.top_left,
-            top_right: rhs.top_right * self.top_right,
-            bottom_left: rhs.bottom_left * self.bottom_left,
-            bottom_right: rhs.bottom_right * self.bottom_right,
-        }
-    }
-}
-
-impl<T> MulAssign<T> for Corner<T>
-where
-    T: Copy + Default + PartialEq + MulAssign,
-{
-    fn mul_assign(&mut self, rhs: T) {
-        self.top_left *= rhs;
-        self.top_right *= rhs;
-        self.bottom_left *= rhs;
-        self.bottom_right *= rhs;
-    }
-}
-
-impl<T> MulAssign<Corner<T>> for Corner<T>
-where
-    T: Copy + Default + PartialEq + MulAssign,
-{
-    fn mul_assign(&mut self, rhs: Corner<T>) {
-        self.top_left *= rhs.top_left;
-        self.top_right *= rhs.top_right;
-        self.bottom_left *= rhs.bottom_left;
-        self.bottom_right *= rhs.bottom_right;
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::Corner;
-
-    #[test]
-    fn tuples_should_convert_to_corner() {
-        let expected = (1.0, 2.0, 3.0, 4.0);
-        let corner: Corner<f32> = expected.into();
-        assert_eq!(expected, corner.into_tuple());
-
-        let expected = (1.0, 1.0, 1.0, 1.0);
-        let corner: Corner<f32> = (expected.0).into();
-        assert_eq!(expected, corner.into_tuple());
-
-        let expected = (1.0, 1.0, 1.0, 1.0);
-        let corner: Corner<f32> = expected.0.into();
-        assert_eq!(expected, corner.into_tuple());
-    }
-
-    #[test]
-    fn multiplication_should_work_on_corners() {
-        let expected = (10.0, 20.0, 30.0, 40.0);
-        let mut corner = Corner::new(1.0, 2.0, 3.0, 4.0);
-
-        // Basic multiplication
-        let multiplied = corner * 10.0;
-        assert_eq!(expected, multiplied.into_tuple());
-
-        // Multiply and assign
-        corner *= 10.0;
-        assert_eq!(expected, corner.into_tuple());
-    }
-}
+use std::ops::{Mul, MulAssign};
+
+use bevy::reflect::{FromReflect, Reflect};
+
+/// A struct for defining properties related to the corners of widgets
+///
+/// This is useful for things like border radii, etc.
+#[derive(Debug, Default, Reflect, FromReflect, Copy, Clone, PartialEq, Eq)]
+pub struct Corner<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    /// The value of the top-left corner
+    pub top_left: T,
+    /// The value of the top-right corner
+    pub top_right: T,
+    /// The value of the bottom-left corner
+    pub bottom_left: T,
+    /// The value of the bottom-right corner
+    pub bottom_right: T,
+}
+
+impl<T> Corner<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    /// Creates a new `Corner` with values individually specified for each corner
+    ///
+    /// # Arguments
+    ///
+    /// * `top_left`: The top-left corner value
+    /// * `top_right`: The top_-right corner value
+    /// * `bottom_left`: The bottom_-left corner value
+    /// * `bottom_right`: The bottom_-right corner value
+    ///
+    pub fn new(top_left: T, top_right: T, bottom_left: T, bottom_right: T) -> Self {
+        Self {
+            top_left,
+            top_right,
+            bottom_left,
+            bottom_right,
+        }
+    }
+
+    /// Creates a new `Corner` with matching top corners and matching bottom corners
+    ///
+    /// # Arguments
+    ///
+    /// * `top`: The value of the top corners
+    /// * `bottom`: The value of the bottom corners
+    ///
+    /// ```
+    /// # use kayak_core::styles::Corner;
+    /// // Creates a `Corner` with only the top corners rounded
+    /// let corner_radius = Corner::vertical(10.0, 0.0);
+    ///
+    /// // Creates a `Corner` with only the bottom corners rounded
+    /// let corner_radius = Corner::vertical(0.0, 10.0);
+    /// ```
+    pub fn vertical(top: T, bottom: T) -> Self {
+        Self {
+            top_left: top,
+            top_right: top,
+            bottom_left: bottom,
+            bottom_right: bottom,
+        }
+    }
+
+    /// Creates a new `Corner` with matching left corners and matching right corners
+    ///
+    /// # Arguments
+    ///
+    /// * `left`: The value of the left corners
+    /// * `right`: The value of the right corners
+    ///
+    /// ```
+    /// # use kayak_core::styles::Corner;
+    /// // Creates a `Corner` with only the left corners rounded
+    /// let corner_radius = Corner::horizontal(10.0, 0.0);
+    ///
+    /// // Creates a `Corner` with only the right corners rounded
+    /// let corner_radius = Corner::horizontal(0.0, 10.0);
+    /// ```
+    pub fn horizontal(left: T, right: T) -> Self {
+        Self {
+            top_left: left,
+            top_right: right,
+            bottom_left: left,
+            bottom_right: right,
+        }
+    }
+
+    /// Creates a new `Corner` with all corners having the same value
+    ///
+    /// # Arguments
+    ///
+    /// * `value`: The value of all corners
+    ///
+    pub fn all(value: T) -> Self {
+        Self {
+            top_left: value,
+            top_right: value,
+            bottom_left: value,
+            bottom_right: value,
+        }
+    }
+
+    /// Converts this `Corner` into a tuple matching `(Top Left, Top Right, Bottom Left, Bottom Right)`
+    pub fn into_tuple(self) -> (T, T, T, T) {
+        (
+            self.top_left,
+            self.top_right,
+            self.bottom_left,
+            self.bottom_right,
+        )
+    }
+}
+
+impl<T> From<Corner<T>> for (T, T, T, T)
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    /// Creates a tuple matching the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)`
+    fn from(edge: Corner<T>) -> Self {
+        edge.into_tuple()
+    }
+}
+
+impl<T> From<T> for Corner<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    fn from(value: T) -> Self {
+        Corner::all(value)
+    }
+}
+
+impl<T> From<(T, T, T, T)> for Corner<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    /// Converts the tuple according to the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)`
+    fn from(value: (T, T, T, T)) -> Self {
+        Corner::new(value.0, value.1, value.2, value.3)
+    }
+}
+
+impl<T> Mul<T> for Corner<T>
+where
+    T: Copy + Default + PartialEq + Mul<Output = T> + Reflect,
+{
+    type Output = Self;
+
+    fn mul(self, rhs: T) -> Self::Output {
+        Self {
+            top_left: self.top_left * rhs,
+            top_right: self.top_right * rhs,
+            bottom_left: self.bottom_left * rhs,
+            bottom_right: self.bottom_right * rhs,
+        }
+    }
+}
+
+impl<T> Mul<Corner<T>> for Corner<T>
+where
+    T: Copy + Default + PartialEq + Mul<Output = T> + Reflect,
+{
+    type Output = Self;
+
+    fn mul(self, rhs: Corner<T>) -> Self::Output {
+        Self {
+            top_left: rhs.top_left * self.top_left,
+            top_right: rhs.top_right * self.top_right,
+            bottom_left: rhs.bottom_left * self.bottom_left,
+            bottom_right: rhs.bottom_right * self.bottom_right,
+        }
+    }
+}
+
+impl<T> MulAssign<T> for Corner<T>
+where
+    T: Copy + Default + PartialEq + MulAssign + Reflect,
+{
+    fn mul_assign(&mut self, rhs: T) {
+        self.top_left *= rhs;
+        self.top_right *= rhs;
+        self.bottom_left *= rhs;
+        self.bottom_right *= rhs;
+    }
+}
+
+impl<T> MulAssign<Corner<T>> for Corner<T>
+where
+    T: Copy + Default + PartialEq + MulAssign + Reflect,
+{
+    fn mul_assign(&mut self, rhs: Corner<T>) {
+        self.top_left *= rhs.top_left;
+        self.top_right *= rhs.top_right;
+        self.bottom_left *= rhs.bottom_left;
+        self.bottom_right *= rhs.bottom_right;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Corner;
+
+    #[test]
+    fn tuples_should_convert_to_corner() {
+        let expected = (1.0, 2.0, 3.0, 4.0);
+        let corner: Corner<f32> = expected.into();
+        assert_eq!(expected, corner.into_tuple());
+
+        let expected = (1.0, 1.0, 1.0, 1.0);
+        let corner: Corner<f32> = (expected.0).into();
+        assert_eq!(expected, corner.into_tuple());
+
+        let expected = (1.0, 1.0, 1.0, 1.0);
+        let corner: Corner<f32> = expected.0.into();
+        assert_eq!(expected, corner.into_tuple());
+    }
+
+    #[test]
+    fn multiplication_should_work_on_corners() {
+        let expected = (10.0, 20.0, 30.0, 40.0);
+        let mut corner = Corner::new(1.0, 2.0, 3.0, 4.0);
+
+        // Basic multiplication
+        let multiplied = corner * 10.0;
+        assert_eq!(expected, multiplied.into_tuple());
+
+        // Multiply and assign
+        corner *= 10.0;
+        assert_eq!(expected, corner.into_tuple());
+    }
+}
diff --git a/src/styles/edge.rs b/src/styles/edge.rs
index dabef3afc026612cdf66b4c9806a7f75183485b2..b11e1bc2de636b494bfbdf7e04807179ecdf784e 100644
--- a/src/styles/edge.rs
+++ b/src/styles/edge.rs
@@ -1,208 +1,210 @@
-use std::ops::{Mul, MulAssign};
-
-/// A struct for defining properties related to the edges of widgets
-///
-/// This is useful for things like borders, padding, etc.
-#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
-pub struct Edge<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    /// The value of the top edge
-    pub top: T,
-    /// The value of the right edge
-    pub right: T,
-    /// The value of the bottom edge
-    pub bottom: T,
-    /// The value of the left edge
-    pub left: T,
-}
-
-impl<T> Edge<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    /// Creates a new `Edge` with values individually specified for each edge
-    ///
-    /// # Arguments
-    ///
-    /// * `top`: The top edge value
-    /// * `right`: The right edge value
-    /// * `bottom`: The bottom edge value
-    /// * `left`: The left edge value
-    ///
-    pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
-        Self {
-            top,
-            right,
-            bottom,
-            left,
-        }
-    }
-
-    /// Creates a new `Edge` with matching vertical edges and matching horizontal edges
-    ///
-    /// # Arguments
-    ///
-    /// * `vertical`: The value of the vertical edges
-    /// * `horizontal`: The value of the horizontal edges
-    ///
-    pub fn axis(vertical: T, horizontal: T) -> Self {
-        Self {
-            top: vertical,
-            right: horizontal,
-            bottom: vertical,
-            left: horizontal,
-        }
-    }
-
-    /// Creates a new `Edge` with all edges having the same value
-    ///
-    /// # Arguments
-    ///
-    /// * `value`: The value of all edges
-    ///
-    pub fn all(value: T) -> Self {
-        Self {
-            top: value,
-            right: value,
-            bottom: value,
-            left: value,
-        }
-    }
-
-    /// Converts this `Edge` into a tuple matching `(Top, Right, Bottom, Left)`
-    pub fn into_tuple(self) -> (T, T, T, T) {
-        (self.top, self.right, self.bottom, self.left)
-    }
-}
-
-impl<T> From<Edge<T>> for (T, T, T, T)
-where
-    T: Copy + Default + PartialEq,
-{
-    fn from(edge: Edge<T>) -> Self {
-        edge.into_tuple()
-    }
-}
-
-impl<T> From<T> for Edge<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    fn from(value: T) -> Self {
-        Edge::all(value)
-    }
-}
-
-impl<T> From<(T, T)> for Edge<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    fn from(value: (T, T)) -> Self {
-        Edge::axis(value.0, value.1)
-    }
-}
-
-impl<T> From<(T, T, T, T)> for Edge<T>
-where
-    T: Copy + Default + PartialEq,
-{
-    fn from(value: (T, T, T, T)) -> Self {
-        Edge::new(value.0, value.1, value.2, value.3)
-    }
-}
-
-impl<T> Mul<T> for Edge<T>
-where
-    T: Copy + Default + PartialEq + Mul<Output = T>,
-{
-    type Output = Self;
-
-    fn mul(self, rhs: T) -> Self::Output {
-        Self {
-            top: self.top * rhs,
-            right: self.right * rhs,
-            bottom: self.bottom * rhs,
-            left: self.left * rhs,
-        }
-    }
-}
-
-impl<T> Mul<Edge<T>> for Edge<T>
-where
-    T: Copy + Default + PartialEq + Mul<Output = T>,
-{
-    type Output = Self;
-
-    fn mul(self, rhs: Edge<T>) -> Self::Output {
-        Self {
-            top: rhs.top * self.top,
-            right: rhs.right * self.right,
-            bottom: rhs.bottom * self.bottom,
-            left: rhs.left * self.left,
-        }
-    }
-}
-
-impl<T> MulAssign<T> for Edge<T>
-where
-    T: Copy + Default + PartialEq + MulAssign,
-{
-    fn mul_assign(&mut self, rhs: T) {
-        self.top *= rhs;
-        self.right *= rhs;
-        self.bottom *= rhs;
-        self.left *= rhs;
-    }
-}
-
-impl<T> MulAssign<Edge<T>> for Edge<T>
-where
-    T: Copy + Default + PartialEq + MulAssign,
-{
-    fn mul_assign(&mut self, rhs: Edge<T>) {
-        self.top *= rhs.top;
-        self.right *= rhs.right;
-        self.bottom *= rhs.bottom;
-        self.left *= rhs.left;
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::Edge;
-
-    #[test]
-    fn tuples_should_convert_to_edge() {
-        let expected = (1.0, 2.0, 3.0, 4.0);
-        let edge: Edge<f32> = expected.into();
-        assert_eq!(expected, edge.into_tuple());
-
-        let expected = (1.0, 2.0, 1.0, 2.0);
-        let edge: Edge<f32> = (expected.0, expected.1).into();
-        assert_eq!(expected, edge.into_tuple());
-
-        let expected = (1.0, 1.0, 1.0, 1.0);
-        let edge: Edge<f32> = (expected.0).into();
-        assert_eq!(expected, edge.into_tuple());
-
-        let expected = (1.0, 1.0, 1.0, 1.0);
-        let edge: Edge<f32> = expected.0.into();
-        assert_eq!(expected, edge.into_tuple());
-    }
-
-    #[test]
-    fn multiplication_should_work_on_edges() {
-        let expected = (10.0, 20.0, 30.0, 40.0);
-        let mut corner = Edge::new(1.0, 2.0, 3.0, 4.0);
-
-        // Basic multiplication
-        let multiplied = corner * 10.0;
-        assert_eq!(expected, multiplied.into_tuple());
-
-        // Multiply and assign
-        corner *= 10.0;
-        assert_eq!(expected, corner.into_tuple());
-    }
-}
+use std::ops::{Mul, MulAssign};
+
+use bevy::reflect::{FromReflect, Reflect};
+
+/// A struct for defining properties related to the edges of widgets
+///
+/// This is useful for things like borders, padding, etc.
+#[derive(Debug, Default, Reflect, FromReflect, Copy, Clone, PartialEq, Eq)]
+pub struct Edge<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    /// The value of the top edge
+    pub top: T,
+    /// The value of the right edge
+    pub right: T,
+    /// The value of the bottom edge
+    pub bottom: T,
+    /// The value of the left edge
+    pub left: T,
+}
+
+impl<T> Edge<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    /// Creates a new `Edge` with values individually specified for each edge
+    ///
+    /// # Arguments
+    ///
+    /// * `top`: The top edge value
+    /// * `right`: The right edge value
+    /// * `bottom`: The bottom edge value
+    /// * `left`: The left edge value
+    ///
+    pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
+        Self {
+            top,
+            right,
+            bottom,
+            left,
+        }
+    }
+
+    /// Creates a new `Edge` with matching vertical edges and matching horizontal edges
+    ///
+    /// # Arguments
+    ///
+    /// * `vertical`: The value of the vertical edges
+    /// * `horizontal`: The value of the horizontal edges
+    ///
+    pub fn axis(vertical: T, horizontal: T) -> Self {
+        Self {
+            top: vertical,
+            right: horizontal,
+            bottom: vertical,
+            left: horizontal,
+        }
+    }
+
+    /// Creates a new `Edge` with all edges having the same value
+    ///
+    /// # Arguments
+    ///
+    /// * `value`: The value of all edges
+    ///
+    pub fn all(value: T) -> Self {
+        Self {
+            top: value,
+            right: value,
+            bottom: value,
+            left: value,
+        }
+    }
+
+    /// Converts this `Edge` into a tuple matching `(Top, Right, Bottom, Left)`
+    pub fn into_tuple(self) -> (T, T, T, T) {
+        (self.top, self.right, self.bottom, self.left)
+    }
+}
+
+impl<T> From<Edge<T>> for (T, T, T, T)
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    fn from(edge: Edge<T>) -> Self {
+        edge.into_tuple()
+    }
+}
+
+impl<T> From<T> for Edge<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    fn from(value: T) -> Self {
+        Edge::all(value)
+    }
+}
+
+impl<T> From<(T, T)> for Edge<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    fn from(value: (T, T)) -> Self {
+        Edge::axis(value.0, value.1)
+    }
+}
+
+impl<T> From<(T, T, T, T)> for Edge<T>
+where
+    T: Copy + Default + PartialEq + Reflect,
+{
+    fn from(value: (T, T, T, T)) -> Self {
+        Edge::new(value.0, value.1, value.2, value.3)
+    }
+}
+
+impl<T> Mul<T> for Edge<T>
+where
+    T: Copy + Default + PartialEq + Mul<Output = T> + Reflect,
+{
+    type Output = Self;
+
+    fn mul(self, rhs: T) -> Self::Output {
+        Self {
+            top: self.top * rhs,
+            right: self.right * rhs,
+            bottom: self.bottom * rhs,
+            left: self.left * rhs,
+        }
+    }
+}
+
+impl<T> Mul<Edge<T>> for Edge<T>
+where
+    T: Copy + Default + PartialEq + Mul<Output = T> + Reflect,
+{
+    type Output = Self;
+
+    fn mul(self, rhs: Edge<T>) -> Self::Output {
+        Self {
+            top: rhs.top * self.top,
+            right: rhs.right * self.right,
+            bottom: rhs.bottom * self.bottom,
+            left: rhs.left * self.left,
+        }
+    }
+}
+
+impl<T> MulAssign<T> for Edge<T>
+where
+    T: Copy + Default + PartialEq + MulAssign + Reflect,
+{
+    fn mul_assign(&mut self, rhs: T) {
+        self.top *= rhs;
+        self.right *= rhs;
+        self.bottom *= rhs;
+        self.left *= rhs;
+    }
+}
+
+impl<T> MulAssign<Edge<T>> for Edge<T>
+where
+    T: Copy + Default + PartialEq + MulAssign + Reflect,
+{
+    fn mul_assign(&mut self, rhs: Edge<T>) {
+        self.top *= rhs.top;
+        self.right *= rhs.right;
+        self.bottom *= rhs.bottom;
+        self.left *= rhs.left;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Edge;
+
+    #[test]
+    fn tuples_should_convert_to_edge() {
+        let expected = (1.0, 2.0, 3.0, 4.0);
+        let edge: Edge<f32> = expected.into();
+        assert_eq!(expected, edge.into_tuple());
+
+        let expected = (1.0, 2.0, 1.0, 2.0);
+        let edge: Edge<f32> = (expected.0, expected.1).into();
+        assert_eq!(expected, edge.into_tuple());
+
+        let expected = (1.0, 1.0, 1.0, 1.0);
+        let edge: Edge<f32> = (expected.0).into();
+        assert_eq!(expected, edge.into_tuple());
+
+        let expected = (1.0, 1.0, 1.0, 1.0);
+        let edge: Edge<f32> = expected.0.into();
+        assert_eq!(expected, edge.into_tuple());
+    }
+
+    #[test]
+    fn multiplication_should_work_on_edges() {
+        let expected = (10.0, 20.0, 30.0, 40.0);
+        let mut corner = Edge::new(1.0, 2.0, 3.0, 4.0);
+
+        // Basic multiplication
+        let multiplied = corner * 10.0;
+        assert_eq!(expected, multiplied.into_tuple());
+
+        // Multiply and assign
+        corner *= 10.0;
+        assert_eq!(expected, corner.into_tuple());
+    }
+}
diff --git a/src/styles/mod.rs b/src/styles/mod.rs
index c6c91acb511bb0f6524d7a5804dfec3c386c3901..2ffda25864e0c179e0e809fc2f4d3d60982e277c 100644
--- a/src/styles/mod.rs
+++ b/src/styles/mod.rs
@@ -3,9 +3,11 @@ mod edge;
 mod options_ref;
 mod render_command;
 mod style;
+mod units;
 
 pub use corner::Corner;
 pub use edge::Edge;
 pub use options_ref::AsRefOption;
 pub use render_command::RenderCommand;
 pub use style::*;
+pub use units::*;
diff --git a/src/styles/render_command.rs b/src/styles/render_command.rs
index eafb32c58690efa6c2da550465f31c745f29d591..d5a2630214bf9738b15858a23017f631f7c9110c 100644
--- a/src/styles/render_command.rs
+++ b/src/styles/render_command.rs
@@ -1,36 +1,39 @@
-use bevy::prelude::{Handle, Image, Vec2};
-use kayak_font::Alignment;
-
-use super::Edge;
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum RenderCommand {
-    Empty,
-    /// Represents a node that has no renderable object but contributes to the layout.
-    Layout,
-    Clip,
-    Quad,
-    Text {
-        content: String,
-        alignment: Alignment,
-        word_wrap: bool,
-    },
-    Image {
-        handle: Handle<Image>,
-    },
-    TextureAtlas {
-        position: Vec2,
-        size: Vec2,
-        handle: Handle<Image>,
-    },
-    NinePatch {
-        border: Edge<f32>,
-        handle: Handle<Image>,
-    },
-}
-
-impl Default for RenderCommand {
-    fn default() -> Self {
-        Self::Empty
-    }
-}
+use bevy::{
+    prelude::{Handle, Image, Vec2},
+    reflect::{FromReflect, Reflect},
+};
+use kayak_font::Alignment;
+
+use super::Edge;
+
+#[derive(Debug, Reflect, FromReflect, Clone, PartialEq)]
+pub enum RenderCommand {
+    Empty,
+    /// Represents a node that has no renderable object but contributes to the layout.
+    Layout,
+    Clip,
+    Quad,
+    Text {
+        content: String,
+        alignment: Alignment,
+        word_wrap: bool,
+    },
+    Image {
+        handle: Handle<Image>,
+    },
+    TextureAtlas {
+        position: Vec2,
+        size: Vec2,
+        handle: Handle<Image>,
+    },
+    NinePatch {
+        border: Edge<f32>,
+        handle: Handle<Image>,
+    },
+}
+
+impl Default for RenderCommand {
+    fn default() -> Self {
+        Self::Empty
+    }
+}
diff --git a/src/styles/style.rs b/src/styles/style.rs
index 4439a2fd49a286e919c6a078d124e6365a4f1efe..70b7f8f17344d4dd1e606ca5530f1cc5c4095bbe 100644
--- a/src/styles/style.rs
+++ b/src/styles/style.rs
@@ -2,10 +2,13 @@
 
 use std::ops::Add;
 
+pub use super::units::{KPositionType, LayoutType, Units};
 use bevy::prelude::Color;
 use bevy::prelude::Component;
+use bevy::prelude::ReflectComponent;
+use bevy::reflect::FromReflect;
+use bevy::reflect::Reflect;
 use bevy::window::CursorIcon;
-pub use morphorm::{LayoutType, PositionType as KPositionType, Units};
 
 use crate::cursor::PointerEvents;
 
@@ -15,8 +18,14 @@ pub use super::Edge;
 use super::RenderCommand;
 
 /// Just a wrapper around bevy's CursorIcon so we can define a default.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct KCursorIcon(pub CursorIcon);
+#[derive(Debug, Reflect, Clone, PartialEq, Eq)]
+pub struct KCursorIcon(#[reflect(ignore)] pub CursorIcon);
+
+impl FromReflect for KCursorIcon {
+    fn from_reflect(_reflect: &dyn Reflect) -> Option<Self> {
+        None
+    }
+}
 
 impl Default for KCursorIcon {
     fn default() -> Self {
@@ -27,8 +36,8 @@ impl Default for KCursorIcon {
 /// The base container of all style properties
 ///
 /// The default value for this enum is [`StyleProp::Unset`].
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum StyleProp<T: Default + Clone> {
+#[derive(Debug, Reflect, FromReflect, Clone, PartialEq, Eq)]
+pub enum StyleProp<T: Default + Clone + Reflect + FromReflect> {
     /// This prop is unset, meaning its actual value is not determined until style resolution,
     /// wherein it will be set to the property's default value.
     ///
@@ -46,7 +55,7 @@ pub enum StyleProp<T: Default + Clone> {
 
 impl<T> Default for StyleProp<T>
 where
-    T: Default + Clone,
+    T: Default + Clone + Reflect + FromReflect,
 {
     fn default() -> Self {
         Self::Unset
@@ -55,7 +64,7 @@ where
 
 impl<T> StyleProp<T>
 where
-    T: Default + Clone,
+    T: Default + Clone + Reflect + FromReflect,
 {
     /// Resolves this style property into a concrete value.
     ///
@@ -122,7 +131,7 @@ where
     }
 }
 
-impl<T: Default + Clone> From<T> for StyleProp<T> {
+impl<T: Default + Clone + Reflect + FromReflect> From<T> for StyleProp<T> {
     fn from(value: T) -> Self {
         StyleProp::Value(value)
     }
@@ -239,7 +248,8 @@ define_styles! {
     ///   // Applied second (sets any remaining `StyleProp::Unset` fields)
     ///   .with_style(&style_b);
     /// ```
-    #[derive(Component, Debug, Default, Clone, PartialEq)]
+    #[derive(Component, Reflect, FromReflect, Debug, Default, Clone, PartialEq)]
+    #[reflect(Component)]
     pub struct KStyle {
         /// The background color of this widget
         ///
@@ -275,6 +285,7 @@ define_styles! {
         /// The spacing between child widgets along the horizontal axis
         pub col_between: StyleProp<Units>,
         /// The cursor icon to display when hovering this widget
+        #[reflect(ignore)]
         pub cursor: StyleProp<KCursorIcon>,
         /// The font name for this widget
         ///
diff --git a/src/styles/units.rs b/src/styles/units.rs
new file mode 100644
index 0000000000000000000000000000000000000000..01c563153c3db11b0b397b677c2a89f2ada5b967
--- /dev/null
+++ b/src/styles/units.rs
@@ -0,0 +1,126 @@
+use bevy::reflect::{FromReflect, Reflect};
+
+/// The layout type determines how nodes will be positioned when directed by the parent
+#[derive(Debug, FromReflect, Reflect, Clone, Copy, PartialEq)]
+pub enum LayoutType {
+    /// Stack child elements horizontally
+    Row,
+    /// Stack child elements vertically
+    Column,
+    /// Position child elements into specified rows and columns
+    Grid,
+}
+
+impl Default for LayoutType {
+    fn default() -> Self {
+        LayoutType::Column
+    }
+}
+
+impl Into<morphorm::LayoutType> for LayoutType {
+    fn into(self) -> morphorm::LayoutType {
+        match self {
+            LayoutType::Column => morphorm::LayoutType::Column,
+            LayoutType::Row => morphorm::LayoutType::Row,
+            LayoutType::Grid => morphorm::LayoutType::Grid,
+        }
+    }
+}
+
+/// The position type determines whether a node will be positioned in-line with its siblings or seperate
+#[derive(Debug, Reflect, FromReflect, Clone, Copy, PartialEq)]
+pub enum KPositionType {
+    /// Node is positioned relative to parent but ignores its siblings
+    SelfDirected,
+    /// Node is positioned relative to parent and in-line with siblings
+    ParentDirected,
+}
+
+impl Default for KPositionType {
+    fn default() -> Self {
+        KPositionType::ParentDirected
+    }
+}
+
+impl Into<morphorm::PositionType> for KPositionType {
+    fn into(self) -> morphorm::PositionType {
+        match self {
+            Self::ParentDirected => morphorm::PositionType::ParentDirected,
+            Self::SelfDirected => morphorm::PositionType::SelfDirected,
+        }
+    }
+}
+
+/// Units which describe spacing and size
+#[derive(Debug, FromReflect, Reflect, Clone, Copy, PartialEq)]
+pub enum Units {
+    /// A number of pixels
+    Pixels(f32),
+    /// A percentage of the parent dimension
+    Percentage(f32),
+    /// A factor of the remaining free space
+    Stretch(f32),
+    /// Automatically determine the value
+    Auto,
+}
+
+impl Default for Units {
+    fn default() -> Self {
+        Units::Auto
+    }
+}
+
+impl Into<morphorm::Units> for Units {
+    fn into(self) -> morphorm::Units {
+        match self {
+            Self::Pixels(value) => morphorm::Units::Pixels(value),
+            Self::Percentage(value) => morphorm::Units::Percentage(value),
+            Self::Stretch(value) => morphorm::Units::Stretch(value),
+            Self::Auto => morphorm::Units::Auto,
+        }
+    }
+}
+
+impl Units {
+    /// Converts the units to an f32 value
+    pub fn value_or(&self, parent_value: f32, auto: f32) -> f32 {
+        match self {
+            &Units::Pixels(pixels) => pixels,
+            &Units::Percentage(percentage) => (percentage / 100.0) * parent_value,
+            &Units::Stretch(_) => auto,
+            &Units::Auto => auto,
+        }
+    }
+
+    /// Returns true if the value is in pixels
+    pub fn is_pixels(&self) -> bool {
+        match self {
+            Units::Pixels(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if the value is a percentage
+    pub fn is_percentage(&self) -> bool {
+        match self {
+            Units::Percentage(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if the value is a stretch factor
+    pub fn is_stretch(&self) -> bool {
+        match self {
+            Units::Stretch(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if the value is auto
+    pub fn is_auto(&self) -> bool {
+        match self {
+            Units::Auto => true,
+            _ => false,
+        }
+    }
+}
diff --git a/src/tree.rs b/src/tree.rs
index a3b9468c1e9f7f907827a6c598fa1ede565c98c3..76469a2c4c2f7e3f724a5511d8bd8b02fff886f0 100644
--- a/src/tree.rs
+++ b/src/tree.rs
@@ -77,6 +77,11 @@ impl Tree {
             children
         } else {
             // Is root node
+            if let Some(root_node) = self.root_node {
+                if root_node == index {
+                    self.root_node = None;
+                }
+            }
             Vec::default()
         }
     }
diff --git a/src/widget_context.rs b/src/widget_context.rs
index 588e1ccd41c2abfa7179a281a02f2d98590a50f6..c6e64c770389ec4737fb9c45fd6fd9ada62f9f69 100644
--- a/src/widget_context.rs
+++ b/src/widget_context.rs
@@ -26,6 +26,7 @@ pub struct KayakWidgetContext {
     pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>,
     widget_state: WidgetState,
     order_tree: Arc<RwLock<Tree>>,
+    pub camera_entity: Option<Entity>,
 }
 
 impl KayakWidgetContext {
@@ -36,6 +37,7 @@ impl KayakWidgetContext {
         widget_state: WidgetState,
         order_tree: Arc<RwLock<Tree>>,
         index: Arc<RwLock<HashMap<Entity, usize>>>,
+        camera_entity: Option<Entity>,
     ) -> Self {
         Self {
             old_tree,
@@ -45,6 +47,7 @@ impl KayakWidgetContext {
             index,
             widget_state,
             order_tree,
+            camera_entity,
         }
     }
 
diff --git a/src/widgets/app.rs b/src/widgets/app.rs
index 7227042fdb5fbdd28598229f2346bcaaefec6087..c234a92142ae98331e46de8ec5eb3b67e720b352 100644
--- a/src/widgets/app.rs
+++ b/src/widgets/app.rs
@@ -1,12 +1,11 @@
 use bevy::prelude::*;
 use kayak_ui_macros::rsx;
-use morphorm::Units;
 
 use crate::{
     children::KChildren,
     context::WidgetName,
     prelude::KayakWidgetContext,
-    styles::{KStyle, RenderCommand, StyleProp},
+    styles::{KStyle, RenderCommand, StyleProp, Units},
     widget::{EmptyState, Widget, WidgetParam},
     CameraUIKayak,
 };
@@ -64,25 +63,20 @@ pub fn app_update(
 pub fn app_render(
     In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
     mut commands: Commands,
-    windows: Res<Windows>,
     mut query: Query<(&mut KStyle, &KChildren)>,
     camera: Query<&Camera, With<CameraUIKayak>>,
 ) -> bool {
     let (mut width, mut height) = (0.0, 0.0);
 
-    if let Ok(camera) = camera.get_single() {
-        if let Some(size) = camera.logical_viewport_size() {
-            width = size.x;
-            height = size.y;
+    if let Some(camera_entity) = widget_context.camera_entity {
+        if let Ok(camera) = camera.get(camera_entity) {
+            if let Some(size) = camera.logical_viewport_size() {
+                width = size.x;
+                height = size.y;
+            }
         }
     }
 
-    if width == 0.0 {
-        let primary_window = windows.get_primary().unwrap();
-        width = primary_window.width();
-        height = primary_window.height();
-    }
-
     if let Ok((mut app_style, children)) = query.get_mut(entity) {
         if app_style.width != StyleProp::Value(Units::Pixels(width)) {
             app_style.width = StyleProp::Value(Units::Pixels(width));
@@ -90,8 +84,8 @@ pub fn app_render(
         if app_style.height != StyleProp::Value(Units::Pixels(height)) {
             app_style.height = StyleProp::Value(Units::Pixels(height));
         }
+
         app_style.render_command = StyleProp::Value(RenderCommand::Layout);
-        // children.process(&widget_context, Some(entity));
         let parent_id = Some(entity);
         rsx! {
             <ClipBundle
diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs
index 2ab6866681c67f1dc026ec82f3323b9f1a6251a2..96825c57566eb94ac0e9a29a8733e0a8a1e310dd 100644
--- a/src/widgets/mod.rs
+++ b/src/widgets/mod.rs
@@ -78,6 +78,7 @@ use window::window_render;
 use crate::{
     context::KayakRootContext,
     widget::{widget_update, widget_update_with_context, EmptyState, Widget},
+    KayakUIPlugin,
 };
 
 use self::window_context_provider::window_context_render;
@@ -86,103 +87,106 @@ pub struct KayakWidgets;
 
 impl Plugin for KayakWidgets {
     fn build(&self, app: &mut bevy::prelude::App) {
-        app.add_startup_system_to_stage(StartupStage::PostStartup, add_widget_systems)
-            .add_system(text_box::cursor_animation_system);
+        app.add_system(text_box::cursor_animation_system);
     }
 }
 
-fn add_widget_systems(mut context: ResMut<KayakRootContext>) {
-    context.add_widget_data::<KayakApp, EmptyState>();
-    context.add_widget_data::<KButton, ButtonState>();
-    context.add_widget_data::<TextProps, EmptyState>();
-    context.add_widget_data::<KWindow, KWindowState>();
-    context.add_widget_data::<WindowContextProvider, EmptyState>();
-    context.add_widget_data::<Background, EmptyState>();
-    context.add_widget_data::<Clip, EmptyState>();
-    context.add_widget_data::<KImage, EmptyState>();
-    context.add_widget_data::<TextureAtlasProps, EmptyState>();
-    context.add_widget_data::<NinePatch, EmptyState>();
-    context.add_widget_data::<Element, EmptyState>();
-    context.add_widget_data::<ScrollBarProps, EmptyState>();
-    context.add_widget_data::<ScrollContentProps, EmptyState>();
-    context.add_widget_data::<ScrollBoxProps, EmptyState>();
-    context.add_widget_data::<ScrollContextProvider, EmptyState>();
-    context.add_widget_data::<TextBoxProps, TextBoxState>();
+pub struct KayakWidgetsContextPlugin;
 
-    context.add_widget_system(KayakApp::default().get_name(), app_update, app_render);
-    context.add_widget_system(
-        KButton::default().get_name(),
-        widget_update::<KButton, ButtonState>,
-        button_render,
-    );
-    context.add_widget_system(
-        TextProps::default().get_name(),
-        widget_update::<TextProps, EmptyState>,
-        text_render,
-    );
-    context.add_widget_system(
-        WindowContextProvider::default().get_name(),
-        widget_update::<WindowContextProvider, EmptyState>,
-        window_context_render,
-    );
-    context.add_widget_system(
-        KWindow::default().get_name(),
-        widget_update_with_context::<KWindow, KWindowState, WindowContext>,
-        window_render,
-    );
-    context.add_widget_system(
-        Background::default().get_name(),
-        widget_update::<Background, EmptyState>,
-        background_render,
-    );
-    context.add_widget_system(
-        Clip::default().get_name(),
-        widget_update::<Clip, EmptyState>,
-        clip_render,
-    );
-    context.add_widget_system(
-        KImage::default().get_name(),
-        widget_update::<KImage, EmptyState>,
-        image_render,
-    );
-    context.add_widget_system(
-        TextureAtlasProps::default().get_name(),
-        widget_update::<TextureAtlasProps, EmptyState>,
-        texture_atlas_render,
-    );
-    context.add_widget_system(
-        NinePatch::default().get_name(),
-        widget_update::<NinePatch, EmptyState>,
-        nine_patch_render,
-    );
-    context.add_widget_system(
-        Element::default().get_name(),
-        widget_update::<Element, EmptyState>,
-        element_render,
-    );
-    context.add_widget_system(
-        ScrollBarProps::default().get_name(),
-        widget_update_with_context::<ScrollBarProps, EmptyState, ScrollContext>,
-        scroll_bar_render,
-    );
-    context.add_widget_system(
-        ScrollContentProps::default().get_name(),
-        widget_update_with_context::<ScrollContentProps, EmptyState, ScrollContext>,
-        scroll_content_render,
-    );
-    context.add_widget_system(
-        ScrollBoxProps::default().get_name(),
-        widget_update_with_context::<ScrollBoxProps, EmptyState, ScrollContext>,
-        scroll_box_render,
-    );
-    context.add_widget_system(
-        ScrollContextProvider::default().get_name(),
-        widget_update::<ScrollContextProvider, EmptyState>,
-        scroll_context_render,
-    );
-    context.add_widget_system(
-        TextBoxProps::default().get_name(),
-        widget_update::<TextBoxProps, TextBoxState>,
-        text_box_render,
-    );
+impl KayakUIPlugin for KayakWidgetsContextPlugin {
+    fn build(&self, context: &mut KayakRootContext) {
+        context.add_widget_data::<KayakApp, EmptyState>();
+        context.add_widget_data::<KButton, ButtonState>();
+        context.add_widget_data::<TextProps, EmptyState>();
+        context.add_widget_data::<KWindow, KWindowState>();
+        context.add_widget_data::<WindowContextProvider, EmptyState>();
+        context.add_widget_data::<Background, EmptyState>();
+        context.add_widget_data::<Clip, EmptyState>();
+        context.add_widget_data::<KImage, EmptyState>();
+        context.add_widget_data::<TextureAtlasProps, EmptyState>();
+        context.add_widget_data::<NinePatch, EmptyState>();
+        context.add_widget_data::<Element, EmptyState>();
+        context.add_widget_data::<ScrollBarProps, EmptyState>();
+        context.add_widget_data::<ScrollContentProps, EmptyState>();
+        context.add_widget_data::<ScrollBoxProps, EmptyState>();
+        context.add_widget_data::<ScrollContextProvider, EmptyState>();
+        context.add_widget_data::<TextBoxProps, TextBoxState>();
+
+        context.add_widget_system(KayakApp::default().get_name(), app_update, app_render);
+        context.add_widget_system(
+            KButton::default().get_name(),
+            widget_update::<KButton, ButtonState>,
+            button_render,
+        );
+        context.add_widget_system(
+            TextProps::default().get_name(),
+            widget_update::<TextProps, EmptyState>,
+            text_render,
+        );
+        context.add_widget_system(
+            WindowContextProvider::default().get_name(),
+            widget_update::<WindowContextProvider, EmptyState>,
+            window_context_render,
+        );
+        context.add_widget_system(
+            KWindow::default().get_name(),
+            widget_update_with_context::<KWindow, KWindowState, WindowContext>,
+            window_render,
+        );
+        context.add_widget_system(
+            Background::default().get_name(),
+            widget_update::<Background, EmptyState>,
+            background_render,
+        );
+        context.add_widget_system(
+            Clip::default().get_name(),
+            widget_update::<Clip, EmptyState>,
+            clip_render,
+        );
+        context.add_widget_system(
+            KImage::default().get_name(),
+            widget_update::<KImage, EmptyState>,
+            image_render,
+        );
+        context.add_widget_system(
+            TextureAtlasProps::default().get_name(),
+            widget_update::<TextureAtlasProps, EmptyState>,
+            texture_atlas_render,
+        );
+        context.add_widget_system(
+            NinePatch::default().get_name(),
+            widget_update::<NinePatch, EmptyState>,
+            nine_patch_render,
+        );
+        context.add_widget_system(
+            Element::default().get_name(),
+            widget_update::<Element, EmptyState>,
+            element_render,
+        );
+        context.add_widget_system(
+            ScrollBarProps::default().get_name(),
+            widget_update_with_context::<ScrollBarProps, EmptyState, ScrollContext>,
+            scroll_bar_render,
+        );
+        context.add_widget_system(
+            ScrollContentProps::default().get_name(),
+            widget_update_with_context::<ScrollContentProps, EmptyState, ScrollContext>,
+            scroll_content_render,
+        );
+        context.add_widget_system(
+            ScrollBoxProps::default().get_name(),
+            widget_update_with_context::<ScrollBoxProps, EmptyState, ScrollContext>,
+            scroll_box_render,
+        );
+        context.add_widget_system(
+            ScrollContextProvider::default().get_name(),
+            widget_update::<ScrollContextProvider, EmptyState>,
+            scroll_context_render,
+        );
+        context.add_widget_system(
+            TextBoxProps::default().get_name(),
+            widget_update::<TextBoxProps, TextBoxState>,
+            text_box_render,
+        );
+    }
 }
diff --git a/src/widgets/text_box.rs b/src/widgets/text_box.rs
index ebb5631a8c540d2fe92a6715464d8a5ea85933c2..2ba680c34861145cffc16d7a237edff0ba6826fd 100644
--- a/src/widgets/text_box.rs
+++ b/src/widgets/text_box.rs
@@ -121,9 +121,17 @@ pub fn text_box_render(
             }
         }
 
+        let style_font = styles.font.clone();
+
         if is_different {
             if let Ok(mut state) = state_query.p1().get_mut(state_entity) {
                 state.current_value = text_box.value.clone();
+                // Update graphemes
+                set_graphemes(&mut state, &font_assets, &font_mapping, &style_font);
+
+                state.cursor_position = state.graphemes.len();
+
+                set_new_cursor_position(&mut state, &font_assets, &font_mapping, &style_font);
             }
         }
 
@@ -161,7 +169,6 @@ pub fn text_box_render(
             };
 
             let cloned_on_change = on_change.clone();
-            let style_font = styles.font.clone();
 
             *on_event = OnEvent::new(
                 move |In((event_dispatcher_context, _, mut event, _entity)): In<(
@@ -211,13 +218,20 @@ pub fn text_box_render(
                                 let cursor_pos = state.cursor_position;
                                 if is_backspace(c) {
                                     if !state.current_value.is_empty() {
-                                        // TODO: This doesn't respect graphemes!
-                                        state.current_value.remove(cursor_pos - 1);
+                                        let char_pos: usize = state.graphemes[0..cursor_pos - 1]
+                                            .iter()
+                                            .map(|g| g.len())
+                                            .sum();
+                                        state.current_value.remove(char_pos);
                                         state.cursor_position -= 1;
                                     }
                                 } else if !c.is_control() {
-                                    // TODO: This doesn't respect graphemes!
-                                    state.current_value.insert(cursor_pos, c);
+                                    let char_pos: usize = state.graphemes[0..cursor_pos]
+                                        .iter()
+                                        .map(|g| g.len())
+                                        .sum();
+                                    state.current_value.insert(char_pos, c);
+
                                     state.cursor_position += 1;
                                 }
 
@@ -380,6 +394,25 @@ fn set_graphemes(
     }
 }
 
+fn get_single_grapheme_length(
+    font_assets: &Res<Assets<KayakFont>>,
+    font_mapping: &FontMapping,
+    style_font: &StyleProp<String>,
+    text: &String,
+) -> usize {
+    let font_handle = match style_font {
+        StyleProp::Value(font) => font_mapping.get_handle(font.clone()).unwrap(),
+        _ => font_mapping.get_handle(DEFAULT_FONT.into()).unwrap(),
+    };
+
+    if let Some(font) = font_assets.get(&font_handle) {
+        let graphemes = font.get_graphemes(&text);
+        return graphemes[0].len();
+    }
+
+    0
+}
+
 fn set_new_cursor_position(
     state: &mut TextBoxState,
     font_assets: &Res<Assets<KayakFont>>,