diff --git a/app/Http/Controllers/TableEditController.php b/app/Http/Controllers/TableEditController.php
index 0cb8d37..f546b47 100644
--- a/app/Http/Controllers/TableEditController.php
+++ b/app/Http/Controllers/TableEditController.php
@@ -79,7 +79,19 @@ class TableEditController extends Controller
         ]);
     }
 
-    // TODO other tab handlers
+    /** @noinspection PhpUnusedPrivateMethodInspection */
+    private function addRows(Changeset $changeset)
+    {
+        $columns = $changeset->fetchAndTransformColumns();
+        $rows = $changeset->getAddedRows(25);
+
+        return view('table.propose.add-rows', [
+            'changeset' => $changeset,
+            'table' => $changeset->table,
+            'columns' => collect($columns),
+            'rows' => $rows,
+        ]);
+    }
 
     public function draftUpdate(User $user, string $table, Request $request)
     {
diff --git a/app/Tables/Changeset.php b/app/Tables/Changeset.php
index e049af1..2b33ad9 100644
--- a/app/Tables/Changeset.php
+++ b/app/Tables/Changeset.php
@@ -6,6 +6,7 @@ namespace App\Tables;
 use App\Models\Revision;
 use App\Models\Row;
 use App\Models\Table;
+use Illuminate\Pagination\Paginator;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Support\Collection;
 use MightyPork\Exceptions\NotExistException;
@@ -186,7 +187,7 @@ class Changeset
             $row->_orig = [];
         }
 
-        // Removed rows
+        // Removed rows - return as null
         if (in_array($row->_id, $this->removedRows)) {
             if ($decorate) {
                 $row->_remove = true;
@@ -195,16 +196,19 @@ class Changeset
             }
         }
 
-        // Changed values
-        if (isset($this->rowUpdates[$row->_id])) {
-            $newVals = $this->rowUpdates[$row->_id];
+        // if marked for removal, hide changes
+        if (!$row->_remove) {
+            // Changed values
+            if (isset($this->rowUpdates[$row->_id])) {
+                $newVals = $this->rowUpdates[$row->_id];
 
-            if ($decorate) {
-                $row->_changed = array_keys($newVals);
-                $row->_orig = array_only((array)$row, $row->_changed);
-            }
+                if ($decorate) {
+                    $row->_changed = array_keys($newVals);
+                    $row->_orig = array_only((array)$row, $row->_changed);
+                }
 
-            $row = (object)array_merge((array)$row, $newVals);
+                $row = (object)array_merge((array)$row, $newVals);
+            }
         }
 
         // Drop deleted columns
@@ -300,8 +304,16 @@ class Changeset
         return (object)$r->getAttributes();
     }
 
+    /**
+     * Apply a row update, adding the row to the list of changes, or removing it
+     * if all differences were undone.
+     *
+     * @param array|object $newVals
+     */
     public function rowUpdate($newVals)
     {
+        $newVals = (object)$newVals;
+
         $_id = $newVals->_id;
         $origRow = $this->fetchRow($_id);
 
@@ -309,8 +321,6 @@ class Changeset
         $cols = collect($this->fetchAndTransformColumns())->keyBy('id');
 
         $updateObj = [];
-        \Debugbar::addMessage(json_encode($cols));
-        \Debugbar::addMessage(var_export($newVals, true));
 
         foreach ($newVals as $colId => $value) {
             if (starts_with($colId, '_')) continue; // internals
@@ -320,10 +330,6 @@ class Changeset
             }
         }
 
-        \Debugbar::addMessage("New: ".json_encode($newVals));
-        \Debugbar::addMessage("Orig: ".json_encode($origRow));
-        \Debugbar::addMessage("UpdateObj: ".json_encode($updateObj));
-
         if (!empty($updateObj)) {
             $this->rowUpdates[$_id] = $updateObj;
         } else {
@@ -331,4 +337,15 @@ class Changeset
             unset($this->rowUpdates[$_id]);
         }
     }
+
+    /**
+     * Get a page of added rows for display in the editor
+     *
+     * @param int $perPage
+     * @return \Illuminate\Pagination\LengthAwarePaginator|Collection|array
+     */
+    public function getAddedRows(int $perPage = 25)
+    {
+        return collection_paginate($this->newRows, $perPage);
+    }
 }
diff --git a/app/helpers.php b/app/helpers.php
index b0159ee..b07fe11 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -95,3 +95,28 @@ function toJSON($object) {
 function fromJSON($object, $assoc=false) {
     return \GuzzleHttp\json_decode($object, $assoc);
 }
+
+/**
+ * @param \Illuminate\Support\Collection|array $items
+ * @param int $per_page
+ * @param null|\Closure $mapFn
+ * @return \Illuminate\Pagination\LengthAwarePaginator
+ */
+function collection_paginate($items, $per_page, $mapFn = null)
+{
+    if (!$items instanceof \Illuminate\Support\Collection) {
+        $items = collect($items);
+    }
+
+    $page = Request::get('page', 1);
+
+    $pageItems = $items->forPage($page, $per_page)->values();
+
+    return new Illuminate\Pagination\LengthAwarePaginator(
+        $mapFn ? $pageItems->map($mapFn) : $pageItems,
+        $items->count(),
+        $per_page,
+        Illuminate\Pagination\Paginator::resolveCurrentPage(),
+        ['path' => Illuminate\Pagination\Paginator::resolveCurrentPath()]
+    );
+}
diff --git a/resources/assets/js/base-setup.js b/resources/assets/js/base-setup.js
index 3bd82f6..4f30037 100644
--- a/resources/assets/js/base-setup.js
+++ b/resources/assets/js/base-setup.js
@@ -1,4 +1,4 @@
-// window._ = require('lodash');
+//window._ = require('lodash');
 window.Popper = require('popper.js').default
 
 /**
diff --git a/resources/assets/js/components/Icon.vue b/resources/assets/js/components/Icon.vue
index d69f391..f4950b2 100644
--- a/resources/assets/js/components/Icon.vue
+++ b/resources/assets/js/components/Icon.vue
@@ -1,12 +1,11 @@
 
-  
+  
     
   
 
 
 
diff --git a/resources/assets/sass/_helpers.scss b/resources/assets/sass/_helpers.scss
index c18f61c..c9acd97 100644
--- a/resources/assets/sass/_helpers.scss
+++ b/resources/assets/sass/_helpers.scss
@@ -25,3 +25,7 @@
 .opacity-fade {
 	transition: opacity .3s ease-in-out;
 }
+
+.pointer {
+	cursor: pointer;
+}