diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4cdaceae..575cb397 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [0.6.7]/[0.7.0] - Unreleased
 
+## Feature
+
+- [#646](https://github.com/ClementTsang/bottom/pull/646): Add `PgUp`/`PgDown` keybind support to scroll up and down a page in a table.
+
 ## [0.6.6] - 2021-12-22
 
 ## Bug Fixes
diff --git a/docs/content/usage/general-usage.md b/docs/content/usage/general-usage.md
index 4bfcdb61..ea1ed104 100644
--- a/docs/content/usage/general-usage.md
+++ b/docs/content/usage/general-usage.md
@@ -55,6 +55,7 @@ Note that key bindings are generally case-sensitive.
 | ++right++ <br/> ++l++ <br/> ++alt+l++                        | Move right within a widget                                   |
 | ++g+g++ , ++home++                                           | Jump to the first entry                                      |
 | ++G++ , ++end++                                              | Jump to the last entry                                       |
+| ++page-up++ , ++page-down++                                  | Scroll up/down a table by a page                             |
 
 ## Mouse bindings
 
diff --git a/src/app.rs b/src/app.rs
index b6bca61d..22a467ee 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1114,6 +1114,20 @@ impl App {
                 0 => KillSignal::Cancel,
                 sig => KillSignal::Kill(sig),
             };
+        } else if self.current_widget.widget_type.is_widget_table() {
+            if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = (
+                &self.current_widget.top_left_corner,
+                &self.current_widget.bottom_right_corner,
+            ) {
+                let border_offset = if self.is_drawing_border() { 1 } else { 0 };
+                let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) {
+                    self.app_config_fields.table_gap
+                } else {
+                    0
+                };
+                let height = brc_y - tlc_y - 2 * border_offset - header_gap_offset;
+                self.change_position_count(-(height as i64));
+            }
         }
     }
 
@@ -1127,6 +1141,20 @@ impl App {
                 new_signal += 2;
             }
             self.delete_dialog_state.selected_signal = KillSignal::Kill(new_signal);
+        } else if self.current_widget.widget_type.is_widget_table() {
+            if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = (
+                &self.current_widget.top_left_corner,
+                &self.current_widget.bottom_right_corner,
+            ) {
+                let border_offset = if self.is_drawing_border() { 1 } else { 0 };
+                let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) {
+                    self.app_config_fields.table_gap
+                } else {
+                    0
+                };
+                let height = brc_y - tlc_y - 2 * border_offset - header_gap_offset;
+                self.change_position_count(height as i64);
+            }
         }
     }
 
@@ -1443,8 +1471,6 @@ impl App {
                 '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
                     self.on_number(caught_char)
                 }
-                'u' => self.on_page_up(),
-                'd' => self.on_page_down(),
                 'g' => {
                     let mut is_first_g = true;
                     if let Some(second_char) = self.second_char {
@@ -2330,36 +2356,29 @@ impl App {
     }
 
     pub fn decrement_position_count(&mut self) {
-        if !self.ignore_normal_keybinds() {
-            match self.current_widget.widget_type {
-                BottomWidgetType::Proc => {
-                    self.increment_process_position(-1);
-                }
-                BottomWidgetType::ProcSort => self.increment_process_sort_position(-1),
-                BottomWidgetType::Temp => self.increment_temp_position(-1),
-                BottomWidgetType::Disk => self.increment_disk_position(-1),
-                BottomWidgetType::CpuLegend => self.increment_cpu_legend_position(-1),
-                _ => {}
-            }
-        }
+        self.change_position_count(-1);
     }
 
     pub fn increment_position_count(&mut self) {
+        self.change_position_count(1);
+    }
+
+    fn change_position_count(&mut self, amount: i64) {
         if !self.ignore_normal_keybinds() {
             match self.current_widget.widget_type {
                 BottomWidgetType::Proc => {
-                    self.increment_process_position(1);
+                    self.change_process_position(amount);
                 }
-                BottomWidgetType::ProcSort => self.increment_process_sort_position(1),
-                BottomWidgetType::Temp => self.increment_temp_position(1),
-                BottomWidgetType::Disk => self.increment_disk_position(1),
-                BottomWidgetType::CpuLegend => self.increment_cpu_legend_position(1),
+                BottomWidgetType::ProcSort => self.change_process_sort_position(amount),
+                BottomWidgetType::Temp => self.change_temp_position(amount),
+                BottomWidgetType::Disk => self.increment_disk_position(amount),
+                BottomWidgetType::CpuLegend => self.change_cpu_legend_position(amount),
                 _ => {}
             }
         }
     }
 
-    fn increment_process_sort_position(&mut self, num_to_change_by: i64) {
+    fn change_process_sort_position(&mut self, num_to_change_by: i64) {
         if let Some(proc_widget_state) = self
             .proc_state
             .get_mut_widget_state(self.current_widget.widget_id - 2)
@@ -2367,9 +2386,11 @@ impl App {
             let current_posn = proc_widget_state.columns.current_scroll_position;
             let num_columns = proc_widget_state.columns.get_enabled_columns_len();
 
-            if current_posn as i64 + num_to_change_by >= 0
-                && current_posn as i64 + num_to_change_by < num_columns as i64
-            {
+            if current_posn as i64 + num_to_change_by < 0 {
+                proc_widget_state.columns.current_scroll_position = 0;
+            } else if current_posn as i64 + num_to_change_by >= num_columns as i64 {
+                proc_widget_state.columns.current_scroll_position = num_columns.saturating_sub(1);
+            } else {
                 proc_widget_state.columns.current_scroll_position =
                     (current_posn as i64 + num_to_change_by) as usize;
             }
@@ -2382,7 +2403,7 @@ impl App {
         }
     }
 
-    fn increment_cpu_legend_position(&mut self, num_to_change_by: i64) {
+    fn change_cpu_legend_position(&mut self, num_to_change_by: i64) {
         if let Some(cpu_widget_state) = self
             .cpu_state
             .widget_states
@@ -2391,9 +2412,11 @@ impl App {
             let current_posn = cpu_widget_state.scroll_state.current_scroll_position;
 
             let cap = self.canvas_data.cpu_data.len();
-            if current_posn as i64 + num_to_change_by >= 0
-                && current_posn as i64 + num_to_change_by < cap as i64
-            {
+            if current_posn as i64 + num_to_change_by < 0 {
+                cpu_widget_state.scroll_state.current_scroll_position = 0;
+            } else if current_posn as i64 + num_to_change_by >= cap as i64 {
+                cpu_widget_state.scroll_state.current_scroll_position = cap.saturating_sub(1);
+            } else {
                 cpu_widget_state.scroll_state.current_scroll_position =
                     (current_posn as i64 + num_to_change_by) as usize;
             }
@@ -2407,7 +2430,7 @@ impl App {
     }
 
     /// Returns the new position.
-    fn increment_process_position(&mut self, num_to_change_by: i64) -> Option<usize> {
+    fn change_process_position(&mut self, num_to_change_by: i64) -> Option<usize> {
         if let Some(proc_widget_state) = self
             .proc_state
             .get_mut_widget_state(self.current_widget.widget_id)
@@ -2418,13 +2441,16 @@ impl App {
                 .finalized_process_data_map
                 .get(&self.current_widget.widget_id)
             {
-                if current_posn as i64 + num_to_change_by >= 0
-                    && current_posn as i64 + num_to_change_by < finalized_process_data.len() as i64
+                if current_posn as i64 + num_to_change_by < 0 {
+                    proc_widget_state.scroll_state.current_scroll_position = 0;
+                } else if current_posn as i64 + num_to_change_by
+                    >= finalized_process_data.len() as i64
                 {
                     proc_widget_state.scroll_state.current_scroll_position =
-                        (current_posn as i64 + num_to_change_by) as usize;
+                        finalized_process_data.len().saturating_sub(1);
                 } else {
-                    return None;
+                    proc_widget_state.scroll_state.current_scroll_position =
+                        (current_posn as i64 + num_to_change_by) as usize;
                 }
             }
 
@@ -2440,7 +2466,7 @@ impl App {
         None
     }
 
-    fn increment_temp_position(&mut self, num_to_change_by: i64) {
+    fn change_temp_position(&mut self, num_to_change_by: i64) {
         if let Some(temp_widget_state) = self
             .temp_state
             .widget_states
@@ -2448,10 +2474,14 @@ impl App {
         {
             let current_posn = temp_widget_state.scroll_state.current_scroll_position;
 
-            if current_posn as i64 + num_to_change_by >= 0
-                && current_posn as i64 + num_to_change_by
-                    < self.canvas_data.temp_sensor_data.len() as i64
+            if current_posn as i64 + num_to_change_by < 0 {
+                temp_widget_state.scroll_state.current_scroll_position = 0;
+            } else if current_posn as i64 + num_to_change_by
+                >= self.canvas_data.temp_sensor_data.len() as i64
             {
+                temp_widget_state.scroll_state.current_scroll_position =
+                    self.canvas_data.temp_sensor_data.len().saturating_sub(1);
+            } else {
                 temp_widget_state.scroll_state.current_scroll_position =
                     (current_posn as i64 + num_to_change_by) as usize;
             }
@@ -2901,9 +2931,6 @@ impl App {
         }
 
         let mut failed_to_get = true;
-        // TODO: [MOUSE] We could use a better data structure for this?  Currently it's a blind
-        // traversal through a hashmap, using a 2d binary tree of sorts would be better.
-        // See: https://docs.rs/kdtree/0.6.0/kdtree/
         for (new_widget_id, widget) in &self.widget_map {
             if let (Some((tlc_x, tlc_y)), Some((brc_x, brc_y))) =
                 (widget.top_left_corner, widget.bottom_right_corner)
@@ -2985,7 +3012,7 @@ impl App {
                                                 .current_scroll_position;
                                             let is_tree_mode = proc_widget_state.is_tree_mode;
 
-                                            let new_position = self.increment_process_position(
+                                            let new_position = self.change_process_position(
                                                 offset_clicked_entry as i64 - visual_index as i64,
                                             );
 
@@ -3008,7 +3035,7 @@ impl App {
                                         if let Some(visual_index) =
                                             proc_widget_state.columns.column_state.selected()
                                         {
-                                            self.increment_process_sort_position(
+                                            self.change_process_sort_position(
                                                 offset_clicked_entry as i64 - visual_index as i64,
                                             );
                                         }
@@ -3022,7 +3049,7 @@ impl App {
                                         if let Some(visual_index) =
                                             cpu_widget_state.scroll_state.table_state.selected()
                                         {
-                                            self.increment_cpu_legend_position(
+                                            self.change_cpu_legend_position(
                                                 offset_clicked_entry as i64 - visual_index as i64,
                                             );
                                         }
@@ -3036,7 +3063,7 @@ impl App {
                                         if let Some(visual_index) =
                                             temp_widget_state.scroll_state.table_state.selected()
                                         {
-                                            self.increment_temp_position(
+                                            self.change_temp_position(
                                                 offset_clicked_entry as i64 - visual_index as i64,
                                             );
                                         }
diff --git a/src/canvas.rs b/src/canvas.rs
index c8f62640..f19481e0 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -695,6 +695,13 @@ impl Painter {
             }
         })?;
 
+        if let Some(updated_current_widget) = app_state
+            .widget_map
+            .get(&app_state.current_widget.widget_id)
+        {
+            app_state.current_widget = updated_current_widget.clone();
+        }
+
         app_state.is_force_redraw = false;
         app_state.is_determining_widget_boundary = false;
 
diff --git a/src/constants.rs b/src/constants.rs
index 4464c16f..48e115f7 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -231,7 +231,7 @@ pub const HELP_CONTENTS_TEXT: [&str; 8] = [
 
 // TODO [Help]: Search in help?
 // TODO [Help]: Move to using tables for easier formatting?
-pub const GENERAL_HELP_TEXT: [&str; 30] = [
+pub const GENERAL_HELP_TEXT: [&str; 31] = [
     "1 - General",
     "q, Ctrl-c        Quit",
     "Esc              Close dialog windows, search, widgets, or exit expanded mode",
@@ -260,6 +260,7 @@ pub const GENERAL_HELP_TEXT: [&str; 30] = [
     "+                Zoom in on chart (decrease time range)",
     "-                Zoom out on chart (increase time range)",
     "=                Reset zoom",
+    "PgUp, PgDown     Scroll up/down a table by a page",
     "Mouse scroll     Scroll through the tables or zoom in/out of charts by scrolling up/down",
     "Mouse click      Selects the clicked widget, table entry, dialog option, or tab",
 ];
diff --git a/src/lib.rs b/src/lib.rs
index 71f6c078..4de735d6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -125,6 +125,8 @@ pub fn handle_key_event_or_break(
             KeyCode::F(5) => app.toggle_tree_mode(),
             KeyCode::F(6) => app.toggle_sort(),
             KeyCode::F(9) => app.start_killing_process(),
+            KeyCode::PageDown => app.on_page_down(),
+            KeyCode::PageUp => app.on_page_up(),
             _ => {}
         }
     } else {