diff --git a/examples/restricted_input.rs b/examples/restricted_input.rs
new file mode 100644
index 0000000000000000000000000000000000000000..18de1076cb28630c8dcab218529f648c4d999ff4
--- /dev/null
+++ b/examples/restricted_input.rs
@@ -0,0 +1,48 @@
+use bevy::prelude::*;
+use bevy_cosmic_edit::{
+    change_active_editor_sprite, change_active_editor_ui, ActiveEditor, CosmicAttrs,
+    CosmicEditPlugin, CosmicEditUiBundle, CosmicMaxChars, CosmicMaxLines, CosmicMetrics,
+};
+use cosmic_text::{Attrs, AttrsOwned};
+
+fn setup(mut commands: Commands) {
+    commands.spawn(Camera2dBundle::default());
+
+    let attrs = AttrsOwned::new(Attrs::new().color(cosmic_text::Color::rgb(69, 69, 69)));
+
+    let editor = commands
+        .spawn(CosmicEditUiBundle {
+            style: Style {
+                // Size and position of text box
+                width: Val::Percent(20.),
+                height: Val::Px(50.),
+                left: Val::Percent(40.),
+                top: Val::Px(100.),
+                ..default()
+            },
+            cosmic_attrs: CosmicAttrs(attrs.clone()),
+            cosmic_metrics: CosmicMetrics {
+                font_size: 16.,
+                line_height: 16.,
+                ..Default::default()
+            },
+            max_chars: CosmicMaxChars(15),
+            max_lines: CosmicMaxLines(1),
+            ..default()
+        })
+        .id();
+
+    commands.insert_resource(ActiveEditor {
+        entity: Some(editor),
+    });
+}
+
+fn main() {
+    App::new()
+        .add_plugins(DefaultPlugins)
+        .add_plugins(CosmicEditPlugin::default())
+        .add_systems(Startup, setup)
+        .add_systems(Update, change_active_editor_ui)
+        .add_systems(Update, change_active_editor_sprite)
+        .run();
+}
diff --git a/src/lib.rs b/src/lib.rs
index ce3c3489063e023eaf44397a714002f5cc07fde6..c1b5dbb2fbac38f87a35782da20ca66a68cba863 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -33,6 +33,7 @@ pub enum CosmicTextPosition {
     TopLeft,
 }
 
+// TODO docs
 #[derive(Clone, Component)]
 pub struct CosmicMetrics {
     pub font_size: f32,
@@ -77,6 +78,7 @@ impl CosmicEditor {
         editor.buffer_mut().lines.clear();
         match text {
             CosmicText::OneStyle(text) => {
+                // TODO get access to max_chars and max_lines here, implement limits
                 editor.buffer_mut().set_text(
                     font_system,
                     text.as_str(),
@@ -202,6 +204,12 @@ impl Default for CosmicAttrs {
 #[derive(Component, Default)]
 pub struct CosmicBackground(pub Option<Handle<Image>>);
 
+#[derive(Component, Default)]
+pub struct CosmicMaxLines(pub usize);
+
+#[derive(Component, Default)]
+pub struct CosmicMaxChars(pub usize);
+
 #[derive(Bundle)]
 pub struct CosmicEditUiBundle {
     // Bevy UI bits
@@ -251,6 +259,10 @@ pub struct CosmicEditUiBundle {
     pub cosmic_attrs: CosmicAttrs,
     /// bg img
     pub background_image: CosmicBackground,
+    /// How many lines are allowed in buffer, 0 for no limit
+    pub max_lines: CosmicMaxLines,
+    /// How many characters are allowed in buffer, 0 for no limit
+    pub max_chars: CosmicMaxChars,
 }
 
 impl CosmicEditUiBundle {
@@ -287,6 +299,8 @@ impl Default for CosmicEditUiBundle {
             cosmic_edit_history: Default::default(),
             cosmic_attrs: Default::default(),
             background_image: Default::default(),
+            max_lines: Default::default(),
+            max_chars: Default::default(),
         }
     }
 }
@@ -317,6 +331,10 @@ pub struct CosmicEditSpriteBundle {
     pub cosmic_attrs: CosmicAttrs,
     /// bg img
     pub background_image: CosmicBackground,
+    /// How many lines are allowed in buffer, 0 for no limit
+    pub max_lines: CosmicMaxLines,
+    /// How many characters are allowed in buffer, 0 for no limit
+    pub max_chars: CosmicMaxChars,
 }
 
 impl CosmicEditSpriteBundle {
@@ -340,15 +358,15 @@ impl Default for CosmicEditSpriteBundle {
             texture: DEFAULT_IMAGE_HANDLE.typed(),
             visibility: Visibility::Hidden,
             computed_visibility: Default::default(),
-            //
             background_color: Default::default(),
-            //
             editor: Default::default(),
             text_position: Default::default(),
             cosmic_metrics: Default::default(),
             cosmic_edit_history: Default::default(),
             cosmic_attrs: Default::default(),
             background_image: Default::default(),
+            max_lines: Default::default(),
+            max_chars: Default::default(),
         }
     }
 }
@@ -608,6 +626,8 @@ pub fn cosmic_edit_bevy_events(
             &GlobalTransform,
             &CosmicAttrs,
             &CosmicTextPosition,
+            &CosmicMaxLines,
+            &CosmicMaxChars,
             Entity,
         ),
         With<CosmicEditor>,
@@ -625,8 +645,16 @@ pub fn cosmic_edit_bevy_events(
     let primary_window = windows.single();
     let scale_factor = primary_window.scale_factor() as f32;
     let (camera, camera_transform) = camera_q.iter().find(|(c, _)| c.is_active).unwrap();
-    for (mut editor, mut edit_history, node_transform, attrs, text_position, entity) in
-        &mut cosmic_edit_query.iter_mut()
+    for (
+        mut editor,
+        mut edit_history,
+        node_transform,
+        attrs,
+        text_position,
+        max_lines,
+        max_chars,
+        entity,
+    ) in &mut cosmic_edit_query.iter_mut()
     {
         let readonly = readonly_query.get(entity).is_ok();
 
@@ -639,7 +667,6 @@ pub fn cosmic_edit_bevy_events(
             }
         };
 
-        let editor = &mut editor.0;
         let attrs = &attrs.0;
 
         if active_editor.entity == Some(entity) {
@@ -657,10 +684,10 @@ pub fn cosmic_edit_bevy_events(
             let option = keys.any_pressed([KeyCode::AltLeft, KeyCode::AltRight]);
 
             // if shift key is pressed
-            let already_has_selection = editor.select_opt().is_some();
+            let already_has_selection = editor.0.select_opt().is_some();
             if shift && !already_has_selection {
-                let cursor = editor.cursor();
-                editor.set_select_opt(Some(cursor));
+                let cursor = editor.0.cursor();
+                editor.0.set_select_opt(Some(cursor));
             }
 
             #[cfg(target_os = "macos")]
@@ -669,87 +696,87 @@ pub fn cosmic_edit_bevy_events(
             let should_jump = command;
 
             if should_jump && keys.just_pressed(KeyCode::Left) {
-                editor.action(&mut font_system.0, Action::PreviousWord);
+                editor.0.action(&mut font_system.0, Action::PreviousWord);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if should_jump && keys.just_pressed(KeyCode::Right) {
-                editor.action(&mut font_system.0, Action::NextWord);
+                editor.0.action(&mut font_system.0, Action::NextWord);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if should_jump && keys.just_pressed(KeyCode::Home) {
-                editor.action(&mut font_system.0, Action::BufferStart);
+                editor.0.action(&mut font_system.0, Action::BufferStart);
                 // there's a bug with cosmic text where it doesn't update the visual cursor for this action
                 // TODO: fix upstream
-                editor.buffer_mut().set_redraw(true);
+                editor.0.buffer_mut().set_redraw(true);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if should_jump && keys.just_pressed(KeyCode::End) {
-                editor.action(&mut font_system.0, Action::BufferEnd);
+                editor.0.action(&mut font_system.0, Action::BufferEnd);
                 // there's a bug with cosmic text where it doesn't update the visual cursor for this action
                 // TODO: fix upstream
-                editor.buffer_mut().set_redraw(true);
+                editor.0.buffer_mut().set_redraw(true);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
 
             if keys.just_pressed(KeyCode::Left) {
-                editor.action(&mut font_system.0, Action::Left);
+                editor.0.action(&mut font_system.0, Action::Left);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if keys.just_pressed(KeyCode::Right) {
-                editor.action(&mut font_system.0, Action::Right);
+                editor.0.action(&mut font_system.0, Action::Right);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if keys.just_pressed(KeyCode::Up) {
-                editor.action(&mut font_system.0, Action::Up);
+                editor.0.action(&mut font_system.0, Action::Up);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if keys.just_pressed(KeyCode::Down) {
-                editor.action(&mut font_system.0, Action::Down);
+                editor.0.action(&mut font_system.0, Action::Down);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
 
             if !readonly && keys.just_pressed(KeyCode::Back) {
                 #[cfg(target_arch = "wasm32")]
-                editor.action(&mut font_system.0, Action::Backspace);
+                editor.0.action(&mut font_system.0, Action::Backspace);
                 *is_deleting = true;
             }
             if !readonly && keys.just_released(KeyCode::Back) {
                 *is_deleting = false;
             }
             if !readonly && keys.just_pressed(KeyCode::Delete) {
-                editor.action(&mut font_system.0, Action::Delete);
+                editor.0.action(&mut font_system.0, Action::Delete);
             }
             if keys.just_pressed(KeyCode::Escape) {
-                editor.action(&mut font_system.0, Action::Escape);
+                editor.0.action(&mut font_system.0, Action::Escape);
             }
             if command && keys.just_pressed(KeyCode::A) {
-                editor.action(&mut font_system.0, Action::BufferEnd);
-                let current_cursor = editor.cursor();
-                editor.set_select_opt(Some(Cursor {
+                editor.0.action(&mut font_system.0, Action::BufferEnd);
+                let current_cursor = editor.0.cursor();
+                editor.0.set_select_opt(Some(Cursor {
                     line: 0,
                     index: 0,
                     affinity: current_cursor.affinity,
@@ -758,30 +785,30 @@ pub fn cosmic_edit_bevy_events(
                 return;
             }
             if keys.just_pressed(KeyCode::Home) {
-                editor.action(&mut font_system.0, Action::Home);
+                editor.0.action(&mut font_system.0, Action::Home);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if keys.just_pressed(KeyCode::End) {
-                editor.action(&mut font_system.0, Action::End);
+                editor.0.action(&mut font_system.0, Action::End);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if keys.just_pressed(KeyCode::PageUp) {
-                editor.action(&mut font_system.0, Action::PageUp);
+                editor.0.action(&mut font_system.0, Action::PageUp);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
             if keys.just_pressed(KeyCode::PageDown) {
-                editor.action(&mut font_system.0, Action::PageDown);
+                editor.0.action(&mut font_system.0, Action::PageDown);
                 if !shift {
-                    editor.set_select_opt(None);
+                    editor.0.set_select_opt(None);
                 }
                 return;
             }
@@ -802,7 +829,7 @@ pub fn cosmic_edit_bevy_events(
                 }
                 let idx = edit_history.current_edit + 1;
                 if let Some(current_edit) = edits.get(idx) {
-                    editor.buffer_mut().lines.clear();
+                    editor.0.buffer_mut().lines.clear();
                     for line in current_edit.lines.iter() {
                         let mut line_text = String::new();
                         let mut attrs_list = AttrsList::new(attrs.as_attrs());
@@ -812,14 +839,14 @@ pub fn cosmic_edit_bevy_events(
                             let end = line_text.len();
                             attrs_list.add_span(start..end, attrs.as_attrs());
                         }
-                        editor.buffer_mut().lines.push(BufferLine::new(
+                        editor.0.buffer_mut().lines.push(BufferLine::new(
                             line_text,
                             attrs_list,
                             Shaping::Advanced,
                         ));
                     }
-                    editor.set_cursor(current_edit.cursor);
-                    editor.buffer_mut().set_redraw(true);
+                    editor.0.set_cursor(current_edit.cursor);
+                    editor.0.buffer_mut().set_redraw(true);
                     edit_history.current_edit += 1;
                 }
                 *undoredo_duration = Some(Duration::from_millis(now_ms as u64));
@@ -838,7 +865,7 @@ pub fn cosmic_edit_bevy_events(
                 }
                 let idx = edit_history.current_edit - 1;
                 if let Some(current_edit) = edits.get(idx) {
-                    editor.buffer_mut().lines.clear();
+                    editor.0.buffer_mut().lines.clear();
                     for line in current_edit.lines.iter() {
                         let mut line_text = String::new();
                         let mut attrs_list = AttrsList::new(attrs.as_attrs());
@@ -848,14 +875,14 @@ pub fn cosmic_edit_bevy_events(
                             let end = line_text.len();
                             attrs_list.add_span(start..end, attrs.as_attrs());
                         }
-                        editor.buffer_mut().lines.push(BufferLine::new(
+                        editor.0.buffer_mut().lines.push(BufferLine::new(
                             line_text,
                             attrs_list,
                             Shaping::Advanced,
                         ));
                     }
-                    editor.set_cursor(current_edit.cursor);
-                    editor.buffer_mut().set_redraw(true);
+                    editor.0.set_cursor(current_edit.cursor);
+                    editor.0.buffer_mut().set_redraw(true);
                     edit_history.current_edit -= 1;
                 }
                 *undoredo_duration = Some(Duration::from_millis(now_ms as u64));
@@ -867,22 +894,32 @@ pub fn cosmic_edit_bevy_events(
             {
                 if let Ok(mut clipboard) = arboard::Clipboard::new() {
                     if command && keys.just_pressed(KeyCode::C) {
-                        if let Some(text) = editor.copy_selection() {
+                        if let Some(text) = editor.0.copy_selection() {
                             clipboard.set_text(text).unwrap();
                             return;
                         }
                     }
                     if !readonly && command && keys.just_pressed(KeyCode::X) {
-                        if let Some(text) = editor.copy_selection() {
+                        if let Some(text) = editor.0.copy_selection() {
                             clipboard.set_text(text).unwrap();
-                            editor.delete_selection();
+                            editor.0.delete_selection();
                         }
                         is_clipboard = true;
                     }
                     if !readonly && command && keys.just_pressed(KeyCode::V) {
                         if let Ok(text) = clipboard.get_text() {
                             for c in text.chars() {
-                                editor.action(&mut font_system.0, Action::Insert(c));
+                                if max_chars.0 == 0 || editor.get_text().len() < max_chars.0 {
+                                    if c == 0xA as char {
+                                        if max_lines.0 == 0
+                                            || editor.0.buffer().lines.len() < max_lines.0
+                                        {
+                                            editor.0.action(&mut font_system.0, Action::Insert(c));
+                                        }
+                                    } else {
+                                        editor.0.action(&mut font_system.0, Action::Insert(c));
+                                    }
+                                }
                             }
                         }
                         is_clipboard = true;
@@ -890,9 +927,10 @@ pub fn cosmic_edit_bevy_events(
                 }
             }
             let (offset_x, offset_y) = match text_position {
-                CosmicTextPosition::Center => {
-                    (get_x_offset(editor.buffer()), get_y_offset(editor.buffer()))
-                }
+                CosmicTextPosition::Center => (
+                    get_x_offset(editor.0.buffer()),
+                    get_y_offset(editor.0.buffer()),
+                ),
                 CosmicTextPosition::TopLeft => (0, 0),
             };
             let point = |node_cursor_pos: (f32, f32)| {
@@ -913,9 +951,9 @@ pub fn cosmic_edit_bevy_events(
                 ) {
                     let (x, y) = point(node_cursor_pos);
                     if shift {
-                        editor.action(&mut font_system.0, Action::Drag { x, y });
+                        editor.0.action(&mut font_system.0, Action::Drag { x, y });
                     } else {
-                        editor.action(&mut font_system.0, Action::Click { x, y });
+                        editor.0.action(&mut font_system.0, Action::Click { x, y });
                     }
                 }
                 return;
@@ -931,9 +969,9 @@ pub fn cosmic_edit_bevy_events(
                 ) {
                     let (x, y) = point(node_cursor_pos);
                     if active_editor.is_changed() && !shift {
-                        editor.action(&mut font_system.0, Action::Click { x, y });
+                        editor.0.action(&mut font_system.0, Action::Click { x, y });
                     } else {
-                        editor.action(&mut font_system.0, Action::Drag { x, y });
+                        editor.0.action(&mut font_system.0, Action::Drag { x, y });
                     }
                 }
                 return;
@@ -941,7 +979,7 @@ pub fn cosmic_edit_bevy_events(
             for ev in scroll_evr.iter() {
                 match ev.unit {
                     MouseScrollUnit::Line => {
-                        editor.action(
+                        editor.0.action(
                             &mut font_system.0,
                             Action::Scroll {
                                 lines: -ev.y as i32,
@@ -949,8 +987,8 @@ pub fn cosmic_edit_bevy_events(
                         );
                     }
                     MouseScrollUnit::Pixel => {
-                        let line_height = editor.buffer().metrics().line_height;
-                        editor.action(
+                        let line_height = editor.0.buffer().metrics().line_height;
+                        editor.0.action(
                             &mut font_system.0,
                             Action::Scroll {
                                 lines: -(ev.y / line_height) as i32,
@@ -965,9 +1003,10 @@ pub fn cosmic_edit_bevy_events(
             }
 
             // fix for issue #8
-            if let Some(select) = editor.select_opt() {
-                if editor.cursor().line == select.line && editor.cursor().index == select.index {
-                    editor.set_select_opt(None);
+            if let Some(select) = editor.0.select_opt() {
+                if editor.0.cursor().line == select.line && editor.0.cursor().index == select.index
+                {
+                    editor.0.set_select_opt(None);
                 }
             }
 
@@ -976,17 +1015,23 @@ pub fn cosmic_edit_bevy_events(
             if keys.just_pressed(KeyCode::Return) {
                 is_return = true;
                 is_edit = true;
-                // to have new line on wasm rather than E
-                editor.action(&mut font_system.0, Action::Insert('\n'));
+                if (max_lines.0 == 0 || editor.0.buffer().lines.len() < max_lines.0)
+                    && (max_chars.0 == 0 || editor.get_text().len() < max_chars.0)
+                {
+                    // to have new line on wasm rather than E
+                    editor.0.action(&mut font_system.0, Action::Insert('\n'));
+                }
             }
 
             if !(is_clipboard || is_return) {
                 for char_ev in char_evr.iter() {
                     is_edit = true;
                     if *is_deleting {
-                        editor.action(&mut font_system.0, Action::Backspace);
-                    } else {
-                        editor.action(&mut font_system.0, Action::Insert(char_ev.char));
+                        editor.0.action(&mut font_system.0, Action::Backspace);
+                    } else if max_chars.0 == 0 || editor.get_text().len() < max_chars.0 {
+                        editor
+                            .0
+                            .action(&mut font_system.0, Action::Insert(char_ev.char));
                     }
                 }
             }
@@ -999,11 +1044,11 @@ pub fn cosmic_edit_bevy_events(
                 if Duration::from_millis(now_ms as u64) - last_edit_duration
                     > Duration::from_millis(150)
                 {
-                    save_edit_history(editor, attrs, &mut edit_history);
+                    save_edit_history(&mut editor.0, attrs, &mut edit_history);
                     *edits_duration = Some(Duration::from_millis(now_ms as u64));
                 }
             } else {
-                save_edit_history(editor, attrs, &mut edit_history);
+                save_edit_history(&mut editor.0, attrs, &mut edit_history);
                 *edits_duration = Some(Duration::from_millis(now_ms as u64));
             }
         }