diff --git a/src/contact.rs b/src/contact.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3b43b6133d7acda25f241629a1f9dbe17b30e9cc
--- /dev/null
+++ b/src/contact.rs
@@ -0,0 +1,42 @@
+use heapless::{String, Vec};
+
+#[derive(Debug, Clone)]
+pub struct Contact {
+    callsign: String<10>,
+    ssid: u8,
+    pub name: String<30>,
+}
+
+#[derive(Debug)]
+pub struct ContactGroup {
+    pub contacts: Vec<Contact, 20>,
+}
+
+impl Default for ContactGroup {
+    fn default() -> Self {
+        let mut contacts = Vec::new();
+        contacts
+            .push(Contact {
+                callsign: String::try_from("VA3QAK").unwrap(),
+                ssid: 19,
+                name: String::try_from("Adrian").unwrap(),
+            })
+            .unwrap();
+        contacts
+            .push(Contact {
+                callsign: String::try_from("VE3SVF").unwrap(),
+                ssid: 239,
+                name: String::try_from("Sasha").unwrap(),
+            })
+            .unwrap();
+        contacts
+            .push(Contact {
+                callsign: String::try_from("VE3KCN").unwrap(),
+                ssid: 19,
+                name: String::try_from("Cam").unwrap(),
+            })
+            .unwrap();
+
+        Self { contacts }
+    }
+}
diff --git a/src/controller.rs b/src/controller.rs
index 9889ed388b8152536bce66d8fb201cde5f54d3c2..c091e51dbe9b9b401e610d4fe482001ce4ff9b34 100644
--- a/src/controller.rs
+++ b/src/controller.rs
@@ -1,25 +1,96 @@
 use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget};
+use heapless::{String, Vec};
 
 use crate::{
+    contact::{Contact, ContactGroup},
     gui::{chat::Chat, selector::Selector, status::StatusBar, Element},
     keyboard::KeyCode,
     voltage::VoltMeter,
 };
 
+pub enum View {
+    MainMenu(Selector<&'static str, [&'static str; 3]>),
+    Contacts(Selector<String<30>, Vec<String<30>, 3>>),
+    Chat(Chat),
+}
+
+impl View {
+    pub fn main_menu_with_id(id: usize) -> Self {
+        let menu = Selector::new(["Contacts", "Messages", "Settings"], id);
+        Self::MainMenu(menu)
+    }
+
+    pub fn from_main_menu_id(id: usize) -> Option<Self> {
+        match id {
+            0 => {
+                let group = ContactGroup::default();
+                let contact_names: Vec<_, 3> =
+                    group.contacts.iter().map(|c| c.name.clone()).collect();
+
+                Some(Self::Contacts(Selector::new(
+                    contact_names.try_into().unwrap(),
+                    0,
+                )))
+            }
+            1 => Some(Self::Chat(Chat::new())),
+            _ => None,
+        }
+    }
+
+    pub fn to_main_menu_id(&self) -> usize {
+        match self {
+            View::MainMenu(_) => unreachable!(),
+            View::Contacts(_) => 0,
+            View::Chat(_) => 1,
+        }
+    }
+}
+
+impl Default for View {
+    fn default() -> Self {
+        Self::main_menu_with_id(0)
+    }
+}
+
+impl Element for View {
+    fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>(
+        &mut self,
+        dt: &mut DT,
+    ) -> Result<(), E> {
+        match self {
+            View::MainMenu(m) => m.render(dt),
+            View::Contacts(c) => c.render(dt),
+            View::Chat(c) => c.render(dt),
+        }
+    }
+
+    fn key_push(&mut self, k: KeyCode) {
+        match self {
+            View::MainMenu(m) => m.key_push(k),
+            View::Contacts(c) => c.key_push(k),
+            View::Chat(c) => c.key_push(k),
+        }
+    }
+
+    fn touchpad_scroll(&mut self, x: i8, y: i8) {
+        match self {
+            View::MainMenu(m) => m.touchpad_scroll(x, y),
+            View::Contacts(c) => c.touchpad_scroll(x, y),
+            View::Chat(c) => c.touchpad_scroll(x, y),
+        }
+    }
+}
+
 pub struct Controller {
     status: StatusBar,
-    selector: Selector<4>,
-    chat: Chat,
-    in_chat: bool,
+    cur_view: View,
 }
 
 impl Controller {
     pub fn new(volt_meter: VoltMeter) -> Self {
         Self {
             status: StatusBar::new(volt_meter),
-            selector: Selector::new(["Contacts", "Messages", "Settings", "mrrp meow"]),
-            chat: Chat::new(),
-            in_chat: false,
+            cur_view: View::default(),
         }
     }
 }
@@ -33,35 +104,30 @@ impl Element for Controller {
 
         self.status.render(dt)?;
 
-        if self.in_chat {
-            self.chat.render(dt)?;
-        } else {
-            self.selector.render(dt)?;
-        }
+        self.cur_view.render(dt)?;
 
         Ok(())
     }
 
     fn key_push(&mut self, k: KeyCode) {
-        match k {
-            KeyCode::Touchpad => {
-                if self.selector.selected() == 1 {
-                    self.in_chat = true;
+        match (&self.cur_view, k) {
+            (View::MainMenu(m), KeyCode::Touchpad) => {
+                if let Some(new_view) = View::from_main_menu_id(m.selected()) {
+                    self.cur_view = new_view;
                 }
             }
-            KeyCode::Back => {
-                self.in_chat = false;
-            }
 
-            _ if self.in_chat => self.chat.key_push(k),
+            (View::MainMenu(_), KeyCode::Back) => {}
+            (_, KeyCode::Back) => {
+                let id = self.cur_view.to_main_menu_id();
+                self.cur_view = View::main_menu_with_id(id);
+            }
 
-            _ => {}
+            (_, k) => self.cur_view.key_push(k),
         }
     }
 
     fn touchpad_scroll(&mut self, x: i8, y: i8) {
-        if !self.in_chat {
-            self.selector.touchpad_scroll(x, y);
-        }
+        self.cur_view.touchpad_scroll(x, y);
     }
 }
diff --git a/src/gui/selector.rs b/src/gui/selector.rs
index 3c2142bdb5bc6eaf93e3cfc7e56c9e12f77df10f..bf5cabf906e79e3a8e1b13db6d227efec4522c6a 100644
--- a/src/gui/selector.rs
+++ b/src/gui/selector.rs
@@ -1,3 +1,5 @@
+use core::marker::PhantomData;
+
 use embedded_graphics::{
     mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
     pixelcolor::Rgb565,
@@ -17,26 +19,39 @@ const BORDER_WIDTH: u32 = 4;
 const SCREEN_WIDTH: u32 = 320;
 const SCREEN_HEIGHT: u32 = 240;
 
-pub struct Selector<const N: usize> {
-    options: [&'static str; N],
+pub struct Selector<T: AsRef<str>, V: AsRef<[T]>> {
+    options: V,
     width: u32,
     height: u32,
     selected: usize,
     y_tracker: i8,
+    _phantom: PhantomData<T>,
 }
 
-impl<const N: usize> Selector<N> {
-    pub fn new(options: [&'static str; N]) -> Self {
-        let width = u32::try_from(options.iter().map(|x| x.len()).max().unwrap_or(0)).unwrap()
+impl<T: AsRef<str>, V: AsRef<[T]>> Selector<T, V> {
+    pub fn new(options: V, selected: usize) -> Self {
+        assert!(selected < options.as_ref().len());
+
+        let width = u32::try_from(
+            options
+                .as_ref()
+                .iter()
+                .map(|x| x.as_ref().len())
+                .max()
+                .unwrap_or(0),
+        )
+        .unwrap()
             * CHAR_WIDTH
             + BORDER_WIDTH * 2;
-        let height = u32::try_from(N).unwrap() * (CHAR_HEIGHT + BORDER_WIDTH) + BORDER_WIDTH;
+        let height = u32::try_from(options.as_ref().len()).unwrap() * (CHAR_HEIGHT + BORDER_WIDTH)
+            + BORDER_WIDTH;
         Self {
             options,
             width,
             height,
-            selected: 0,
+            selected,
             y_tracker: 0,
+            _phantom: PhantomData,
         }
     }
 
@@ -45,7 +60,7 @@ impl<const N: usize> Selector<N> {
     }
 }
 
-impl<const N: usize> Element for Selector<N> {
+impl<T: AsRef<str>, V: AsRef<[T]>> Element for Selector<T, V> {
     fn render<
         E,
         DT: embedded_graphics::prelude::DrawTarget<
@@ -77,7 +92,7 @@ impl<const N: usize> Element for Selector<N> {
             .text_color(Rgb565::BLACK)
             .build();
 
-        for (i, text) in self.options.iter().enumerate() {
+        for (i, text) in self.options.as_ref().iter().enumerate() {
             // Draw our background rectangles
             let rect_style = if i == self.selected {
                 selected_rect_style
@@ -101,7 +116,7 @@ impl<const N: usize> Element for Selector<N> {
 
             // Render the text
             Text::with_baseline(
-                text,
+                text.as_ref(),
                 Point::new(
                     (BORDER_WIDTH + start_x).try_into().unwrap(),
                     (start_y + i * (CHAR_HEIGHT + BORDER_WIDTH) + 1)
@@ -128,7 +143,7 @@ impl<const N: usize> Element for Selector<N> {
         } else if self.y_tracker > 50 {
             self.y_tracker = 0;
 
-            if self.selected < N - 1 {
+            if self.selected < self.options.as_ref().len() - 1 {
                 self.selected += 1;
             }
         }
diff --git a/src/main.rs b/src/main.rs
index 7154345bfa109302f46a352eaa00ec3747800e6e..9d56274d638167304ae692db669c4bc3459a27af 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,6 +2,7 @@
 #![no_main]
 
 mod buffered_display;
+mod contact;
 mod controller;
 mod gui;
 mod keyboard;