diff --git a/src/controller.rs b/src/controller.rs
index 8c7bf0ed0af3c6aa30598a17c3a1c1878880fa06..8c50cd49a2645e73991288d2ccb3cdcc6c793359 100644
--- a/src/controller.rs
+++ b/src/controller.rs
@@ -93,6 +93,8 @@ impl Default for View {
 }
 
 impl Element for View {
+    type T = ();
+
     fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>(
         &mut self,
         dt: &mut DT,
@@ -111,7 +113,9 @@ impl Element for View {
             View::MainMenu(m) => m.key_push(k),
             View::Contacts(c) => c.key_push(k),
             View::ContactOptions(c, _) => c.key_push(k),
-            View::ContactView(c, _) => c.key_push(k),
+            View::ContactView(c, _) => {
+                c.key_push(k);
+            }
             View::Chat(c) => c.key_push(k),
         }
     }
@@ -147,6 +151,8 @@ impl Controller {
 }
 
 impl Element for Controller {
+    type T = ();
+
     fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>(
         &mut self,
         dt: &mut DT,
@@ -161,7 +167,7 @@ impl Element for Controller {
     }
 
     fn key_push(&mut self, k: KeyCode) {
-        match (&self.cur_view, k) {
+        match (&mut self.cur_view, k) {
             (View::MainMenu(m), KeyCode::Touchpad) => {
                 if let Some(new_view) = View::from_main_menu_id(m.selected(), &self.contacts) {
                     self.cur_view = new_view;
@@ -204,6 +210,14 @@ impl Element for Controller {
                 self.cur_view = View::new_main_menu(id);
             }
 
+            (View::ContactView(cv, contact_id), k) => {
+                if cv.key_push(k) {
+                    self.contacts.contacts.remove(*contact_id);
+
+                    self.cur_view = View::new_contacts(*contact_id, &self.contacts);
+                }
+            }
+
             (_, k) => self.cur_view.key_push(k),
         }
     }
diff --git a/src/gui/button.rs b/src/gui/button.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a3aa538722942ab8a8cc679cbed8d84ffcca586f
--- /dev/null
+++ b/src/gui/button.rs
@@ -0,0 +1,101 @@
+use embedded_graphics::{
+    geometry::{Point, Size},
+    mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
+    pixelcolor::{Rgb565, RgbColor},
+    primitives::{Primitive, PrimitiveStyleBuilder, Rectangle},
+    text::{Baseline, Text},
+    Drawable,
+};
+
+use crate::keyboard::KeyCode;
+
+use super::Element;
+
+const CHAR_WIDTH: u32 = 10;
+const CHAR_HEIGHT: u32 = 20;
+const BORDER_WIDTH: u32 = 4;
+const BACKGROUND_COLOR: Rgb565 = Rgb565::new(31, 41, 0); // #FFA500
+const BORDER_COLOR: Rgb565 = Rgb565::new(31, 26, 0); // 0xFF6600
+
+pub struct Button {
+    x: i32,
+    y: i32,
+
+    text: &'static str,
+    pub selected: bool,
+}
+
+impl Button {
+    pub fn new(x: i32, y: i32, text: &'static str) -> Self {
+        Self {
+            x,
+            y,
+            text,
+            selected: false,
+        }
+    }
+}
+
+impl Element for Button {
+    type T = bool;
+
+    fn render<
+        E,
+        DT: embedded_graphics::prelude::DrawTarget<
+            Color = embedded_graphics::pixelcolor::Rgb565,
+            Error = E,
+        >,
+    >(
+        &mut self,
+        dt: &mut DT,
+    ) -> Result<(), E> {
+        let width = CHAR_WIDTH * u32::try_from(self.text.len()).unwrap() + BORDER_WIDTH * 2;
+        let height = CHAR_HEIGHT + BORDER_WIDTH * 2;
+
+        // Draw border/background
+        let unselected_rect_style = PrimitiveStyleBuilder::new()
+            .stroke_color(BORDER_COLOR)
+            .stroke_width(BORDER_WIDTH)
+            .fill_color(BACKGROUND_COLOR)
+            .build();
+
+        let selected_rect_style = PrimitiveStyleBuilder::new()
+            .stroke_color(BORDER_COLOR)
+            .stroke_width(BORDER_WIDTH)
+            .fill_color(Rgb565::WHITE)
+            .build();
+
+        let rect_style = if self.selected {
+            selected_rect_style
+        } else {
+            unselected_rect_style
+        };
+
+        Rectangle::new(Point::new(self.x, self.y), Size::new(width, height))
+            .into_styled(rect_style)
+            .draw(dt)?;
+
+        // Draw text
+        let text_x = self.x + i32::try_from(BORDER_WIDTH).unwrap();
+        let text_y = self.y + i32::try_from(BORDER_WIDTH).unwrap();
+
+        let text_style = MonoTextStyleBuilder::new()
+            .font(&FONT_10X20)
+            .text_color(Rgb565::BLACK)
+            .build();
+
+        Text::with_baseline(
+            self.text,
+            Point::new(text_x, text_y),
+            text_style,
+            Baseline::Top,
+        )
+        .draw(dt)?;
+
+        Ok(())
+    }
+
+    fn key_push(&mut self, k: KeyCode) -> bool {
+        k == KeyCode::Touchpad
+    }
+}
diff --git a/src/gui/chat.rs b/src/gui/chat.rs
index 31bcf5ca2120fd651aa7adbd05c1cf70b8bfdf1a..a1ba3a8f93f8c38f658ef51238226c4048ffa382 100644
--- a/src/gui/chat.rs
+++ b/src/gui/chat.rs
@@ -13,6 +13,8 @@ impl Chat {
 }
 
 impl Element for Chat {
+    type T = ();
+
     fn render<
         E,
         DT: embedded_graphics::prelude::DrawTarget<
diff --git a/src/gui/contact_view.rs b/src/gui/contact_view.rs
index 29a393e0ce03ffd6ee880ce27ea3fdf05abbbab3..14da19e9709d928098cb0d5d3e837f8a1e52e685 100644
--- a/src/gui/contact_view.rs
+++ b/src/gui/contact_view.rs
@@ -9,6 +9,7 @@ use embedded_graphics::{
 use heapless::String;
 
 use super::{
+    button::Button,
     scroll_tracker::{ScrollAction, ScrollTracker},
     textbox::{self, TextBox},
     Element,
@@ -24,6 +25,7 @@ pub struct ContactView {
     name: TextBox,
     callsign: TextBox,
     ssid: TextBox,
+    delete: Button,
     tracker: ScrollTracker,
     selected: usize,
 }
@@ -59,12 +61,16 @@ impl ContactView {
         ssid.set_text(&ssid_buf).unwrap();
         ssid.selected = false;
 
+        let mut delete = Button::new(LABEL_X, 160, "Delete");
+        delete.selected = false;
+
         Self {
             contact,
             editable,
             name,
             callsign,
             ssid,
+            delete,
             tracker: ScrollTracker::new(),
             selected: 0,
         }
@@ -92,6 +98,8 @@ impl ContactView {
 }
 
 impl Element for ContactView {
+    type T = bool;
+
     fn render<
         E,
         DT: embedded_graphics::prelude::DrawTarget<
@@ -135,7 +143,10 @@ impl Element for ContactView {
             self.callsign.render(dt)?;
 
             self.ssid.selected = self.selected == 2;
-            self.ssid.render(dt)?
+            self.ssid.render(dt)?;
+
+            self.delete.selected = self.selected == 3;
+            self.delete.render(dt)?;
         } else {
             Text::with_baseline(
                 &self.contact.name,
@@ -167,17 +178,21 @@ impl Element for ContactView {
         Ok(())
     }
 
-    fn key_push(&mut self, k: crate::keyboard::KeyCode) {
+    // Returns true if contact should be deleted
+    fn key_push(&mut self, k: crate::keyboard::KeyCode) -> bool {
         if !self.editable {
-            return;
+            return false;
         }
 
         match self.selected {
             0 => self.name.key_push(k),
             1 => self.callsign.key_push(k),
             2 => self.ssid.key_push(k),
+            3 => return self.delete.key_push(k),
             _ => unreachable!(),
         }
+
+        false
     }
 
     fn touchpad_scroll(&mut self, _x: i8, y: i8) {
@@ -188,7 +203,7 @@ impl Element for ContactView {
                 }
             }
             ScrollAction::Next => {
-                if self.selected < 2 {
+                if self.selected < 3 {
                     self.selected += 1;
                 }
             }
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
index 7f8e3d4e8f526ca3237188e27ab1a6bf5c02564f..bb79983b512fc108c6256624b63dfbdd965ba9a3 100644
--- a/src/gui/mod.rs
+++ b/src/gui/mod.rs
@@ -2,6 +2,7 @@ use embedded_graphics::{draw_target::DrawTarget, pixelcolor::Rgb565};
 
 use crate::keyboard::KeyCode;
 
+pub mod button;
 pub mod chat;
 pub mod contact_view;
 pub mod scroll_tracker;
@@ -10,12 +11,14 @@ pub mod status;
 pub mod textbox;
 
 pub trait Element {
+    type T;
+
     fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>(
         &mut self,
         dt: &mut DT,
     ) -> Result<(), E>;
 
-    fn key_push(&mut self, _k: KeyCode) {}
+    fn key_push(&mut self, _k: KeyCode) -> Self::T;
 
     fn touchpad_scroll(&mut self, _x: i8, _y: i8) {}
 }
diff --git a/src/gui/selector.rs b/src/gui/selector.rs
index 1e44065cde455048e285f748d6fcb76cf484ff5a..39894b8c693764fa9ebfa1b58872d565a3be38cc 100644
--- a/src/gui/selector.rs
+++ b/src/gui/selector.rs
@@ -9,6 +9,8 @@ use embedded_graphics::{
     Drawable,
 };
 
+use crate::keyboard::KeyCode;
+
 use super::{
     scroll_tracker::{ScrollAction, ScrollTracker},
     Element,
@@ -64,6 +66,8 @@ impl<T: AsRef<str>, V: AsRef<[T]>> Selector<T, V> {
 }
 
 impl<T: AsRef<str>, V: AsRef<[T]>> Element for Selector<T, V> {
+    type T = ();
+
     fn render<
         E,
         DT: embedded_graphics::prelude::DrawTarget<
@@ -133,6 +137,8 @@ impl<T: AsRef<str>, V: AsRef<[T]>> Element for Selector<T, V> {
         Ok(())
     }
 
+    fn key_push(&mut self, _k: KeyCode) {}
+
     fn touchpad_scroll(&mut self, _x: i8, y: i8) {
         match self.tracker.scroll(y) {
             ScrollAction::Previous => {
diff --git a/src/gui/status.rs b/src/gui/status.rs
index 9f0b3a8c276776b8221fc0ddb3fc75c6431990a1..999ba938fc7aeb3b61776fdfe63b48339967e672 100644
--- a/src/gui/status.rs
+++ b/src/gui/status.rs
@@ -28,6 +28,8 @@ impl StatusBar {
 }
 
 impl Element for StatusBar {
+    type T = ();
+
     fn render<
         E,
         DT: embedded_graphics::prelude::DrawTarget<
diff --git a/src/gui/textbox.rs b/src/gui/textbox.rs
index 490676d85580de80b249baa12af7645552bfc0d8..9a4fbdecce813b456fa557eda85478bea084c01c 100644
--- a/src/gui/textbox.rs
+++ b/src/gui/textbox.rs
@@ -112,6 +112,8 @@ impl TextBox {
 }
 
 impl Element for TextBox {
+    type T = ();
+
     fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>(
         &mut self,
         dt: &mut DT,