diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index afbf1f6b..583d8fc8 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -60,7 +60,11 @@ impl Application {
         let editor = &mut self.editor;
         let compositor = &mut self.compositor;
 
-        let mut cx = crate::compositor::Context { editor, executor };
+        let mut cx = crate::compositor::Context {
+            editor,
+            executor,
+            scroll: None,
+        };
 
         compositor.render(&mut cx);
     }
@@ -91,6 +95,7 @@ impl Application {
         let mut cx = crate::compositor::Context {
             editor: &mut self.editor,
             executor: &self.executor,
+            scroll: None,
         };
         // Handle key events
         let should_redraw = match event {
diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs
index 0a08a0d1..ba8453f3 100644
--- a/helix-term/src/compositor.rs
+++ b/helix-term/src/compositor.rs
@@ -29,6 +29,7 @@ use helix_view::{Editor, View};
 pub struct Context<'a> {
     pub editor: &'a mut Editor,
     pub executor: &'static smol::Executor<'static>,
+    pub scroll: Option<usize>,
 }
 
 pub trait Component {
diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs
index 976d00fa..3040e1ca 100644
--- a/helix-term/src/ui/markdown.rs
+++ b/helix-term/src/ui/markdown.rs
@@ -112,8 +112,9 @@ impl Component for Markdown {
 
         let contents = Text::from(lines);
 
-        let par = Paragraph::new(contents).wrap(Wrap { trim: false });
-        // .scroll(x, y) offsets
+        let par = Paragraph::new(contents)
+            .wrap(Wrap { trim: false })
+            .scroll((cx.scroll.unwrap_or_default() as u16, 0));
 
         let area = Rect::new(area.x + 1, area.y + 1, area.width - 2, area.height - 2);
         par.render(area, surface);
diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs
index 4ca70c50..0dbb3a24 100644
--- a/helix-term/src/ui/popup.rs
+++ b/helix-term/src/ui/popup.rs
@@ -19,6 +19,7 @@ pub struct Popup {
     contents: Box<dyn Component>,
     position: Option<Position>,
     size: (u16, u16),
+    scroll: usize,
 }
 
 impl Popup {
@@ -29,12 +30,21 @@ impl Popup {
             contents,
             position: None,
             size: (0, 0),
+            scroll: 0,
         }
     }
 
     pub fn set_position(&mut self, pos: Option<Position>) {
         self.position = pos;
     }
+
+    pub fn scroll(&mut self, offset: usize, direction: bool) {
+        if direction {
+            self.scroll += offset;
+        } else {
+            self.scroll = self.scroll.saturating_sub(offset);
+        }
+    }
 }
 
 impl Component for Popup {
@@ -64,6 +74,21 @@ impl Component for Popup {
                 code: KeyCode::Char('c'),
                 modifiers: KeyModifiers::CONTROL,
             } => close_fn,
+
+            KeyEvent {
+                code: KeyCode::Char('d'),
+                modifiers: KeyModifiers::CONTROL,
+            } => {
+                self.scroll(self.size.1 as usize / 2, true);
+                return EventResult::Consumed(None);
+            }
+            KeyEvent {
+                code: KeyCode::Char('u'),
+                modifiers: KeyModifiers::CONTROL,
+            } => {
+                self.scroll(self.size.1 as usize / 2, false);
+                return EventResult::Consumed(None);
+            }
             _ => self.contents.handle_event(event, cx),
         }
         // for some events, we want to process them but send ignore, specifically all input except
@@ -85,6 +110,8 @@ impl Component for Popup {
         use tui::text::Text;
         use tui::widgets::{Paragraph, Widget, Wrap};
 
+        cx.scroll = Some(self.scroll);
+
         let position = self
             .position
             .or_else(|| cx.editor.cursor_position())