diff --git a/examples/text_box.rs b/examples/text_box.rs
index 2c0bca89258cc9a4909cfa3a8fd358c8f143f92a..5a51d0217d585c4d016e8281949d5926673e0f1b 100644
--- a/examples/text_box.rs
+++ b/examples/text_box.rs
@@ -5,19 +5,20 @@ use bevy::{
 };
 use kayak_core::Color;
 use kayak_render_macros::use_state;
-use kayak_ui::{bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle}};
+use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
 use kayak_ui::core::{
     render, rsx,
     styles::{Style, StyleProp, Units},
     widget, Index,
 };
-use kayak_ui::widgets::{App, OnChange, Window, TextBox, SpinBox};
+use kayak_ui::widgets::{App, Inspector, OnChange, SpinBox, TextBox, Window};
 
 #[widget]
 fn TextBoxExample() {
     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());
+    let (spin_value, set_spin_value, _) = use_state!("3".to_string());
 
     let input_styles = Style {
         top: StyleProp::Value(Units::Pixels(10.0)),
@@ -41,8 +42,12 @@ fn TextBoxExample() {
         set_red_value(event.value);
     });
 
+    let on_change_spin = OnChange::new(move |event| {
+        set_spin_value(event.value);
+    });
+
     rsx! {
-        <Window position={(50.0, 50.0)} size={(300.0, 300.0)} title={"TextBox Example".to_string()}>
+        <Window position={(50.0, 50.0)} size={(500.0, 300.0)} title={"TextBox Example".to_string()}>
             <TextBox styles={Some(input_styles)} value={value} on_change={Some(on_change)} />
             <TextBox
                 styles={Some(input_styles)}
@@ -51,7 +56,7 @@ fn TextBoxExample() {
                 placeholder={Some("This is a placeholder".to_string())}
             />
             <TextBox styles={Some(red_text_styles)} value={red_value} on_change={Some(on_change_red)} />
-            <SpinBox styles={Some(input_styles)} />
+            <SpinBox styles={Some(input_styles)} value={spin_value} on_change={Some(on_change_spin)} />
         </Window>
     }
 }
@@ -69,6 +74,7 @@ fn startup(
         render! {
             <App>
                 <TextBoxExample />
+                <Inspector />
             </App>
         }
     });
diff --git a/src/widgets/on_change.rs b/src/widgets/on_change.rs
index 1aa632f4daf197621caba05742c3ab65651e9767..b4fdebba15a0ea64cf3730e6eb54f7541c9a320e 100644
--- a/src/widgets/on_change.rs
+++ b/src/widgets/on_change.rs
@@ -24,4 +24,4 @@ impl std::fmt::Debug for OnChange {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_tuple("OnChange").finish()
     }
-}
\ No newline at end of file
+}
diff --git a/src/widgets/spin_box.rs b/src/widgets/spin_box.rs
index 34d756c20384781a3636650dcbf042ca2846295c..fa160c1016fb440fb9b7305e4144e302375f6f84 100644
--- a/src/widgets/spin_box.rs
+++ b/src/widgets/spin_box.rs
@@ -1,14 +1,20 @@
-use crate::{core::{
-    render_command::RenderCommand,
-    rsx,
-    styles::{Corner, Style, Units},
-    widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps,
-}, widgets::ChangeEvent};
-use kayak_core::{OnLayout, CursorIcon};
-
-use crate::widgets::{Background, Clip, Text, OnChange};
-
-#[derive(Default, Debug, PartialEq, Clone)]
+use crate::{
+    core::{
+        render_command::RenderCommand,
+        rsx,
+        styles::{Corner, Style, Units},
+        widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps,
+    },
+    widgets::{Button, ChangeEvent},
+};
+use kayak_core::{
+    styles::{LayoutType, StyleProp},
+    CursorIcon, OnLayout,
+};
+
+use crate::widgets::{Background, Clip, OnChange, Text};
+
+#[derive(Debug, PartialEq, Clone)]
 pub struct SpinBoxProps {
     /// If true, prevents the widget from being focused (and consequently edited)
     pub disabled: bool,
@@ -22,12 +28,35 @@ pub struct SpinBoxProps {
     /// You can use the [`on_change`] callback to update this prop as the user types.
     pub value: String,
     pub styles: Option<Style>,
+    /// Text on increment button defaults to `>`
+    pub incr_str: String,
+    /// Text on decrement button defaults to `<`
+    pub decr_str: String,
     pub children: Option<Children>,
     pub on_event: Option<OnEvent>,
     pub on_layout: Option<OnLayout>,
     pub focusable: Option<bool>,
 }
 
+
+impl Default for SpinBoxProps {
+    fn default() -> SpinBoxProps {
+        SpinBoxProps { 
+            incr_str: ">".into(),
+            decr_str: "<".into(),
+            disabled: Default::default(),
+            on_change:  Default::default(),
+            placeholder: Default::default(),
+            value: Default::default(),
+            styles: Default::default(),
+            children: Default::default(),
+            on_event: Default::default(),
+            on_layout: Default::default(),
+            focusable: Default::default(),
+        }
+    }
+}
+
 impl WidgetProps for SpinBoxProps {
     fn get_children(&self) -> Option<Children> {
         self.children.clone()
@@ -58,11 +87,11 @@ impl WidgetProps for SpinBoxProps {
 pub struct FocusSpinbox(pub bool);
 
 #[widget]
-/// A widget that displays a text input field
+/// A widget that displays a spinnable text field
 ///
 /// # Props
 ///
-/// __Type:__ [`TextBoxProps`]
+/// __Type:__ [`SpinBoxProps`]
 ///
 /// | Common Prop | Accepted |
 /// | :---------: | :------: |
@@ -145,34 +174,54 @@ pub fn SpinBox(props: SpinBoxProps) {
             ..Style::default()
         }
     } else {
-        Style::default()
+        Style {
+            width: Units::Stretch(100.0).into(),
+            ..Style::default()
+        }
     };
 
+    let button_style = Some(Style {
+        height: Units::Pixels(24.0).into(),
+        width: Units::Pixels(24.0).into(),
+        ..Default::default()
+    });
+
     let value = if value.is_empty() {
         placeholder.unwrap_or_else(|| value.clone())
     } else {
         value
     };
 
+    let inline_style = Style {
+        layout_type: StyleProp::Value(LayoutType::Row),
+        ..Style::default()
+    };
+
+    let incr_str = props.clone().incr_str;
+    let decr_str = props.clone().decr_str;
+
     rsx! {
         <Background styles={Some(background_styles)}>
-            <Clip>
+            <Clip styles={Some(inline_style)}>
+                <Button styles={button_style}>
+                    <Text content={decr_str} />
+                </Button>
                 <Text
                     content={value}
                     size={14.0}
-                    line_height={Some(22.0)}
                     styles={Some(text_styles)}
                 />
+                <Button styles={button_style}>
+                    <Text content={incr_str} />
+                </Button>
             </Clip>
         </Background>
     }
 }
 
-
 /// Checks if the given character contains the "Backspace" sequence
 ///
 /// Context: [Wikipedia](https://en.wikipedia.org/wiki/Backspace#Common_use)
 fn is_backspace(c: char) -> bool {
     c == '\u{8}' || c == '\u{7f}'
 }
-
diff --git a/src/widgets/text_box.rs b/src/widgets/text_box.rs
index 2113edc3e50de5fa363d91d57944067b05d74ffa..e9cfc8ad5e4d88e315f0da6d7ff5de0c2424002c 100644
--- a/src/widgets/text_box.rs
+++ b/src/widgets/text_box.rs
@@ -1,12 +1,15 @@
-use crate::{core::{
-    render_command::RenderCommand,
-    rsx,
-    styles::{Corner, Style, Units},
-    widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps,
-}, widgets::ChangeEvent};
+use crate::{
+    core::{
+        render_command::RenderCommand,
+        rsx,
+        styles::{Corner, Style, Units},
+        widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps,
+    },
+    widgets::ChangeEvent,
+};
 use kayak_core::{CursorIcon, OnLayout};
 
-use crate::widgets::{Background, Clip, Text, OnChange};
+use crate::widgets::{Background, Clip, OnChange, Text};
 
 /// Props used by the [`TextBox`] widget
 #[derive(Default, Debug, PartialEq, Clone)]