diff --git a/src/calculate_nodes.rs b/src/calculate_nodes.rs
index 4b7ccc82b76595ba3394e4894b0bab355e38ce9a..31c6d07b6c806cdacfe8b3c61f64245b56602ae4 100644
--- a/src/calculate_nodes.rs
+++ b/src/calculate_nodes.rs
@@ -219,6 +219,7 @@ fn create_primitive(
             font,
             properties,
             text_layout,
+            word_wrap,
             ..
         } => {
             // --- Bind to Font Asset --- //
@@ -247,6 +248,11 @@ fn create_primitive(
                                 parent_layout.height - border_y,
                             );
 
+                            // TODO: Fix this hack.
+                            if !*word_wrap {
+                                properties.max_size.0 = 100000.0;
+                            }
+
                             needs_layout = false;
 
                             if properties.max_size.0 == 0.0 || properties.max_size.1 == 0.0 {
diff --git a/src/render_primitive.rs b/src/render_primitive.rs
index 799af3e6c2b5e75e9c7a570ebc45969408790848..3a3d95caa8abaaaf5669ddc4233137b2e7ad4df4 100644
--- a/src/render_primitive.rs
+++ b/src/render_primitive.rs
@@ -25,6 +25,7 @@ pub enum RenderPrimitive {
         text_layout: TextLayout,
         layout: Rect,
         properties: TextProperties,
+        word_wrap: bool,
     },
     Image {
         border_radius: Corner<f32>,
@@ -103,7 +104,11 @@ impl From<&KStyle> for RenderPrimitive {
                 border: style.border.resolve(),
                 layout: Rect::default(),
             },
-            RenderCommand::Text { content, alignment } => Self::Text {
+            RenderCommand::Text {
+                content,
+                alignment,
+                word_wrap,
+            } => Self::Text {
                 color: style.color.resolve(),
                 content,
                 font,
@@ -115,6 +120,7 @@ impl From<&KStyle> for RenderPrimitive {
                     alignment,
                     ..Default::default()
                 },
+                word_wrap,
             },
             RenderCommand::Image { handle } => Self::Image {
                 border_radius: style.border_radius.resolve(),
diff --git a/src/styles/render_command.rs b/src/styles/render_command.rs
index d2f242cfddebe561acc679d19109649026de81aa..eafb32c58690efa6c2da550465f31c745f29d591 100644
--- a/src/styles/render_command.rs
+++ b/src/styles/render_command.rs
@@ -13,6 +13,7 @@ pub enum RenderCommand {
     Text {
         content: String,
         alignment: Alignment,
+        word_wrap: bool,
     },
     Image {
         handle: Handle<Image>,
diff --git a/src/widgets/text.rs b/src/widgets/text.rs
index 790272daeeb34bf4bded21fd338e99a08804e0bd..a7f8ce2dfbd2ced1c328ccd55565dedc81aeb980 100644
--- a/src/widgets/text.rs
+++ b/src/widgets/text.rs
@@ -33,6 +33,9 @@ pub struct TextProps {
     pub alignment: Alignment,
     /// Custom styles to pass in.
     pub user_styles: KStyle,
+    /// Basic word wrapping.
+    /// Defautls to true
+    pub word_wrap: bool,
 }
 
 impl Default for TextProps {
@@ -44,6 +47,7 @@ impl Default for TextProps {
             show_cursor: false,
             size: -1.0,
             alignment: Alignment::Start,
+            word_wrap: true,
             user_styles: Default::default(),
         }
     }
@@ -84,6 +88,7 @@ pub fn text_render(
             render_command: StyleProp::Value(RenderCommand::Text {
                 content: text.content.clone(),
                 alignment: text.alignment,
+                word_wrap: text.word_wrap,
             }),
             font: if let Some(ref font) = text.font {
                 StyleProp::Value(font.clone())
diff --git a/src/widgets/text_box.rs b/src/widgets/text_box.rs
index 8bf53c00891b88dd5b77c2fae9ec3a0719086b3f..bcce5c1648f4f228241c33b796dac73b382357d7 100644
--- a/src/widgets/text_box.rs
+++ b/src/widgets/text_box.rs
@@ -12,7 +12,7 @@ use crate::{
     on_layout::OnLayout,
     prelude::{KChildren, KayakWidgetContext, OnChange},
     render::font::FontMapping,
-    styles::{Edge, KStyle, RenderCommand, StyleProp, Units},
+    styles::{Edge, KPositionType, KStyle, RenderCommand, StyleProp, Units},
     widget::Widget,
     widget_state::WidgetState,
     widgets::{
@@ -22,6 +22,8 @@ use crate::{
     Focusable, DEFAULT_FONT,
 };
 
+use super::ElementBundle;
+
 /// Props used by the [`TextBox`] widget
 #[derive(Component, PartialEq, Default, Debug, Clone)]
 pub struct TextBoxProps {
@@ -44,6 +46,7 @@ pub struct TextBoxState {
     pub cursor_position: usize,
     pub cursor_visible: bool,
     pub cursor_last_update: Instant,
+    pub current_value: String,
 }
 
 impl Default for TextBoxState {
@@ -55,6 +58,7 @@ impl Default for TextBoxState {
             cursor_position: Default::default(),
             cursor_visible: Default::default(),
             cursor_last_update: Instant::now(),
+            current_value: String::new(),
         }
     }
 }
@@ -96,18 +100,34 @@ pub fn text_box_render(
     In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
     mut commands: Commands,
     mut query: Query<(&mut KStyle, &TextBoxProps, &mut OnEvent, &OnChange)>,
-    state_query: Query<&TextBoxState>,
+    mut state_query: ParamSet<(Query<&TextBoxState>, Query<&mut TextBoxState>)>,
+    font_assets: Res<Assets<KayakFont>>,
+    font_mapping: Res<FontMapping>,
 ) -> bool {
     if let Ok((mut styles, text_box, mut on_event, on_change)) = query.get_mut(entity) {
         let state_entity = widget_context.use_state::<TextBoxState>(
             &mut commands,
             entity,
             TextBoxState {
+                current_value: text_box.value.clone(),
                 ..TextBoxState::default()
             },
         );
 
-        if let Ok(state) = state_query.get(state_entity) {
+        let mut is_different = false;
+        if let Ok(state) = state_query.p0().get(state_entity) {
+            if state.current_value != text_box.value {
+                is_different = true;
+            }
+        }
+
+        if is_different {
+            if let Ok(mut state) = state_query.p1().get_mut(state_entity) {
+                state.current_value = text_box.value.clone();
+            }
+        }
+
+        if let Ok(state) = state_query.p0().get(state_entity) {
             *styles = KStyle::default()
                 // Required styles
                 .with_style(KStyle {
@@ -140,9 +160,7 @@ pub fn text_box_render(
                 ..Default::default()
             };
 
-            let current_value = text_box.value.clone();
             let cloned_on_change = on_change.clone();
-
             let style_font = styles.font.clone();
 
             *on_event = OnEvent::new(
@@ -185,40 +203,26 @@ pub fn text_box_render(
                             }
                         }
                         EventType::CharInput { c } => {
-                            let mut current_value = current_value.clone();
-                            let cloned_on_change = cloned_on_change.clone();
-                            if let Ok(state) = state_query.get(state_entity) {
+                            if let Ok(mut state) = state_query.get_mut(state_entity) {
+                                let cloned_on_change = cloned_on_change.clone();
                                 if !state.focused {
                                     return (event_dispatcher_context, event);
                                 }
-                            } else {
-                                return (event_dispatcher_context, event);
-                            }
-                            if is_backspace(c) {
-                                if !current_value.is_empty() {
-                                    if let Ok(mut state) = state_query.get_mut(state_entity) {
+                                let cursor_pos = state.cursor_position;
+                                if is_backspace(c) {
+                                    if !state.current_value.is_empty() {
                                         // TODO: This doesn't respect graphemes!
-                                        current_value.remove(state.cursor_position - 1);
+                                        state.current_value.remove(cursor_pos - 1);
                                         state.cursor_position -= 1;
                                     }
-                                }
-                            } else if !c.is_control() {
-                                if let Ok(mut state) = state_query.get_mut(state_entity) {
+                                } else if !c.is_control() {
                                     // TODO: This doesn't respect graphemes!
-                                    current_value.insert(state.cursor_position, c);
+                                    state.current_value.insert(cursor_pos, c);
                                     state.cursor_position += 1;
                                 }
-                            }
 
-                            if let Ok(mut state) = state_query.get_mut(state_entity) {
                                 // Update graphemes
-                                set_graphemes(
-                                    &mut state,
-                                    &font_assets,
-                                    &font_mapping,
-                                    &style_font,
-                                    &current_value,
-                                );
+                                set_graphemes(&mut state, &font_assets, &font_mapping, &style_font);
 
                                 set_new_cursor_position(
                                     &mut state,
@@ -226,22 +230,15 @@ pub fn text_box_render(
                                     &font_mapping,
                                     &style_font,
                                 );
+                                cloned_on_change.set_value(state.current_value.clone());
+                                event.add_system(cloned_on_change);
                             }
-
-                            cloned_on_change.set_value(current_value);
-                            event.add_system(cloned_on_change);
                         }
                         EventType::Focus => {
                             if let Ok(mut state) = state_query.get_mut(state_entity) {
                                 state.focused = true;
                                 // Update graphemes
-                                set_graphemes(
-                                    &mut state,
-                                    &font_assets,
-                                    &font_mapping,
-                                    &style_font,
-                                    &current_value,
-                                );
+                                set_graphemes(&mut state, &font_assets, &font_mapping, &style_font);
 
                                 state.cursor_position = state.graphemes.len();
 
@@ -266,7 +263,7 @@ pub fn text_box_render(
 
             let cursor_styles = KStyle {
                 background_color: Color::rgba(0.933, 0.745, 0.745, 1.0).into(),
-                position_type: crate::styles::KPositionType::SelfDirected.into(),
+                position_type: KPositionType::SelfDirected.into(),
                 top: Units::Pixels(5.0).into(),
                 left: Units::Pixels(state.cursor_x).into(),
                 width: Units::Pixels(2.0).into(),
@@ -274,6 +271,51 @@ pub fn text_box_render(
                 ..Default::default()
             };
 
+            let text_styles = KStyle {
+                top: Units::Stretch(1.0).into(),
+                bottom: Units::Stretch(1.0).into(),
+                ..Default::default()
+            };
+
+            let shift = if let Some(layout) = widget_context.get_layout(entity) {
+                let font_handle = match &styles.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 string_to_cursor = state.graphemes[0..state.cursor_position].join("");
+                    let measurement = font.measure(
+                        &string_to_cursor,
+                        TextProperties {
+                            font_size: 14.0,
+                            line_height: 18.0,
+                            max_size: (10000.0, 18.0),
+                            alignment: kayak_font::Alignment::Start,
+                            tab_size: 4,
+                        },
+                    );
+                    if measurement.size().0 > layout.width {
+                        (layout.width - measurement.size().0) - 20.0
+                    } else {
+                        0.0
+                    }
+                } else {
+                    0.0
+                }
+            } else {
+                0.0
+            };
+
+            let scroll_styles = KStyle {
+                position_type: KPositionType::SelfDirected.into(),
+                padding_left: StyleProp::Value(Units::Stretch(0.0)),
+                padding_right: StyleProp::Value(Units::Stretch(0.0)),
+                padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
+                padding_top: StyleProp::Value(Units::Stretch(1.0)),
+                left: Units::Pixels(shift).into(),
+                ..Default::default()
+            };
+
             let parent_id = Some(entity);
             rsx! {
                 <BackgroundBundle styles={background_styles}>
@@ -281,30 +323,27 @@ pub fn text_box_render(
                         height: Units::Pixels(26.0).into(),
                         padding_left: StyleProp::Value(Units::Stretch(0.0)),
                         padding_right: StyleProp::Value(Units::Stretch(0.0)),
-                        padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
-                        padding_top: StyleProp::Value(Units::Stretch(1.0)),
                         ..Default::default()
                     }}>
-                        <TextWidgetBundle
-                            text={TextProps {
-                                content: text_box.value.clone(),
-                                size: 14.0,
-                                line_height: Some(18.0),
-                                user_styles: KStyle {
-                                    top: Units::Stretch(1.0).into(),
-                                    bottom: Units::Stretch(1.0).into(),
+                        <ElementBundle styles={scroll_styles}>
+                            <TextWidgetBundle
+                                text={TextProps {
+                                    content: text_box.value.clone(),
+                                    size: 14.0,
+                                    line_height: Some(18.0),
+                                    user_styles: text_styles,
+                                    word_wrap: false,
                                     ..Default::default()
-                                },
-                                ..Default::default()
-                            }}
-                        />
-                        {
-                            if state.focused && state.cursor_visible {
-                                constructor! {
-                                    <BackgroundBundle styles={cursor_styles} />
+                                }}
+                            />
+                            {
+                                if state.focused && state.cursor_visible {
+                                    constructor! {
+                                        <BackgroundBundle styles={cursor_styles} />
+                                    }
                                 }
                             }
-                        }
+                        </ElementBundle>
                     </ClipBundle>
                 </BackgroundBundle>
             }
@@ -326,7 +365,6 @@ fn set_graphemes(
     font_assets: &Res<Assets<KayakFont>>,
     font_mapping: &FontMapping,
     style_font: &StyleProp<String>,
-    current_value: &String,
 ) {
     let font_handle = match style_font {
         StyleProp::Value(font) => font_mapping.get_handle(font.clone()).unwrap(),
@@ -335,7 +373,7 @@ fn set_graphemes(
 
     if let Some(font) = font_assets.get(&font_handle) {
         state.graphemes = font
-            .get_graphemes(&current_value)
+            .get_graphemes(&state.current_value)
             .iter()
             .map(|s| s.to_string())
             .collect::<Vec<_>>();