diff --git a/examples/text_box.rs b/examples/text_box.rs
index 5a51d0217d585c4d016e8281949d5926673e0f1b..78d1c767c7fbc7f5aa8e6bafee8de2c66319eb8b 100644
--- a/examples/text_box.rs
+++ b/examples/text_box.rs
@@ -11,7 +11,7 @@ use kayak_ui::core::{
     styles::{Style, StyleProp, Units},
     widget, Index,
 };
-use kayak_ui::widgets::{App, Inspector, OnChange, SpinBox, TextBox, Window};
+use kayak_ui::widgets::{App, Inspector, OnChange, SpinBox, SpinBoxStyle, TextBox, Window};
 
 #[widget]
 fn TextBoxExample() {
@@ -46,6 +46,8 @@ fn TextBoxExample() {
         set_spin_value(event.value);
     });
 
+    let vert = SpinBoxStyle::Vertical;
+
     rsx! {
         <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)} />
@@ -56,7 +58,21 @@ 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)} value={spin_value} on_change={Some(on_change_spin)} />
+            <SpinBox
+                styles={Some(input_styles)}
+                value={spin_value}
+                on_change={Some(on_change_spin)}
+                min_val={0.0}
+                max_val={10.0}
+            />
+            <SpinBox
+                spin_button_style={vert}
+                styles={Some(input_styles)}
+                value={spin_value}
+                on_change={Some(on_change_spin)}
+                min_val={0.0}
+                max_val={10.0}
+            />
         </Window>
     }
 }
@@ -74,7 +90,6 @@ fn startup(
         render! {
             <App>
                 <TextBoxExample />
-                <Inspector />
             </App>
         }
     });
diff --git a/src/widgets/spin_box.rs b/src/widgets/spin_box.rs
index fa160c1016fb440fb9b7305e4144e302375f6f84..1e8317bb24180d22e96091e7e7425acff624f02c 100644
--- a/src/widgets/spin_box.rs
+++ b/src/widgets/spin_box.rs
@@ -1,3 +1,5 @@
+use std::{fmt::Debug, fmt::Formatter, sync::Arc};
+
 use crate::{
     core::{
         render_command::RenderCommand,
@@ -11,9 +13,22 @@ use kayak_core::{
     styles::{LayoutType, StyleProp},
     CursorIcon, OnLayout,
 };
+use kayak_render_macros::use_state;
 
 use crate::widgets::{Background, Clip, OnChange, Text};
 
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum SpinBoxStyle {
+    Horizontal,
+    Vertical,
+}
+
+impl Default for SpinBoxStyle {
+    fn default() -> Self {
+        SpinBoxStyle::Horizontal
+    }
+}
+
 #[derive(Debug, PartialEq, Clone)]
 pub struct SpinBoxProps {
     /// If true, prevents the widget from being focused (and consequently edited)
@@ -22,6 +37,8 @@ pub struct SpinBoxProps {
     pub on_change: Option<OnChange>,
     /// The text to display when the user input is empty
     pub placeholder: Option<String>,
+    /// Whether spinbox is horizontally or vertically aligned.
+    pub spin_button_style: SpinBoxStyle,
     /// The user input
     ///
     /// This is a controlled state. You _must_ set this to the value to you wish to be displayed.
@@ -32,25 +49,55 @@ pub struct SpinBoxProps {
     pub incr_str: String,
     /// Text on decrement button defaults to `<`
     pub decr_str: String,
-    pub children: Option<Children>,
+    /// Events on increment button press
+    pub on_incr_event: Option<OnEvent>,
+    /// Events on decrement button press
+    pub on_decr_event: Option<OnEvent>,
+    /// Events for text edit
     pub on_event: Option<OnEvent>,
+    /// Minimal value
+    pub min_val: f32,
+    /// Maximal value
+    pub max_val: f32,
+    pub children: Option<Children>,
     pub on_layout: Option<OnLayout>,
     pub focusable: Option<bool>,
 }
 
+impl SpinBoxProps {
+    pub fn get_float(&self) -> f32 {
+        self.value.parse::<f32>().unwrap_or_default()
+    }
+
+    pub fn get_int(&self) -> i16 {
+        let temp_float = self.get_float();
+        if temp_float > f32::from(i16::MAX) {
+            i16::MAX
+        } else if temp_float < f32::from(i16::MIN) {
+            i16::MIN
+        } else {
+            temp_float.round() as i16
+        }
+    }
+}
 
 impl Default for SpinBoxProps {
     fn default() -> SpinBoxProps {
-        SpinBoxProps { 
-            incr_str: ">".into(),
-            decr_str: "<".into(),
+        SpinBoxProps {
+            incr_str: "+".into(),
+            decr_str: "-".into(),
             disabled: Default::default(),
-            on_change:  Default::default(),
+            on_change: Default::default(),
             placeholder: Default::default(),
             value: Default::default(),
             styles: Default::default(),
+            spin_button_style: Default::default(),
             children: Default::default(),
+            on_incr_event: Default::default(),
+            on_decr_event: Default::default(),
             on_event: Default::default(),
+            min_val: f32::MIN,
+            max_val: f32::MAX,
             on_layout: Default::default(),
             focusable: Default::default(),
         }
@@ -106,6 +153,9 @@ pub fn SpinBox(props: SpinBoxProps) {
         on_change,
         placeholder,
         value,
+        max_val,
+        min_val,
+        spin_button_style,
         ..
     } = props.clone();
 
@@ -180,11 +230,19 @@ pub fn SpinBox(props: SpinBoxProps) {
         }
     };
 
-    let button_style = Some(Style {
-        height: Units::Pixels(24.0).into(),
-        width: Units::Pixels(24.0).into(),
-        ..Default::default()
-    });
+    let button_style = match spin_button_style {
+        SpinBoxStyle::Horizontal => Some(Style {
+            height: Units::Pixels(24.0).into(),
+            width: Units::Pixels(24.0).into(),
+            ..Default::default()
+        }),
+        SpinBoxStyle::Vertical => Some(Style {
+            height: Units::Pixels(12.0).into(),
+            width: Units::Pixels(24.0).into(),
+
+            ..Default::default()
+        }),
+    };
 
     let value = if value.is_empty() {
         placeholder.unwrap_or_else(|| value.clone())
@@ -192,30 +250,97 @@ pub fn SpinBox(props: SpinBoxProps) {
         value
     };
 
-    let inline_style = Style {
+    let row = Style {
         layout_type: StyleProp::Value(LayoutType::Row),
         ..Style::default()
     };
 
+    let col = Style {
+        layout_type: StyleProp::Value(LayoutType::Column),
+        height: Units::Stretch(100.0).into(),
+        width: Units::Pixels(26.0).into(),
+        ..Style::default()
+    };
+
     let incr_str = props.clone().incr_str;
     let decr_str = props.clone().decr_str;
 
-    rsx! {
-        <Background styles={Some(background_styles)}>
-            <Clip styles={Some(inline_style)}>
-                <Button styles={button_style}>
-                    <Text content={decr_str} />
-                </Button>
-                <Text
-                    content={value}
-                    size={14.0}
-                    styles={Some(text_styles)}
-                />
-                <Button styles={button_style}>
-                    <Text content={incr_str} />
-                </Button>
-            </Clip>
-        </Background>
+    let (spin_value, set_val, _) = use_state!(value);
+    let x = spin_value.parse::<f32>().unwrap_or_default();
+    let decr_fn = set_val.clone();
+    let incr_fn = set_val.clone();
+
+    let incr_event = if let Some(event) = props.clone().on_incr_event {
+        event
+    } else {
+        OnEvent::new(move |_, event| match event.event_type {
+            EventType::Click(_) => {
+                if x >= max_val {
+                    return;
+                }
+                incr_fn((x + 1.0f32).to_string());
+            }
+            _ => {}
+        })
+    };
+
+    let decr_event = if let Some(event) = props.clone().on_decr_event {
+        event
+    } else {
+        OnEvent::new(move |_, event| match event.event_type {
+            EventType::Click(_) => {
+                if x <= min_val {
+                    return;
+                }
+                decr_fn((x - 1.0f32).to_string());
+            }
+            _ => {}
+        })
+    };
+
+    match spin_button_style {
+        SpinBoxStyle::Horizontal => {
+            rsx! {
+                <Background styles={Some(background_styles)}>
+                    <Clip styles={Some(row)}>
+                        <Button styles={button_style} on_event={Some(decr_event)}>
+                            <Text content={decr_str} />
+                        </Button>
+                        <Text
+                            content={spin_value}
+                            size={14.0}
+                            styles={Some(text_styles)}
+                        />
+                        <Button styles={button_style} on_event={Some(incr_event)}>
+                            <Text content={incr_str} />
+                        </Button>
+                    </Clip>
+                </Background>
+            }
+        }
+        SpinBoxStyle::Vertical => {
+            rsx! {
+                <Background styles={Some(background_styles)}>
+
+                    <Clip styles={Some(row)}>
+                        <Text
+                            content={spin_value}
+                            size={14.0}
+                            styles={Some(text_styles)}
+                        />
+                        <Clip styles={Some(col)}>
+
+                            <Button styles={button_style} on_event={Some(incr_event)}>
+                                <Text content={incr_str} size={11.0} />
+                            </Button>
+                            <Button styles={button_style} on_event={Some(decr_event)}>
+                                <Text content={decr_str} size={11.0}/>
+                            </Button>
+                        </Clip>
+                    </Clip>
+                </Background>
+            }
+        }
     }
 }