diff --git a/Cargo.lock b/Cargo.lock
index 6952b31856aae93917848862495a449e796d2ac1..4c7aa96b1b83743d78f886d939dab68a07127c14 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -421,7 +421,6 @@ dependencies = [
  "crevice",
  "kayak_core",
  "kayak_font",
- "kayak_render_macros",
  "serde",
  "serde_json",
  "serde_path_to_error",
@@ -1923,6 +1922,7 @@ dependencies = [
 name = "kayak_widgets"
 version = "0.1.0"
 dependencies = [
+ "bevy",
  "kayak_ui",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 06240ea2d36be1d38fb3136744e291fdd7bc4ce1..fcfd8e00fdaab5a80810e6081ec4b9316f6b887b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,4 +26,4 @@ kayak_render_macros = { path = "kayak_render_macros" }
 
 [dev-dependencies]
 bevy = { git = "https://github.com/StarArawn/bevy", rev = "bcca341d696c66d0173d8b0ac7a1b23b4b9e775c" }
-kayak_widgets = { path = "kayak_widgets" }
+kayak_widgets = { path = "kayak_widgets", features = ["bevy_renderer"] }
diff --git a/bevy_kayak_ui/Cargo.toml b/bevy_kayak_ui/Cargo.toml
index 1b1d89f6e943368084cf63593dc22395185749c9..e0bf20227966435777b8b9a093e33bdcd608f0f1 100644
--- a/bevy_kayak_ui/Cargo.toml
+++ b/bevy_kayak_ui/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
 bytemuck = "1.7.2"
 bevy = { git = "https://github.com/StarArawn/bevy", rev = "bcca341d696c66d0173d8b0ac7a1b23b4b9e775c" }
 kayak_core = { path = "../kayak_core" }
-kayak_render_macros = { path = "../kayak_render_macros" }
 kayak_font = { path = "../kayak_font" }
 #kayak_font = { path = "../kayak_font" }
 crevice = { git = "https://github.com/StarArawn/bevy", rev = "bcca341d696c66d0173d8b0ac7a1b23b4b9e775c" }
diff --git a/bevy_kayak_ui/src/bevy_context.rs b/bevy_kayak_ui/src/bevy_context.rs
index 5eb9cfe732618bfef4450c4c14a44fb4a3a7e7a8..847d6c8e53a39c83aafc3909df98bf54beedd189 100644
--- a/bevy_kayak_ui/src/bevy_context.rs
+++ b/bevy_kayak_ui/src/bevy_context.rs
@@ -1,28 +1,17 @@
 use std::sync::{Arc, RwLock};
 
-use kayak_core::{
-    context::KayakContext,
-    render_command::RenderCommand,
-    styles::{Style, StyleProp, Units},
-};
+use kayak_core::context::KayakContext;
 
 pub struct BevyContext {
     pub kayak_context: Arc<RwLock<KayakContext>>,
 }
 
 impl BevyContext {
-    pub fn new<F: Fn(&mut Style, &mut KayakContext)>(width: f32, height: f32, f: F) -> Self {
-        let mut app_styles = Style {
-            render_command: StyleProp::Value(RenderCommand::Window),
-            width: StyleProp::Value(Units::Pixels(width)),
-            height: StyleProp::Value(Units::Pixels(height)),
-            ..Style::default()
-        };
-
+    pub fn new<F: Fn(&mut KayakContext)>(f: F) -> Self {
         let kayak_context = Arc::new(RwLock::new(KayakContext::new()));
 
         if let Ok(mut kayak_context) = kayak_context.write() {
-            f(&mut app_styles, &mut kayak_context);
+            f(&mut kayak_context);
             kayak_context.widget_manager.dirty(true);
         }
 
diff --git a/bevy_kayak_ui/src/lib.rs b/bevy_kayak_ui/src/lib.rs
index 9fe1162e19277e95b4cb2938715876b67bc8becb..16511b328c2cf152460c6891c9f4a853699f9403 100644
--- a/bevy_kayak_ui/src/lib.rs
+++ b/bevy_kayak_ui/src/lib.rs
@@ -3,7 +3,7 @@ use bevy::{
     math::Vec2,
     prelude::{EventReader, IntoExclusiveSystem, MouseButton, Plugin, Res, World},
     render::color::Color,
-    window::{CursorMoved, Windows},
+    window::{CursorMoved, WindowCreated, WindowResized, Windows},
 };
 
 mod bevy_context;
@@ -12,7 +12,7 @@ mod render;
 
 pub use bevy_context::BevyContext;
 pub use camera::*;
-use kayak_core::InputEvent;
+use kayak_core::{bind, Binding, InputEvent, MutableBound};
 pub use render::unified::font::FontMapping;
 pub use render::unified::image::ImageManager;
 
@@ -21,8 +21,10 @@ pub struct BevyKayakUIPlugin;
 
 impl Plugin for BevyKayakUIPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
-        app.add_plugin(render::BevyKayakUIRenderPlugin)
+        app.insert_resource(bind(WindowSize::default()))
+            .add_plugin(render::BevyKayakUIRenderPlugin)
             .add_plugin(camera::KayakUICameraPlugin)
+            .add_system(update_window_size)
             .add_system(process_events)
             .add_system(update.exclusive_system());
     }
@@ -79,3 +81,43 @@ pub fn process_events(
         context.process_events(input_events);
     }
 }
+
+/// Tracks the bevy window size.
+#[derive(Default, Debug, Clone, Copy, PartialEq)]
+pub struct WindowSize(pub f32, pub f32);
+
+fn update_window_size(
+    mut window_resized_events: EventReader<WindowResized>,
+    mut window_created_events: EventReader<WindowCreated>,
+    windows: Res<Windows>,
+    window_size: Res<Binding<WindowSize>>,
+) {
+    let mut changed_window_ids = Vec::new();
+    // handle resize events. latest events are handled first because we only want to resize each
+    // window once
+    for event in window_resized_events.iter().rev() {
+        if changed_window_ids.contains(&event.id) {
+            continue;
+        }
+
+        changed_window_ids.push(event.id);
+    }
+
+    // handle resize events. latest events are handled first because we only want to resize each
+    // window once
+    for event in window_created_events.iter().rev() {
+        if changed_window_ids.contains(&event.id) {
+            continue;
+        }
+
+        changed_window_ids.push(event.id);
+    }
+
+    for window_id in changed_window_ids {
+        if let Some(window) = windows.get(window_id) {
+            let width = window.width();
+            let height = window.height();
+            window_size.set(WindowSize(width, height));
+        }
+    }
+}
diff --git a/bevy_kayak_ui/src/render/unified/mod.rs b/bevy_kayak_ui/src/render/unified/mod.rs
index a88be611c5ae1bc90ca23feadbe7d33e16ab6f47..1c8da6a231580b38aecb97d6bdafbd59c05695f2 100644
--- a/bevy_kayak_ui/src/render/unified/mod.rs
+++ b/bevy_kayak_ui/src/render/unified/mod.rs
@@ -8,7 +8,7 @@ use bevy::{
     },
     sprite::Rect,
 };
-use kayak_core::render_primitive::RenderPrimitive;
+use kayak_core::{render_primitive::RenderPrimitive, Binding, Bound};
 use kayak_font::KayakFont;
 
 use crate::{
@@ -16,7 +16,7 @@ use crate::{
         ui_pass::TransparentUI,
         unified::pipeline::{DrawUI, QuadMeta, UnifiedPipeline},
     },
-    BevyContext, FontMapping, ImageManager,
+    BevyContext, FontMapping, ImageManager, WindowSize,
 };
 
 use self::pipeline::{ExtractQuadBundle, ExtractedQuad, ImageBindGroups, UIQuadType};
@@ -68,6 +68,7 @@ pub fn extract(
     font_mapping: Res<FontMapping>,
     image_manager: Res<ImageManager>,
     images: Res<Assets<Image>>,
+    window_size: Res<Binding<WindowSize>>,
 ) {
     let render_primitives = if let Ok(context) = context.kayak_context.read() {
         context.widget_manager.build_render_primitives()
@@ -75,6 +76,8 @@ pub fn extract(
         vec![]
     };
 
+    // dbg!(&render_primitives);
+
     let mut extracted_quads = Vec::new();
     for render_primitive in render_primitives {
         match render_primitive {
@@ -120,5 +123,6 @@ pub fn extract(
         }
     }
 
+    commands.insert_resource(window_size.get());
     commands.spawn_batch(extracted_quads);
 }
diff --git a/bevy_kayak_ui/src/render/unified/pipeline.rs b/bevy_kayak_ui/src/render/unified/pipeline.rs
index cbc8651609bfabd318ed9eed39a46737dceaa2c0..b09888438c059a0e263810087da7e7aaca6c6573 100644
--- a/bevy_kayak_ui/src/render/unified/pipeline.rs
+++ b/bevy_kayak_ui/src/render/unified/pipeline.rs
@@ -34,7 +34,7 @@ use crevice::std140::AsStd140;
 use kayak_font::{FontRenderingPipeline, FontTextureCache, KayakFont};
 
 use super::UNIFIED_SHADER_HANDLE;
-use crate::render::ui_pass::TransparentUI;
+use crate::{render::ui_pass::TransparentUI, WindowSize};
 
 pub struct UnifiedPipeline {
     view_layout: BindGroupLayout,
@@ -547,6 +547,7 @@ pub struct DrawUI {
         SRes<RenderPipelineCache>,
         SRes<FontTextureCache>,
         SRes<ImageBindGroups>,
+        SRes<WindowSize>,
         SQuery<Read<ViewUniformOffset>>,
         SQuery<Read<ExtractedQuad>>,
     )>,
@@ -574,6 +575,7 @@ impl Draw<TransparentUI> for DrawUI {
             pipelines,
             font_texture_cache,
             image_bind_groups,
+            window_size,
             views,
             quads,
         ) = self.params.get(world);
@@ -585,8 +587,13 @@ impl Draw<TransparentUI> for DrawUI {
         if extracted_quad.quad_type == UIQuadType::Clip {
             let x = extracted_quad.rect.min.x as u32;
             let y = extracted_quad.rect.min.y as u32;
-            let width = extracted_quad.rect.width() as u32;
-            let height = extracted_quad.rect.height() as u32;
+            let mut width = extracted_quad.rect.width() as u32;
+            let mut height = extracted_quad.rect.height() as u32;
+            width = width.min(window_size.0 as u32);
+            height = height.min(window_size.1 as u32);
+            if width == 0 || height == 0 {
+                return;
+            }
             pass.set_scissor_rect(x, y, width, height);
             return;
         }
diff --git a/examples/bevy.rs b/examples/bevy.rs
index 6f7ff8f9a1bfacb933ee58c3fd11a771b3407e75..b15a55082dd16854c37c1f9d11d9b3ede2be9199 100644
--- a/examples/bevy.rs
+++ b/examples/bevy.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
@@ -25,7 +24,6 @@ fn CustomWidget() {
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
@@ -33,15 +31,9 @@ fn startup(
 
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <CustomWidget/>
             </App>
         }
diff --git a/examples/clipping.rs b/examples/clipping.rs
index d33210b7f2cadfd03bc76ea6b2957f22317702b2..84d279635579f51abf8326e2a87e30af0fbac8ba 100644
--- a/examples/clipping.rs
+++ b/examples/clipping.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, ImageManager, UICameraBundle};
@@ -15,25 +14,18 @@ use kayak_widgets::{App, Clip, NinePatch, Text};
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     asset_server: Res<AssetServer>,
     mut image_manager: ResMut<ImageManager>,
     mut font_mapping: ResMut<FontMapping>,
 ) {
     commands.spawn_bundle(UICameraBundle::new());
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
     let handle: Handle<bevy::render::texture::Image> = asset_server.load("kenny/panel_brown.png");
     let panel_brown_handle = image_manager.get(&handle);
 
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         let nine_patch_styles = Style {
             width: StyleProp::Value(Units::Pixels(512.0)),
             height: StyleProp::Value(Units::Pixels(512.0)),
@@ -69,7 +61,7 @@ Vestibulum rutrum imperdiet nisl, et consequat massa porttitor vel. Ut velit jus
         "#.to_string();
 
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <NinePatch
                     styles={Some(nine_patch_styles)}
                     border={Space {
diff --git a/examples/counter.rs b/examples/counter.rs
index 8cc9258fcaebfd658185374c643f0d494025a21d..a443d5c5a54d6b1e2eb5d4524975c59750332fb1 100644
--- a/examples/counter.rs
+++ b/examples/counter.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
@@ -58,7 +57,6 @@ fn Counter(context: &mut KayakContext) {
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
@@ -66,15 +64,9 @@ fn startup(
 
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <Counter />
             </App>
         }
diff --git a/examples/full_ui.rs b/examples/full_ui.rs
index 141c5e1cdfe221e71beb6fec700047af67c5274e..9295e4e6d3a3086fb60d39667f148e32a212c530 100644
--- a/examples/full_ui.rs
+++ b/examples/full_ui.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut, World},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, ImageManager, UICameraBundle};
@@ -82,25 +81,18 @@ fn BlueButton(context: KayakContext, children: Children, styles: Option<Style>)
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     asset_server: Res<AssetServer>,
     mut image_manager: ResMut<ImageManager>,
     mut font_mapping: ResMut<FontMapping>,
 ) {
     commands.spawn_bundle(UICameraBundle::new());
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
     let handle: Handle<bevy::render::texture::Image> = asset_server.load("kenny/panel_brown.png");
     let panel_brown_handle = image_manager.get(&handle);
 
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         let nine_patch_styles = Style {
             layout_type: StyleProp::Value(LayoutType::Column),
             width: StyleProp::Value(Units::Pixels(512.0)),
@@ -117,7 +109,7 @@ fn startup(
             padding_right: StyleProp::Value(Units::Stretch(1.0)),
             padding_top: StyleProp::Value(Units::Stretch(1.0)),
             padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
-            ..styles.clone()
+            ..Style::default()
         };
 
         let header_styles = Style {
diff --git a/examples/global_counter.rs b/examples/global_counter.rs
index 6a0691dce00d55fba538bbca5955cfdb556e9895..e6f0ee7cf0ee7dc38bfaacfbba9a07145bc479a7 100644
--- a/examples/global_counter.rs
+++ b/examples/global_counter.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut, World},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
@@ -40,7 +39,6 @@ fn Counter(context: &mut KayakContext) {
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
@@ -48,17 +46,11 @@ fn startup(
 
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
     commands.insert_resource(bind(GlobalCount(0)));
 
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <Counter />
             </App>
         }
diff --git a/examples/if.rs b/examples/if.rs
index cb57cb2028da689a7dbbce3aeb6c68536c4761cf..2fb918b45d45a271fad0b008b695df8e57ef5f7d 100644
--- a/examples/if.rs
+++ b/examples/if.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
@@ -60,7 +59,6 @@ fn Removal(context: &mut KayakContext) {
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
@@ -68,15 +66,9 @@ fn startup(
 
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <Removal />
             </App>
         }
diff --git a/examples/image.rs b/examples/image.rs
index e43484da180f1f00d0c5b274a5a05b8d6324e47d..e8ca07f45e7299dd9904d4c32c0003d2e4f2b1d3 100644
--- a/examples/image.rs
+++ b/examples/image.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
@@ -14,22 +13,15 @@ use kayak_widgets::{App, Image};
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     asset_server: Res<AssetServer>,
     mut image_manager: ResMut<ImageManager>,
 ) {
     commands.spawn_bundle(UICameraBundle::new());
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
     let handle: Handle<bevy::render::texture::Image> = asset_server.load("generic-rpg-vendor.png");
     let ui_image_handle = image_manager.get(&handle);
 
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         let image_styles = Style {
             width: StyleProp::Value(Units::Pixels(200.0)),
             height: StyleProp::Value(Units::Pixels(182.0)),
@@ -37,7 +29,7 @@ fn startup(
         };
 
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <Image styles={Some(image_styles)} handle={ui_image_handle} />
             </App>
         }
diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs
index f608f7de1eb7c326741cf32c5bb28dba9c486d2f..0262434e435e409ac46bf77962e389f85f621cf7 100644
--- a/examples/nine_patch.rs
+++ b/examples/nine_patch.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
@@ -15,22 +14,15 @@ use kayak_widgets::{App, NinePatch};
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     asset_server: Res<AssetServer>,
     mut image_manager: ResMut<ImageManager>,
 ) {
     commands.spawn_bundle(UICameraBundle::new());
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
     let handle: Handle<bevy::render::texture::Image> = asset_server.load("panel.png");
     let ui_image_handle = image_manager.get(&handle);
 
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         // The border prop splits up the image into 9 quadrants like so:
         // 1----2----3
         // |         |
@@ -61,7 +53,7 @@ fn startup(
         };
 
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 <NinePatch
                     styles={Some(nine_patch_styles)}
                     border={Space {
diff --git a/examples/vec_widget.rs b/examples/vec_widget.rs
index 9c2d7de249f94d6d02f8c7393982bf88a610f479..03d5f2f82de9297e6fb9775581283190a994eb02 100644
--- a/examples/vec_widget.rs
+++ b/examples/vec_widget.rs
@@ -1,7 +1,6 @@
 use bevy::{
-    math::Vec2,
     prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
-    window::{WindowDescriptor, Windows},
+    window::WindowDescriptor,
     DefaultPlugins,
 };
 use kayak_core::constructor;
@@ -11,7 +10,6 @@ use kayak_widgets::{App, Text};
 
 fn startup(
     mut commands: Commands,
-    windows: Res<Windows>,
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
@@ -19,16 +17,10 @@ fn startup(
 
     font_mapping.add(asset_server.load("roboto.kayak_font"));
 
-    let window_size = if let Some(window) = windows.get_primary() {
-        Vec2::new(window.width(), window.height())
-    } else {
-        panic!("Couldn't find primary window!");
-    };
-
-    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+    let context = BevyContext::new(|context| {
         let data = vec!["Text1", "Text2", "Text3", "Text4"];
         render! {
-            <App styles={Some(styles.clone())}>
+            <App>
                 {VecTracker::from(data.iter().map(|data| {
                     constructor! {
                         <Text content={data.clone().to_string()} size={16.0} />
diff --git a/kayak_core/src/context.rs b/kayak_core/src/context.rs
index f75ddb7619560185f40bba90d178b0b34c7878b0..19300e25d7d55a3c27cdfea62b6ebb8a5f95e37c 100644
--- a/kayak_core/src/context.rs
+++ b/kayak_core/src/context.rs
@@ -209,16 +209,10 @@ impl KayakContext {
                 panic!("Couldn't get lock on dirty nodes!")
             };
         for node_index in dirty_nodes {
-            // if self
-            //     .widget_manager
-            //     .dirty_nodes
-            //     .iter()
-            //     .any(|dirty_index| node_index == *dirty_index)
-            // {
             let mut widget = self.widget_manager.take(node_index);
             widget.render(self);
             self.widget_manager.repossess(widget);
-            // }
+            self.widget_manager.dirty_render_nodes.insert(node_index);
         }
 
         // self.widget_manager.dirty_nodes.clear();
diff --git a/kayak_core/src/widget_manager.rs b/kayak_core/src/widget_manager.rs
index 3237ba076703341a5a9b75193f17f40b1919bb5e..3d9e1c321443f8fe0d61ea7f726370682faeae4d 100644
--- a/kayak_core/src/widget_manager.rs
+++ b/kayak_core/src/widget_manager.rs
@@ -17,7 +17,7 @@ use crate::{
 #[derive(Debug)]
 pub struct WidgetManager {
     pub(crate) current_widgets: Arena<Option<Box<dyn Widget>>>,
-    pub(crate) dirty_render_nodes: Vec<Index>,
+    pub(crate) dirty_render_nodes: HashSet<Index>,
     pub(crate) dirty_nodes: Arc<Mutex<HashSet<Index>>>,
     pub(crate) nodes: Arena<Option<Node>>,
     pub tree: Tree,
@@ -30,7 +30,7 @@ impl WidgetManager {
     pub fn new() -> Self {
         Self {
             current_widgets: Arena::new(),
-            dirty_render_nodes: Vec::new(),
+            dirty_render_nodes: HashSet::new(),
             dirty_nodes: Arc::new(Mutex::new(HashSet::new())),
             nodes: Arena::new(),
             tree: Tree::default(),
@@ -51,7 +51,7 @@ impl WidgetManager {
             if force {
                 for (node_index, _) in self.current_widgets.iter() {
                     dirty_nodes.insert(node_index);
-                    self.dirty_render_nodes.push(node_index);
+                    self.dirty_render_nodes.insert(node_index);
                 }
             }
         }
@@ -86,7 +86,7 @@ impl WidgetManager {
                     let boxed_widget: Box<dyn Widget> = Box::new(widget);
                     *self.current_widgets[*widget_id].as_mut().unwrap() = boxed_widget;
                     // Tell renderer that the nodes changed.
-                    self.dirty_render_nodes.push(*widget_id);
+                    self.dirty_render_nodes.insert(*widget_id);
                     return (true, *widget_id);
                     // } else {
                     //     return (false, *widget_id);
@@ -106,7 +106,7 @@ impl WidgetManager {
             .set_id(widget_id);
 
         // Tell renderer that the nodes changed.
-        self.dirty_render_nodes.push(widget_id);
+        self.dirty_render_nodes.insert(widget_id);
 
         // Remove from the dirty nodes lists.
         // if let Some(index) = self.dirty_nodes.iter().position(|id| widget_id == *id) {
@@ -148,7 +148,7 @@ impl WidgetManager {
             width: crate::styles::StyleProp::Default,
             ..Style::default()
         };
-        for dirty_node_index in self.dirty_render_nodes.drain(..) {
+        for dirty_node_index in self.dirty_render_nodes.drain() {
             let dirty_widget = self.current_widgets[dirty_node_index].as_ref().unwrap();
             let parent_styles =
                 if let Some(parent_widget_id) = self.tree.parents.get(&dirty_node_index) {
@@ -198,6 +198,7 @@ impl WidgetManager {
                 .cloned()
                 .unwrap_or(vec![]);
             let styles = styles.unwrap_or(default_styles.clone());
+
             if matches!(styles.render_command.resolve(), RenderCommand::Empty) {
                 continue;
             }
diff --git a/kayak_widgets/Cargo.toml b/kayak_widgets/Cargo.toml
index 1f4158e6d2ac69130ae8adbc6288a65024f1987b..a1c7814859b98717503b005bfc47b951ad883195 100644
--- a/kayak_widgets/Cargo.toml
+++ b/kayak_widgets/Cargo.toml
@@ -4,5 +4,10 @@ version = "0.1.0"
 edition = "2021"
 resolver = "2"
 
+[features]
+default = []
+bevy_renderer = ["bevy", "kayak_ui/bevy_renderer"]
+
 [dependencies]
 kayak_ui = { path = "../", version = "0.1.0" }
+bevy = { git = "https://github.com/StarArawn/bevy", rev = "bcca341d696c66d0173d8b0ac7a1b23b4b9e775c", optional = true }
diff --git a/kayak_widgets/src/app.rs b/kayak_widgets/src/app.rs
index 0ff42b42141a9dab9d846945a8537d0966c551c4..a77f39dce391a987130c28dced02a2392750ea4f 100644
--- a/kayak_widgets/src/app.rs
+++ b/kayak_widgets/src/app.rs
@@ -1,10 +1,42 @@
+use kayak_ui::bevy::WindowSize;
 use kayak_ui::core::derivative::*;
-use kayak_ui::core::{rsx, widget, Children};
+use kayak_ui::core::{
+    render_command::RenderCommand,
+    rsx,
+    styles::{Style, StyleProp, Units},
+    widget, Binding, Bound, Children,
+};
 
 use crate::Clip;
 
 #[widget]
 pub fn App(children: Children) {
+    *styles = Some(Style {
+        render_command: StyleProp::Value(RenderCommand::Window),
+        ..styles.clone().unwrap_or_default()
+    });
+
+    #[cfg(feature = "bevy_renderer")]
+    {
+        let window_size = if let Ok(world) = context.get_global_state::<bevy::prelude::World>() {
+            if let Some(window_size) = world.get_resource::<Binding<WindowSize>>() {
+                window_size.clone()
+            } else {
+                return;
+            }
+        } else {
+            return;
+        };
+
+        context.bind(&window_size);
+        let window_size = window_size.get();
+        *styles = Some(Style {
+            width: StyleProp::Value(Units::Pixels(window_size.0)),
+            height: StyleProp::Value(Units::Pixels(window_size.1)),
+            ..styles.clone().unwrap_or_default()
+        });
+    }
+
     rsx! {
         <Clip>
             {children}