diff --git a/examples/login.rs b/examples/login.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ea2e54ea1f714da788edde7f55ba929fa3ab5d8b
--- /dev/null
+++ b/examples/login.rs
@@ -0,0 +1,138 @@
+use bevy::{prelude::*, window::PrimaryWindow};
+use bevy_cosmic_edit::*;
+
+fn setup(mut commands: Commands) {
+    commands.spawn(Camera2dBundle::default());
+
+    commands
+        .spawn(NodeBundle {
+            style: Style {
+                flex_direction: FlexDirection::Column,
+                width: Val::Px(300.0),
+                ..default()
+            },
+            ..default()
+        })
+        .with_children(|root| {
+            root.spawn(CosmicEditBundle {
+                max_lines: CosmicMaxLines(1),
+                ..default()
+            })
+            .insert(ButtonBundle {
+                style: Style {
+                    // Size and position of text box
+                    width: Val::Px(300.),
+                    height: Val::Px(50.),
+                    margin: UiRect::all(Val::Px(15.0)),
+                    ..default()
+                },
+                background_color: BackgroundColor(Color::WHITE),
+                ..default()
+            })
+            .insert(CosmicEditPlaceholderBundle {
+                text_setter: PlaceholderText(CosmicText::OneStyle("Username".into())),
+                attrs: PlaceholderAttrs(AttrsOwned::new(
+                    Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
+                )),
+            });
+
+            root.spawn(CosmicEditBundle {
+                max_lines: CosmicMaxLines(1),
+                ..default()
+            })
+            .insert(ButtonBundle {
+                style: Style {
+                    // Size and position of text box
+                    width: Val::Px(300.),
+                    height: Val::Px(50.),
+                    margin: UiRect::all(Val::Px(15.0)),
+                    ..default()
+                },
+                background_color: BackgroundColor(Color::WHITE),
+                ..default()
+            })
+            .insert(CosmicEditPlaceholderBundle {
+                text_setter: PlaceholderText(CosmicText::OneStyle("Password".into())),
+                attrs: PlaceholderAttrs(AttrsOwned::new(
+                    Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
+                )),
+            })
+            .insert(PasswordInput);
+        });
+}
+
+fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor {
+    cosmic_text::Color::rgba(
+        (color.r() * 255.) as u8,
+        (color.g() * 255.) as u8,
+        (color.b() * 255.) as u8,
+        (color.a() * 255.) as u8,
+    )
+}
+
+fn change_active_editor_ui(
+    mut commands: Commands,
+    mut interaction_query: Query<
+        (&Interaction, Entity),
+        (
+            Changed<Interaction>,
+            (With<CosmicEditor>, Without<ReadOnly>),
+        ),
+    >,
+) {
+    for (interaction, entity) in interaction_query.iter_mut() {
+        if let Interaction::Pressed = interaction {
+            commands.insert_resource(Focus(Some(entity)));
+        }
+    }
+}
+
+fn change_active_editor_sprite(
+    mut commands: Commands,
+    windows: Query<&Window, With<PrimaryWindow>>,
+    buttons: Res<Input<MouseButton>>,
+    mut cosmic_edit_query: Query<
+        (&mut Sprite, &GlobalTransform, Entity),
+        (With<CosmicEditor>, Without<ReadOnly>),
+    >,
+    camera_q: Query<(&Camera, &GlobalTransform)>,
+) {
+    let window = windows.single();
+    let (camera, camera_transform) = camera_q.single();
+    if buttons.just_pressed(MouseButton::Left) {
+        for (sprite, node_transform, entity) in &mut cosmic_edit_query.iter_mut() {
+            let size = sprite.custom_size.unwrap_or(Vec2::new(1., 1.));
+            let x_min = node_transform.affine().translation.x - size.x / 2.;
+            let y_min = node_transform.affine().translation.y - size.y / 2.;
+            let x_max = node_transform.affine().translation.x + size.x / 2.;
+            let y_max = node_transform.affine().translation.y + size.y / 2.;
+            if let Some(pos) = window.cursor_position() {
+                if let Some(pos) = camera.viewport_to_world_2d(camera_transform, pos) {
+                    if x_min < pos.x && pos.x < x_max && y_min < pos.y && pos.y < y_max {
+                        commands.insert_resource(Focus(Some(entity)))
+                    };
+                }
+            };
+        }
+    }
+}
+
+fn print_changed_input(mut evr_type: EventReader<CosmicTextChanged>) {
+    for ev in evr_type.iter() {
+        println!("Changed: {}", ev.0 .1);
+    }
+}
+
+fn main() {
+    App::new()
+        .add_plugins(DefaultPlugins)
+        .add_plugins(CosmicEditPlugin {
+            change_cursor: CursorConfig::Default,
+            ..default()
+        })
+        .add_systems(Startup, setup)
+        .add_systems(Update, change_active_editor_ui)
+        .add_systems(Update, change_active_editor_sprite)
+        .add_systems(Update, print_changed_input)
+        .run();
+}
diff --git a/src/font/FiraMono-Regular-subset.ttf b/src/font/FiraMono-Regular-subset.ttf
index bca868cdd34be48bbd1e23cbe0e91a2ef6c04f0e..c0fee60b0317902b0378bb21537dadf053fcef24 100644
Binary files a/src/font/FiraMono-Regular-subset.ttf and b/src/font/FiraMono-Regular-subset.ttf differ
diff --git a/src/lib.rs b/src/lib.rs
index a13ee14e62e7c271609c4c77b066df407fe742e3..d6d80a3e89afb96ab2b908564c878da296105de8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -202,6 +202,9 @@ pub struct PlaceholderText(pub CosmicText);
 #[derive(Component)]
 pub struct PlaceholderAttrs(pub AttrsOwned);
 
+#[derive(Component)]
+pub struct PasswordInput;
+
 impl Default for PlaceholderAttrs {
     fn default() -> Self {
         Self(AttrsOwned::new(Attrs::new()))
diff --git a/src/render.rs b/src/render.rs
index cb585f6028b8818963b41d77aca7c4469d8f6b6b..4f0b93547773fa756c667b29de190bb9cdb774f3 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -12,7 +12,7 @@ use image::{imageops::FilterType, GenericImageView};
 use crate::{
     get_text_size, get_x_offset_center, get_y_offset_center, CosmicAttrs, CosmicBackground,
     CosmicCanvas, CosmicEditor, CosmicFontSystem, CosmicMetrics, CosmicMode, CosmicTextPosition,
-    FillColor, Focus, Placeholder, ReadOnly, XOffset,
+    FillColor, Focus, PasswordInput, Placeholder, ReadOnly, XOffset,
 };
 
 #[derive(Resource)]
@@ -43,6 +43,7 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         &mut XOffset,
         &CosmicMode,
         Option<&mut Placeholder>,
+        Option<&PasswordInput>,
     )>,
     mut font_system: ResMut<CosmicFontSystem>,
 ) {
@@ -50,7 +51,7 @@ pub(crate) fn cosmic_edit_redraw_buffer(
     let scale = primary_window.scale_factor() as f32;
 
     for (
-        mut editor,
+        mut cosmic_editor,
         attrs,
         background_image,
         fill_color,
@@ -62,18 +63,30 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         mut x_offset,
         mode,
         mut placeholder_opt,
+        password_opt,
     ) in &mut cosmic_edit_query.iter_mut()
     {
-        if !editor.0.buffer().redraw() {
+        if !cosmic_editor.0.buffer().redraw() {
             continue;
         }
 
+        let current_text = cosmic_editor.get_text();
+
+        // intercept text for password inputs
+        if password_opt.is_some() && !current_text.is_empty() {
+            cosmic_editor.set_text(
+                crate::CosmicText::OneStyle("•".repeat(current_text.len())),
+                attrs.0.clone(),
+                &mut font_system.0,
+            );
+        }
+
         // Check for placeholder, replace editor if found and buffer is empty
-        let editor = if editor.get_text().is_empty() && placeholder_opt.is_some() {
+        let editor = if current_text.is_empty() && placeholder_opt.is_some() {
             let placeholder = &mut placeholder_opt.as_mut().unwrap().0 .0;
             placeholder.buffer_mut().set_redraw(true);
 
-            editor.0.buffer_mut().set_redraw(true);
+            cosmic_editor.0.buffer_mut().set_redraw(true);
 
             let mut cursor = placeholder.cursor();
             cursor.index = 0;
@@ -81,7 +94,7 @@ pub(crate) fn cosmic_edit_redraw_buffer(
             *x_offset = XOffset(None);
             placeholder
         } else {
-            &mut editor.0
+            &mut cosmic_editor.0
         };
 
         editor.shape_as_needed(&mut font_system.0);
@@ -262,6 +275,15 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         }
 
         editor.buffer_mut().set_redraw(false);
+
+        // reset intercepted text
+        if password_opt.is_some() && !current_text.is_empty() {
+            cosmic_editor.set_text(
+                crate::CosmicText::OneStyle(current_text),
+                attrs.0.clone(),
+                &mut font_system.0,
+            );
+        }
     }
 }