diff --git a/examples/fold.rs b/examples/fold.rs
index 3158dcc33bec4bfbb689e317c7a42b862a2bd237..f88ed605f289f04e5b167526f380aae01b5214b1 100644
--- a/examples/fold.rs
+++ b/examples/fold.rs
@@ -7,15 +7,18 @@ use bevy::{
 use kayak_ui::{
     bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle},
     core::{
-        render, rsx,
+        render, rsx, WidgetProps,
         styles::{Style, StyleProp, Units},
         use_state, widget, Color, EventType, Handler, Index, OnEvent,
     },
     widgets::{App, Background, Button, Fold, If, Text, Window},
 };
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct FolderTreeProps {}
+
 #[widget]
-fn FolderTree(context: &mut KayakContext) {
+fn FolderTree(props: FolderTreeProps) {
     let button_text_styles = Style {
         width: StyleProp::Value(Units::Stretch(1.0)),
         height: StyleProp::Value(Units::Pixels(22.0)),
diff --git a/examples/full_ui.rs b/examples/full_ui.rs
index f40962a246239ef598edba533dfe89ce76a6540e..2208c49bbbd276d52166470917972796b2a101e3 100644
--- a/examples/full_ui.rs
+++ b/examples/full_ui.rs
@@ -6,14 +6,22 @@ use bevy::{
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, ImageManager, UICameraBundle};
 use kayak_ui::core::{
     layout_cache::Space,
-    render, rsx,
+    render, rsx, WidgetProps,
     styles::{LayoutType, Style, StyleProp, Units},
     widget, Bound, Children, EventType, Index, MutableBound, OnEvent,
 };
 use kayak_ui::widgets::{App, NinePatch, Text};
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct BlueButtonProps {
+    #[prop_field(Styles)]
+    styles: Option<Style>,
+    #[prop_field(Children)]
+    children: Option<Children>
+}
+
 #[widget]
-fn BlueButton(context: KayakContext, children: Children, styles: Option<Style>) {
+fn BlueButton(props: BlueButtonProps) {
     let (blue_button_handle, blue_button_hover_handle) = {
         let world = context.get_global_state::<World>();
         if world.is_err() {
@@ -48,7 +56,7 @@ fn BlueButton(context: KayakContext, children: Children, styles: Option<Style>)
         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().unwrap_or_default()
+        ..props.styles.clone().unwrap_or_default()
     };
 
     let cloned_current_button_handle = current_button_handle.clone();
@@ -62,6 +70,7 @@ fn BlueButton(context: KayakContext, children: Children, styles: Option<Style>)
         _ => (),
     });
 
+    let children = props.get_children();
     rsx! {
         <NinePatch
             border={Space {
diff --git a/examples/global_counter.rs b/examples/global_counter.rs
index ac6642bc3acd963253d37399ae5bcdbf8f581701..e2fbb7e08b8af178be03c26127a4959d4572180e 100644
--- a/examples/global_counter.rs
+++ b/examples/global_counter.rs
@@ -4,14 +4,17 @@ use bevy::{
     DefaultPlugins,
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
-use kayak_ui::core::{bind, render, rsx, widget, Binding, Bound, Index, MutableBound};
+use kayak_ui::core::{bind, render, rsx, widget, WidgetProps, Binding, Bound, Index, MutableBound};
 use kayak_ui::widgets::{App, Text, Window};
 
 #[derive(Clone, PartialEq)]
 struct GlobalCount(pub u32);
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct CounterProps {}
+
 #[widget]
-fn Counter(context: &mut KayakContext) {
+fn Counter(props: CounterProps) {
     let global_count = context
         .query_world::<Res<Binding<GlobalCount>>, _, _>(move |global_count| global_count.clone());
 
diff --git a/examples/hooks.rs b/examples/hooks.rs
index d4a25335917f80914decbe4508b6b4a503c98582..a85973de04eb64fd6979397224b5ecb29313f7e5 100644
--- a/examples/hooks.rs
+++ b/examples/hooks.rs
@@ -16,13 +16,16 @@ use bevy::{
 };
 use kayak_ui::{
     bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle},
-    core::{render, rsx, use_effect, use_state, widget, EventType, Index, OnEvent},
+    core::{render, rsx, use_effect, use_state, widget, WidgetProps, EventType, Index, OnEvent},
     widgets::{App, Button, Text, Window},
 };
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct StateCounterProps {}
+
 /// A simple widget that tracks how many times a button is clicked using simple state data
 #[widget]
-fn StateCounter() {
+fn StateCounter(props: StateCounterProps) {
     // On its own, a widget can't track anything, since every value will just be reset when the widget is re-rendered.
     // To get around this, and keep track of a value, we have to use states. States are values that are kept across renders.
     // Additionally, anytime a state is updated with a new value, it causes the containing widget to re-render, making it
@@ -53,9 +56,12 @@ fn StateCounter() {
     }
 }
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct EffectCounterProps {}
+
 /// Another widget that tracks how many times a button is clicked using side-effects
 #[widget]
-fn EffectCounter() {
+fn EffectCounter(props: EffectCounterProps) {
     // In this widget, we're going to implement another counter, but this time using side-effects.
     // To put it very simply, a side-effect is when something happens in response to something else happening.
     // In our case, we want to create a side-effect that updates a counter when another state is updated.
diff --git a/examples/if.rs b/examples/if.rs
index 326751161cd79a2379a16c544a8876df3a24e4f9..7120ce9f709dadf5534f60e1b20a49db18e16684 100644
--- a/examples/if.rs
+++ b/examples/if.rs
@@ -5,14 +5,17 @@ use bevy::{
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
 use kayak_ui::core::{
-    render, rsx,
+    render, rsx, WidgetProps,
     styles::{Style, StyleProp, Units},
     widget, Bound, EventType, Index, MutableBound, OnEvent,
 };
 use kayak_ui::widgets::{App, Button, If, Text, Window};
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct RemovalProps {}
+
 #[widget]
-fn Removal(context: &mut KayakContext) {
+fn Removal(props: RemovalProps) {
     let text_styles = Style {
         bottom: StyleProp::Value(Units::Stretch(1.0)),
         left: StyleProp::Value(Units::Stretch(0.1)),
diff --git a/examples/provider.rs b/examples/provider.rs
index fc32364776e3e2cdfd0321b271b04afddb5b4764..8a39f05bbda6ed8d379d91437082125adf3f2c7d 100644
--- a/examples/provider.rs
+++ b/examples/provider.rs
@@ -16,13 +16,14 @@ use bevy::prelude::{
 use kayak_ui::{
     bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle},
     core::{
-        render, rsx,
+        render, rsx, WidgetProps,
         styles::{LayoutType, Style, StyleProp, Units},
         widget, Bound, Color, EventType, Index, MutableBound, OnEvent,
     },
     widgets::{App, Background, Element, If, Text, TooltipConsumer, TooltipProvider, Window},
 };
 use std::sync::Arc;
+use kayak_core::Children;
 
 /// The color theme struct we will be using across our demo widgets
 #[derive(Debug, Default, Clone, PartialEq)]
@@ -60,23 +61,37 @@ impl Theme {
     }
 }
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct ThemeProviderProps {
+    pub initial_theme: Theme,
+    #[prop_field(Children)]
+    children: Option<Children>,
+}
+
 /// This widget provides a theme to its children
 ///
 /// Any descendant of this provider can access its theme by calling [create_consumer](kayak_core::KayakContext::create_consumer).
 /// It can also be nested within itself, allowing for differing provider values.
 #[widget]
-fn ThemeProvider(context: &mut KayakContext, initial_theme: Theme) {
+fn ThemeProvider(props: ThemeProviderProps) {
+    let ThemeProviderProps { initial_theme, children } = props.clone();
     // Create the provider
     context.create_provider(initial_theme);
     rsx! { <>{children}</> }
 }
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct ThemeButtonProps {
+    pub theme: Theme,
+}
+
 /// A widget that shows a colored box, representing the theme
 ///
 /// This widget acts as one of our consumers of the [ThemeProvider]. It then uses the theme data to
 /// display its content and also updates the shared state when clicked.
 #[widget]
-fn ThemeButton(context: &mut KayakContext, theme: Theme) {
+fn ThemeButton(props: ThemeButtonProps) {
+    let ThemeButtonProps { theme } = props.clone();
     // Create a consumer
     // This grabs the current theme from the nearest ThemeProvider up the widget tree
     let consumer = context
@@ -120,11 +135,14 @@ fn ThemeButton(context: &mut KayakContext, theme: Theme) {
     }
 }
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct ThemeSelectorProps {}
+
 /// A widget displaying a set of [ThemeButton] widgets
 ///
 /// This is just an abstracted container. Not much to see here...
 #[widget]
-fn ThemeSelector() {
+fn ThemeSelector(props: ThemeSelectorProps) {
     let vampire_theme = Theme::vampire();
     let solar_theme = Theme::solar();
     let vector_theme = Theme::vector();
@@ -146,11 +164,16 @@ fn ThemeSelector() {
     }
 }
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct ThemeDemoProps {
+    is_root: bool
+}
+
 /// A widget that demonstrates the theming in action
 ///
 /// The `is_root` prop just ensures we don't recursively render this widget to infinity
 #[widget]
-fn ThemeDemo(context: &mut KayakContext, is_root: bool) {
+fn ThemeDemo(props: ThemeDemoProps) {
     // Create a consumer
     // This grabs the current theme from the nearest ThemeProvider up the widget tree
     let consumer = context
@@ -158,7 +181,7 @@ fn ThemeDemo(context: &mut KayakContext, is_root: bool) {
         .expect("Requires ThemeProvider as an ancestor");
     let theme = consumer.get();
 
-    let select_lbl = if is_root {
+    let select_lbl = if props.is_root {
         format!("Select Theme (Current: {})", theme.name)
     } else {
         format!("Select A Different Theme (Current: {})", theme.name)
@@ -223,7 +246,7 @@ fn ThemeDemo(context: &mut KayakContext, is_root: bool) {
                     <Text content={btn_text} line_height={Some(20.0)} size={14.0} styles={Some(btn_text_style)} />
                 </Background>
 
-                <If condition={is_root}>
+                <If condition={props.is_root}>
                     <Element styles={Some(nested_style)}>
 
                         // This is one of the benefits of the provider/consumer pattern:
diff --git a/examples/tabs/tab.rs b/examples/tabs/tab.rs
index 1d0eb9a9a4b9061e635e87d45920a5f32f8d00d5..dd2eeae602b66db2062438678d0a08f14ea2d37f 100644
--- a/examples/tabs/tab.rs
+++ b/examples/tabs/tab.rs
@@ -1,7 +1,7 @@
 use kayak_ui::{
     core::{
         render_command::RenderCommand,
-        rsx,
+        rsx, WidgetProps,
         styles::{LayoutType, Style, StyleProp, Units},
         use_state, widget, Bound, EventType, OnEvent,
     },
@@ -17,9 +17,21 @@ enum TabHoverState {
     Active,
 }
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TabProps {
+    pub content: String,
+    pub selected: bool,
+    #[prop_field(Styles)]
+    pub styles: Option<Style>,
+    #[prop_field(OnEvent)]
+    pub on_event: Option<OnEvent>,
+}
+
 /// The actual tab, displayed in a [TabBar](crate::tab_bar::TabBar)
 #[widget]
-pub fn Tab(context: &mut KayakContext, content: String, selected: bool) {
+pub fn Tab(props: TabProps) {
+    let TabProps{content, selected, ..} = props.clone();
+
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
     let (focus_state, set_focus_state, ..) = use_state!(false);
     let (hover_state, set_hover_state, ..) = use_state!(TabHoverState::None);
@@ -93,11 +105,11 @@ pub fn Tab(context: &mut KayakContext, content: String, selected: bool) {
         ..Default::default()
     };
 
-    self.styles = Some(Style {
+    props.styles = Some(Style {
         render_command: StyleProp::Value(RenderCommand::Layout),
         height: StyleProp::Value(Units::Pixels(theme.get().tab_height)),
         max_width: StyleProp::Value(Units::Pixels(100.0)),
-        ..styles.clone().unwrap_or_default()
+        ..props.styles.clone().unwrap_or_default()
     });
 
     rsx! {
diff --git a/examples/tabs/tab_bar.rs b/examples/tabs/tab_bar.rs
index 6c32a16b96e81b012fdc95c730cbf8f9d5a4991d..b382ea62228d1cef5ec17a945d50ba8e2492ba3e 100644
--- a/examples/tabs/tab_bar.rs
+++ b/examples/tabs/tab_bar.rs
@@ -1,6 +1,6 @@
 use kayak_ui::{
     core::{
-        constructor, rsx,
+        constructor, rsx, WidgetProps,
         styles::{LayoutType, Style, StyleProp, Units},
         widget, Bound, EventType, Handler, KeyCode, OnEvent, VecTracker,
     },
@@ -10,17 +10,22 @@ use kayak_ui::{
 use crate::tab::Tab;
 use crate::TabTheme;
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TabBarProps {
+    pub tabs: Vec<String>,
+    pub selected: usize,
+    pub on_select_tab: Handler<usize>,
+    #[prop_field(Styles)]
+    pub styles: Option<Style>
+}
+
 /// A widget displaying a collection of tabs in a horizontal bar
 #[widget]
-pub fn TabBar(
-    context: &mut KayakContext,
-    tabs: Vec<String>,
-    selected: usize,
-    on_select_tab: Handler<usize>,
-) {
+pub fn TabBar(props: TabBarProps) {
+    let TabBarProps{on_select_tab, selected, tabs, ..} = props.clone();
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
 
-    let tabs = tabs.iter().enumerate().map(|(index, tab)| {
+    let tabs = tabs.into_iter().enumerate().map(move |(index, tab)| {
         let on_select = on_select_tab.clone();
         let tab_event_handler = OnEvent::new(move |_, event| {
             match event.event_type {
@@ -40,19 +45,19 @@ pub fn TabBar(
         constructor! {
             <Tab content={tab.clone()} on_event={Some(tab_event_handler.clone())} selected={selected == index} />
         }
-    }).collect::<Vec<_>>();
+    });
 
     let background_styles = Style {
         layout_type: StyleProp::Value(LayoutType::Row),
         background_color: StyleProp::Value(theme.get().bg),
         height: StyleProp::Value(Units::Auto),
         width: StyleProp::Value(Units::Stretch(1.0)),
-        ..styles.clone().unwrap_or_default()
+        ..props.styles.clone().unwrap_or_default()
     };
 
     rsx! {
         <Background styles={Some(background_styles)}>
-            <VecTracker data={tabs} />
+            {VecTracker::from(tabs.clone())}
         </Background>
     }
 }
diff --git a/examples/tabs/tab_box.rs b/examples/tabs/tab_box.rs
index fa186e41a4ef4f42e3d35563aa7a743739016113..413130776683612d2f5f302abb9ea7bfbf96fbae 100644
--- a/examples/tabs/tab_box.rs
+++ b/examples/tabs/tab_box.rs
@@ -1,6 +1,6 @@
 use kayak_ui::core::{
     render_command::RenderCommand,
-    rsx,
+    rsx, WidgetProps,
     styles::{Style, StyleProp},
     use_state, widget, Bound, Fragment, Handler,
 };
@@ -18,11 +18,20 @@ pub struct TabData {
     pub content: Fragment,
 }
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TabBoxProps {
+    pub initial_tab: usize,
+    pub tabs: Vec<TabData>,
+    #[prop_field(Styles)]
+    pub styles: Option<Style>,
+}
+
 /// The actual tab container widget.
 ///
 /// This houses both the tab bar and its content.
 #[widget]
-pub fn TabBox(context: &mut KayakContext, tabs: Vec<TabData>, initial_tab: usize) {
+pub fn TabBox(props: TabBoxProps) {
+    let TabBoxProps { initial_tab, tabs, .. } = props.clone();
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
     let (selected, set_selected, ..) = use_state!(initial_tab);
 
@@ -39,7 +48,7 @@ pub fn TabBox(context: &mut KayakContext, tabs: Vec<TabData>, initial_tab: usize
         set_selected(index);
     });
 
-    self.styles = Some(Style {
+    props.styles = Some(Style {
         render_command: StyleProp::Value(RenderCommand::Quad),
         background_color: StyleProp::Value(theme.get().fg),
         ..Default::default()
diff --git a/examples/tabs/tab_content.rs b/examples/tabs/tab_content.rs
index 3348cac2f0d90c7767ca55c983475a356aa92907..afae825f268cee66f4e98e2245a453809d1e79ce 100644
--- a/examples/tabs/tab_content.rs
+++ b/examples/tabs/tab_content.rs
@@ -1,6 +1,6 @@
 use kayak_ui::core::{
     render_command::RenderCommand,
-    rsx,
+    rsx, WidgetProps,
     styles::{Style, StyleProp},
     widget, Bound, Fragment, VecTracker,
 };
@@ -8,9 +8,18 @@ use std::ops::Index;
 
 use crate::TabTheme;
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TabContentProps {
+    pub selected: usize,
+    pub tabs: Vec<Fragment>,
+    #[prop_field(Styles)]
+    pub styles: Option<Style>,
+}
+
 /// A widget that displays the selected tab's content
 #[widget]
-pub fn TabContent(context: &mut KayakContext, tabs: Vec<Fragment>, selected: usize) {
+pub fn TabContent(props: TabContentProps) {
+    let TabContentProps { selected, tabs, .. } = props.clone();
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
 
     if selected >= tabs.len() {
@@ -18,17 +27,18 @@ pub fn TabContent(context: &mut KayakContext, tabs: Vec<Fragment>, selected: usi
         return;
     }
 
-    self.styles = Some(Style {
+    props.styles = Some(Style {
         render_command: StyleProp::Value(RenderCommand::Quad),
         background_color: StyleProp::Value(theme.get().fg),
         ..Default::default()
     });
 
     let tab = tabs.index(selected).clone();
+    let tab = vec![tab.clone()];
 
     rsx! {
         <>
-            <VecTracker data={vec![tab.clone()]} />
+            {VecTracker::from(tab.clone().into_iter())}
         </>
     }
 }
diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs
index 9b199a98f14c7db8f9717291ed799312c45a3c71..04e9b3d3cdb3973084389d1f8b9dd4400f472c74 100644
--- a/examples/tabs/tabs.rs
+++ b/examples/tabs/tabs.rs
@@ -13,9 +13,9 @@ use bevy::{
 use kayak_ui::{
     bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle},
     core::{
-        constructor, render, rsx,
+        constructor, render, rsx, WidgetProps,
         styles::{Style, StyleProp, Units},
-        widget, Children, Color, Index,
+        widget, Color, Index,
     },
     widgets::{App, Text, Window},
 };
@@ -30,8 +30,11 @@ mod tab_box;
 mod tab_content;
 mod theming;
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TabDemoProps {}
+
 #[widget]
-fn TabDemo() {
+fn TabDemo(props: TabDemoProps) {
     let text_style = Style {
         width: StyleProp::Value(Units::Percentage(75.0)),
         top: StyleProp::Value(Units::Stretch(0.5)),
@@ -48,8 +51,6 @@ fn TabDemo() {
         TabData {
             name: "Tab 1".to_string(),
             content: {
-                // This is a temporary hack to prevent the `constructor` macro from throwing an error
-                let children = Children::None;
                 let text_style = text_style.clone();
                 constructor! {
                     <>
@@ -61,7 +62,6 @@ fn TabDemo() {
         TabData {
             name: "Tab 2".to_string(),
             content: {
-                let children = Children::None;
                 let text_style = text_style.clone();
                 constructor! {
                     <>
@@ -73,7 +73,6 @@ fn TabDemo() {
         TabData {
             name: "Tab 3".to_string(),
             content: {
-                let children = Children::None;
                 let text_style = text_style.clone();
                 constructor! {
                     <>
diff --git a/examples/tabs/theming.rs b/examples/tabs/theming.rs
index 02906902b8fbaa499a957f75eb3a8a4a668460ff..41f3f29b26d9ba95eee7118b7e143498cdaf0c0e 100644
--- a/examples/tabs/theming.rs
+++ b/examples/tabs/theming.rs
@@ -1,4 +1,4 @@
-use kayak_ui::core::{rsx, widget, Color};
+use kayak_ui::core::{Children, rsx, widget, Color, WidgetProps};
 
 #[derive(Clone, Copy, Debug, Default, PartialEq)]
 pub struct TabTheme {
@@ -19,8 +19,16 @@ pub struct ColorState {
     pub active: Color,
 }
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct TabThemeProviderProps {
+    pub initial_theme: TabTheme,
+    #[prop_field(Children)]
+    pub children: Option<Children>
+}
+
 #[widget]
-pub fn TabThemeProvider(initial_theme: TabTheme) {
+pub fn TabThemeProvider(props: TabThemeProviderProps) {
+    let TabThemeProviderProps { initial_theme, children } = props.clone();
     context.create_provider(initial_theme);
     rsx! { <>{children}</> }
 }
diff --git a/examples/text_box.rs b/examples/text_box.rs
index 2507de922eae80af89b426ccb54b101a68ea25aa..bfaf2fc589e17ec0c3b849244444fb54c4dba24d 100644
--- a/examples/text_box.rs
+++ b/examples/text_box.rs
@@ -7,14 +7,17 @@ use kayak_core::Color;
 use kayak_render_macros::use_state;
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
 use kayak_ui::core::{
-    render, rsx,
+    render, rsx, WidgetProps,
     styles::{Style, StyleProp, Units},
     widget, Index,
 };
 use kayak_ui::widgets::{App, OnChange, TextBox, Window};
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct TextBoxExampleProps {}
+
 #[widget]
-fn TextBoxExample(context: &mut KayakContext) {
+fn TextBoxExample(props: TextBoxExampleProps) {
     let (value, set_value, _) = use_state!("I started with a value!".to_string());
     let (empty_value, set_empty_value, _) = use_state!("".to_string());
     let (red_value, set_red_value, _) = use_state!("This text is red".to_string());
diff --git a/examples/todo/add_button.rs b/examples/todo/add_button.rs
index a3dbdbe32e14dd56dae9dac581e93ffdabc62e97..d6143fa0f2cff12fb2bcc5a9555de2232c76131f 100644
--- a/examples/todo/add_button.rs
+++ b/examples/todo/add_button.rs
@@ -1,19 +1,27 @@
 use kayak_ui::core::{
     color::Color,
     render_command::RenderCommand,
-    rsx,
+    rsx, WidgetProps,
     styles::{Style, StyleProp, Units},
-    use_state, widget, Children, EventType, OnEvent,
+    use_state, widget, EventType, OnEvent,
 };
 
 use kayak_ui::widgets::{Background, Text};
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+pub struct AddButtonProps {
+    #[prop_field(Styles)]
+    pub styles: Option<Style>,
+    #[prop_field(OnEvent)]
+    pub on_event: Option<OnEvent>,
+}
+
 #[widget]
-pub fn AddButton(children: Children, styles: Option<Style>) {
+pub fn AddButton(props: AddButtonProps) {
     let (color, set_color, ..) = use_state!(Color::new(0.0781, 0.0898, 0.101, 1.0));
 
-    let base_styles = styles.clone().unwrap_or_default();
-    *styles = Some(Style {
+    let base_styles = props.styles.clone().unwrap_or_default();
+    props.styles = Some(Style {
         render_command: StyleProp::Value(RenderCommand::Layout),
         height: StyleProp::Value(Units::Pixels(32.0)),
         width: StyleProp::Value(Units::Pixels(30.0)),
diff --git a/examples/todo/card.rs b/examples/todo/card.rs
index 7196752c70f1766f27c3450e79e10838c7496c69..b9b06183e4e27b4b80d86963416f6b8f8f29de9f 100644
--- a/examples/todo/card.rs
+++ b/examples/todo/card.rs
@@ -1,5 +1,5 @@
 use kayak_ui::core::{
-    rsx,
+    rsx, WidgetProps,
     styles::{LayoutType, Style, StyleProp, Units},
     widget, Color, EventType, Handler, OnEvent,
 };
@@ -7,8 +7,16 @@ use kayak_ui::widgets::{Background, Text};
 
 use super::delete_button::DeleteButton;
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+pub struct CardProps {
+    pub card_id: usize,
+    pub name: String,
+    pub on_delete: Handler<usize>,
+}
+
 #[widget]
-pub fn Card(card_id: usize, name: String, on_delete: Handler<usize>) {
+pub fn Card(props: CardProps) {
+    let CardProps{card_id, name, on_delete} = props.clone();
     let background_styles = Style {
         layout_type: StyleProp::Value(LayoutType::Row),
         background_color: StyleProp::Value(Color::new(0.176, 0.196, 0.215, 1.0)),
diff --git a/examples/todo/cards.rs b/examples/todo/cards.rs
index 96b6661173ef15f1adaaa1f96fe4508d000c5286..88e6ba44e8c502388384b0c1f2cf09c3104166f0 100644
--- a/examples/todo/cards.rs
+++ b/examples/todo/cards.rs
@@ -1,10 +1,17 @@
-use kayak_ui::core::{constructor, rsx, widget, Handler, VecTracker};
+use kayak_ui::core::{constructor, rsx, widget, Handler, VecTracker, WidgetProps};
 use kayak_ui::widgets::Element;
 
 use super::{card::Card, Todo};
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+pub struct CardsProps {
+    pub cards: Vec<Todo>,
+    pub on_delete: Handler<usize>,
+}
+
 #[widget]
-pub fn Cards(cards: Vec<Todo>, on_delete: Handler<usize>) {
+pub fn Cards(props: CardsProps) {
+    let CardsProps { cards, on_delete } = props.clone();
     rsx! {
         <Element>
             {VecTracker::from(
diff --git a/examples/todo/delete_button.rs b/examples/todo/delete_button.rs
index bbe6735b67d26dabdd795c43a3c29e53a15553b8..34d572960bc80d3fe753db1fe3ef064f16ef85db 100644
--- a/examples/todo/delete_button.rs
+++ b/examples/todo/delete_button.rs
@@ -1,19 +1,27 @@
 use kayak_ui::core::{
     color::Color,
     render_command::RenderCommand,
-    rsx,
+    rsx, WidgetProps,
     styles::{Style, StyleProp, Units},
-    use_state, widget, Children, EventType, OnEvent,
+    use_state, widget, EventType, OnEvent,
 };
 
 use kayak_ui::widgets::{Background, Text};
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+pub struct DeleteButtonProps {
+    #[prop_field(Styles)]
+    styles: Option<Style>,
+    #[prop_field(OnEvent)]
+    pub on_event: Option<OnEvent>,
+}
+
 #[widget]
-pub fn DeleteButton(children: Children, styles: Option<Style>) {
+pub fn DeleteButton(props: DeleteButtonProps) {
     let (color, set_color, ..) = use_state!(Color::new(0.0781, 0.0898, 0.101, 1.0));
 
-    let base_styles = styles.clone().unwrap_or_default();
-    *styles = Some(Style {
+    let base_styles = props.styles.clone().unwrap_or_default();
+    props.styles = Some(Style {
         render_command: StyleProp::Value(RenderCommand::Layout),
         height: StyleProp::Value(Units::Pixels(32.0)),
         width: StyleProp::Value(Units::Pixels(30.0)),
diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs
index fea3f1e36eebc6f3be6151a5302f6a39b963ccab..95b063cbeac85989bbdf1116ebefa294a1b3ab79 100644
--- a/examples/todo/todo.rs
+++ b/examples/todo/todo.rs
@@ -5,7 +5,7 @@ use bevy::{
 };
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
 use kayak_ui::core::{
-    render, rsx,
+    render, rsx, WidgetProps,
     styles::{LayoutType, Style, StyleProp, Units},
     use_state, widget, EventType, Handler, Index, OnEvent,
 };
@@ -23,8 +23,11 @@ pub struct Todo {
     name: String,
 }
 
+#[derive(WidgetProps, Clone, Debug, Default, PartialEq)]
+struct TodoAppProps {}
+
 #[widget]
-fn TodoApp() {
+fn TodoApp(props: TodoAppProps) {
     let (todos, set_todos, ..) = use_state!(vec![
         Todo {
             name: "Use bevy to make a game!".to_string(),
diff --git a/examples/world_interaction.rs b/examples/world_interaction.rs
index 6494378dd74f5245cee918a3a075ce2c2974133e..96633098a28bd4e7c07f3e32534b18d82a712605 100644
--- a/examples/world_interaction.rs
+++ b/examples/world_interaction.rs
@@ -18,7 +18,7 @@ use bevy::{
 use kayak_ui::{
     bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle},
     core::{
-        render, rsx,
+        render, rsx, WidgetProps,
         styles::{Style, StyleProp, Units},
         use_state, widget, EventType, Index, OnEvent,
     },
@@ -65,8 +65,11 @@ fn set_active_tile_target(
     tile.target = tile_pos;
 }
 
+#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
+pub struct ControlPanelProps {}
+
 #[widget]
-fn ControlPanel() {
+fn ControlPanel(props: ControlPanelProps) {
     let text_styles = Style {
         left: StyleProp::Value(Units::Stretch(1.0)),
         right: StyleProp::Value(Units::Stretch(1.0)),
@@ -106,7 +109,7 @@ fn ControlPanel() {
 
     rsx! {
         <>
-            <Window draggable={true} position={(50.0, 50.0)} size={(300.0, 150.0)} title={"Square Mover: The Game".to_string()}>
+            <Window draggable={true} position={(50.0, 50.0)} size={(300.0, 200.0)} title={"Square Mover: The Game".to_string()}>
                 <Text size={13.0} content={"You can check if the cursor is over the UI or on a focusable widget using the BevyContext resource.".to_string()} styles={Some(text_styles)} />
                 <Button on_event={Some(on_change_color)} styles={Some(button_styles)}>
                     <Text size={16.0} content={"Change Tile Color".to_string()} />