diff --git a/changelog.md b/changelog.md
index b6fa17ca4..6404ab537 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+* Added row modal editing feature to the grid view.
+* Made it possible to resize the field width per view.
 * Added validation and formatting for the number field.
 * Cancel the editing state of a fields when the escape key is pressed.
 * The next field is now selected when the tab character is pressed when a field is
diff --git a/web-frontend/modules/core/assets/scss/components/_form.scss b/web-frontend/modules/core/assets/scss/components/_form.scss
index ab67cefcc..c6a07d4c6 100644
--- a/web-frontend/modules/core/assets/scss/components/_form.scss
+++ b/web-frontend/modules/core/assets/scss/components/_form.scss
@@ -15,6 +15,10 @@
   }
 }
 
+.control-label-icon {
+  margin-right: 6px;
+}
+
 .control-context {
   color: $color-primary-900;
   margin-left: 6px;
diff --git a/web-frontend/modules/database/components/field/FieldContext.vue b/web-frontend/modules/database/components/field/FieldContext.vue
new file mode 100644
index 000000000..156bbc7e9
--- /dev/null
+++ b/web-frontend/modules/database/components/field/FieldContext.vue
@@ -0,0 +1,69 @@
+<template>
+  <Context ref="context">
+    <ul class="context-menu">
+      <li>
+        <a
+          ref="updateFieldContextLink"
+          class="grid-view-description-options"
+          @click="
+            $refs.updateFieldContext.toggle(
+              $refs.updateFieldContextLink,
+              'bottom',
+              'left',
+              0
+            )
+          "
+        >
+          <i class="context-menu-icon fas fa-fw fa-pen"></i>
+          Edit field
+        </a>
+        <UpdateFieldContext
+          ref="updateFieldContext"
+          :field="field"
+          @update="$refs.context.hide()"
+        ></UpdateFieldContext>
+      </li>
+      <li v-if="!field.primary">
+        <a @click="deleteField(field)">
+          <i class="context-menu-icon fas fa-fw fa-trash"></i>
+          Delete field
+        </a>
+      </li>
+    </ul>
+  </Context>
+</template>
+
+<script>
+import { notifyIf } from '@baserow/modules/core/utils/error'
+import context from '@baserow/modules/core/mixins/context'
+import UpdateFieldContext from '@baserow/modules/database/components/field/UpdateFieldContext'
+
+export default {
+  name: 'FieldContext',
+  components: { UpdateFieldContext },
+  mixins: [context],
+  props: {
+    field: {
+      type: Object,
+      required: true,
+    },
+  },
+  methods: {
+    setLoading(field, value) {
+      this.$store.dispatch('field/setItemLoading', { field, value })
+    },
+    async deleteField(field) {
+      this.$refs.context.hide()
+      this.setLoading(field, true)
+
+      try {
+        await this.$store.dispatch('field/delete', field)
+      } catch (error) {
+        notifyIf(error, 'field')
+      }
+
+      this.setLoading(field, false)
+    },
+  },
+}
+</script>
diff --git a/web-frontend/modules/database/components/row/RowEditFieldBoolean.vue b/web-frontend/modules/database/components/row/RowEditFieldBoolean.vue
new file mode 100644
index 000000000..4363efbb2
--- /dev/null
+++ b/web-frontend/modules/database/components/row/RowEditFieldBoolean.vue
@@ -0,0 +1,26 @@
+<template>
+  <div class="control-elements">
+    <div
+      class="field-boolean-checkbox"
+      :class="{ active: value }"
+      @click="toggle(value)"
+    >
+      <i class="fas fa-check check"></i>
+    </div>
+  </div>
+</template>
+
+<script>
+import rowEditField from '@baserow/modules/database/mixins/rowEditField'
+
+export default {
+  mixins: [rowEditField],
+  methods: {
+    toggle(value) {
+      const oldValue = !!value
+      const newValue = !value
+      this.$emit('update', newValue, oldValue)
+    },
+  },
+}
+</script>
diff --git a/web-frontend/modules/database/components/row/RowEditFieldNumber.vue b/web-frontend/modules/database/components/row/RowEditFieldNumber.vue
new file mode 100644
index 000000000..909fc2225
--- /dev/null
+++ b/web-frontend/modules/database/components/row/RowEditFieldNumber.vue
@@ -0,0 +1,27 @@
+<template>
+  <div class="control-elements">
+    <input
+      ref="input"
+      v-model="copy"
+      type="text"
+      class="input input-large field-number"
+      :class="{ 'input-error': !isValid() }"
+      @keyup.enter="$refs.input.blur()"
+      @focus="select()"
+      @blur="unselect()"
+    />
+    <div v-show="!isValid()" class="error">
+      {{ getError() }}
+    </div>
+  </div>
+</template>
+
+<script>
+import rowEditField from '@baserow/modules/database/mixins/rowEditField'
+import rowEditFieldInput from '@baserow/modules/database/mixins/rowEditFieldInput'
+import numberField from '@baserow/modules/database/mixins/numberField'
+
+export default {
+  mixins: [rowEditField, rowEditFieldInput, numberField],
+}
+</script>
diff --git a/web-frontend/modules/database/components/row/RowEditFieldText.vue b/web-frontend/modules/database/components/row/RowEditFieldText.vue
new file mode 100644
index 000000000..41e6516ae
--- /dev/null
+++ b/web-frontend/modules/database/components/row/RowEditFieldText.vue
@@ -0,0 +1,22 @@
+<template>
+  <div class="control-elements">
+    <input
+      ref="input"
+      v-model="copy"
+      type="text"
+      class="input input-large"
+      @keyup.enter="$refs.input.blur()"
+      @focus="select()"
+      @blur="unselect()"
+    />
+  </div>
+</template>
+
+<script>
+import rowEditField from '@baserow/modules/database/mixins/rowEditField'
+import rowEditFieldInput from '@baserow/modules/database/mixins/rowEditFieldInput'
+
+export default {
+  mixins: [rowEditField, rowEditFieldInput],
+}
+</script>
diff --git a/web-frontend/modules/database/components/row/RowEditModal.vue b/web-frontend/modules/database/components/row/RowEditModal.vue
new file mode 100644
index 000000000..39f8952ba
--- /dev/null
+++ b/web-frontend/modules/database/components/row/RowEditModal.vue
@@ -0,0 +1,92 @@
+<template>
+  <Modal>
+    <h2 v-if="primary !== undefined" class="box-title">
+      {{ getHeading(primary, row) }}
+    </h2>
+    <form>
+      <RowEditModalField
+        v-for="field in getFields(fields, primary)"
+        :ref="'field-' + field.id"
+        :key="'row-edit-field-' + field.id"
+        :field="field"
+        :row="row"
+        @update="update"
+      ></RowEditModalField>
+      <div class="actions">
+        <a
+          ref="createFieldContextLink"
+          @click="$refs.createFieldContext.toggle($refs.createFieldContextLink)"
+        >
+          <i class="fas fa-plus"></i>
+          add field
+        </a>
+        <CreateFieldContext
+          ref="createFieldContext"
+          :table="table"
+        ></CreateFieldContext>
+      </div>
+    </form>
+  </Modal>
+</template>
+
+<script>
+import modal from '@baserow/modules/core/mixins/modal'
+import RowEditModalField from '@baserow/modules/database/components/row/RowEditModalField'
+import CreateFieldContext from '@baserow/modules/database/components/field/CreateFieldContext'
+
+export default {
+  name: 'RowEditModal',
+  components: {
+    RowEditModalField,
+    CreateFieldContext,
+  },
+  mixins: [modal],
+  props: {
+    table: {
+      type: Object,
+      required: true,
+    },
+    primary: {
+      type: Object,
+      required: false,
+      default: undefined,
+    },
+    fields: {
+      type: Array,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      row: {},
+    }
+  },
+  methods: {
+    show(row, ...args) {
+      this.row = row
+      this.getRootModal().show(...args)
+    },
+    /**
+     * Because the modal can't update values by himself, an event will be called to
+     * notify the parent component to actually update the value.
+     */
+    update(context) {
+      context.table = this.table
+      this.$emit('update', context)
+    },
+    getFields(fields, primary) {
+      return primary !== undefined ? [primary].concat(fields) : fields
+    },
+    getHeading(primary, row) {
+      const name = `field_${primary.id}`
+      if (Object.prototype.hasOwnProperty.call(row, name)) {
+        return this.$registry
+          .get('field', primary.type)
+          .toHumanReadableString(primary, row[name])
+      } else {
+        return null
+      }
+    },
+  },
+}
+</script>
diff --git a/web-frontend/modules/database/components/row/RowEditModalField.vue b/web-frontend/modules/database/components/row/RowEditModalField.vue
new file mode 100644
index 000000000..a234ac984
--- /dev/null
+++ b/web-frontend/modules/database/components/row/RowEditModalField.vue
@@ -0,0 +1,58 @@
+<template>
+  <div class="control">
+    <label class="control-label">
+      <i
+        class="fas control-label-icon"
+        :class="'fa-' + field._.type.iconClass"
+      ></i>
+      {{ field.name }}
+      <a
+        ref="contextLink"
+        class="control-context"
+        @click="$refs.context.toggle($refs.contextLink, 'bottom', 'left', 0)"
+      >
+        <i class="fas fa-caret-down"></i>
+      </a>
+    </label>
+    <FieldContext ref="context" :field="field"></FieldContext>
+    <component
+      :is="getFieldComponent(field.type)"
+      ref="field"
+      :field="field"
+      :value="row['field_' + field.id]"
+      @update="update"
+    />
+  </div>
+</template>
+
+<script>
+import FieldContext from '@baserow/modules/database/components/field/FieldContext'
+
+export default {
+  name: 'RowEditModalField',
+  components: { FieldContext },
+  props: {
+    field: {
+      type: Object,
+      required: true,
+    },
+    row: {
+      type: Object,
+      required: true,
+    },
+  },
+  methods: {
+    getFieldComponent(type) {
+      return this.$registry.get('field', type).getRowEditFieldComponent()
+    },
+    update(value, oldValue) {
+      this.$emit('update', {
+        row: this.row,
+        field: this.field,
+        value,
+        oldValue,
+      })
+    },
+  },
+}
+</script>
diff --git a/web-frontend/modules/database/components/view/grid/GridView.vue b/web-frontend/modules/database/components/view/grid/GridView.vue
index 7c7a612bb..3f5a33afa 100644
--- a/web-frontend/modules/database/components/view/grid/GridView.vue
+++ b/web-frontend/modules/database/components/view/grid/GridView.vue
@@ -45,7 +45,12 @@
                 v-for="row in rows"
                 :key="'left-row-' + view.id + '-' + row.id"
                 class="grid-view-row"
-                :class="{ 'grid-view-row-loading': row._.loading }"
+                :class="{
+                  'grid-view-row-loading': row._.loading,
+                  'grid-view-row-hover': row._.hover,
+                }"
+                @mouseover="setRowHover(row, true)"
+                @mouseleave="setRowHover(row, false)"
                 @contextmenu.prevent="showRowContext($event, row)"
               >
                 <div
@@ -54,7 +59,10 @@
                 >
                   <div class="grid-view-row-info">
                     <div class="grid-view-row-count">{{ row.id }}</div>
-                    <a href="#" class="grid-view-row-more">
+                    <a
+                      class="grid-view-row-more"
+                      @click="$refs.rowEditModal.show(row)"
+                    >
                       <i class="fas fa-expand"></i>
                     </a>
                   </div>
@@ -68,6 +76,7 @@
                   :style="{ width: widths.fields[primary.id] + 'px' }"
                   @selected="selectedField(primary, $event.component)"
                   @selectNext="selectNextField(row, primary, fields, primary)"
+                  @update="updateValue"
                 ></GridViewField>
               </div>
             </div>
@@ -175,7 +184,12 @@
                 v-for="row in rows"
                 :key="'right-row-' + view.id + '-' + row.id"
                 class="grid-view-row"
-                :class="{ 'grid-view-row-loading': row._.loading }"
+                :class="{
+                  'grid-view-row-loading': row._.loading,
+                  'grid-view-row-hover': row._.hover,
+                }"
+                @mouseover="setRowHover(row, true)"
+                @mouseleave="setRowHover(row, false)"
                 @contextmenu.prevent="showRowContext($event, row)"
               >
                 <GridViewField
@@ -193,6 +207,7 @@
                     selectNextField(row, field, fields, primary, true)
                   "
                   @selectNext="selectNextField(row, field, fields, primary)"
+                  @update="updateValue"
                 ></GridViewField>
               </div>
             </div>
@@ -225,6 +240,13 @@
         </li>
       </ul>
     </Context>
+    <RowEditModal
+      ref="rowEditModal"
+      :table="table"
+      :primary="primary"
+      :fields="fields"
+      @update="updateValue"
+    ></RowEditModal>
   </div>
 </template>
 
@@ -235,6 +257,7 @@ import CreateFieldContext from '@baserow/modules/database/components/field/Creat
 import GridViewFieldType from '@baserow/modules/database/components/view/grid/GridViewFieldType'
 import GridViewField from '@baserow/modules/database/components/view/grid/GridViewField'
 import GridViewFieldWidthHandle from '@baserow/modules/database/components/view/grid/GridViewFieldWidthHandle'
+import RowEditModal from '@baserow/modules/database/components/row/RowEditModal'
 import { notifyIf } from '@baserow/modules/core/utils/error'
 import _ from 'lodash'
 
@@ -245,6 +268,7 @@ export default {
     GridViewFieldType,
     GridViewField,
     GridViewFieldWidthHandle,
+    RowEditModal,
   },
   props: {
     primary: {
@@ -273,6 +297,7 @@ export default {
       addHover: false,
       loading: true,
       selectedRow: null,
+      lastHoveredRow: null,
       widths: {
         fields: {},
       },
@@ -308,6 +333,19 @@ export default {
     this.calculateWidths(this.primary, this.fields, this.fieldOptions)
   },
   methods: {
+    async updateValue({ field, row, value, oldValue }) {
+      try {
+        await this.$store.dispatch('view/grid/updateValue', {
+          table: this.table,
+          row,
+          field,
+          value,
+          oldValue,
+        })
+      } catch (error) {
+        notifyIf(error, 'field')
+      }
+    },
     scroll(pixelY, pixelX) {
       const $rightBody = this.$refs.rightBody
       const $right = this.$refs.right
@@ -412,7 +450,8 @@ export default {
       try {
         await this.$store.dispatch('view/grid/create', {
           table: this.table,
-          fields: this.fields,
+          // We need a list of all fields including the primary one here.
+          fields: [this.primary].concat(...this.fields),
           values: {},
         })
       } catch (error) {
@@ -523,6 +562,21 @@ export default {
       current[0].unselect()
       next[0].select()
     },
+    setRowHover(row, value) {
+      // Sometimes the mouseleave is not triggered, but because you can hover only one
+      // row at a time we can remember which was hovered last and set the hover state to
+      // false if it differs.
+      if (this.lastHoveredRow !== null && this.lastHoveredRow.id !== row.id) {
+        this.$store.dispatch('view/grid/setRowHover', {
+          row: this.lastHoveredRow,
+          value: false,
+        })
+        this.lastHoveredRow = true
+      }
+
+      this.$store.dispatch('view/grid/setRowHover', { row, value })
+      this.lastHoveredRow = row
+    },
   },
 }
 </script>
diff --git a/web-frontend/modules/database/components/view/grid/GridViewField.vue b/web-frontend/modules/database/components/view/grid/GridViewField.vue
index 46a042441..6466214db 100644
--- a/web-frontend/modules/database/components/view/grid/GridViewField.vue
+++ b/web-frontend/modules/database/components/view/grid/GridViewField.vue
@@ -2,7 +2,7 @@
   <div class="grid-view-column" @click="select()">
     <component
       :is="getFieldComponent(field.type)"
-      ref="column"
+      ref="field"
       :field="field"
       :value="row['field_' + field.id]"
       :selected="selected"
@@ -13,15 +13,10 @@
 
 <script>
 import { isElement } from '@baserow/modules/core/utils/dom'
-import { notifyIf } from '@baserow/modules/core/utils/error'
 
 export default {
   name: 'GridViewField',
   props: {
-    table: {
-      type: Object,
-      required: true,
-    },
     field: {
       type: Object,
       required: true,
@@ -62,25 +57,12 @@ export default {
      * which will actually update the value via the store.
      */
     update(value, oldValue) {
-      this.$store
-        .dispatch('view/grid/updateValue', {
-          table: this.table,
-          row: this.row,
-          field: this.field,
-          value,
-          oldValue,
-        })
-        .catch((error) => {
-          notifyIf(error, 'column')
-        })
-        .then(() => {
-          this.$forceUpdate()
-        })
-
-      // This is needed because in some cases we do have a value yet, so a watcher of
-      // the value is not guaranteed. This will make sure the component shows the
-      // latest value.
-      this.$forceUpdate()
+      this.$emit('update', {
+        row: this.row,
+        field: this.field,
+        value,
+        oldValue,
+      })
     },
     /**
      * Method that is called when a user clicks on the grid field. It wil
@@ -97,7 +79,7 @@ export default {
           this.clickTimestamp !== null &&
           timestamp - this.clickTimestamp < 200
         ) {
-          this.$refs.column.doubleClick()
+          this.$refs.field.doubleClick()
         }
       } else {
         // If the field is not yet selected we can change the state to selected.
@@ -105,7 +87,7 @@ export default {
         this.$nextTick(() => {
           // Call the select method on the next tick because we want to wait for all
           // changes to have rendered.
-          this.$refs.column.select()
+          this.$refs.field.select()
         })
 
         // Register a body click event listener so that we can detect if a user has
@@ -146,7 +128,7 @@ export default {
       this.clickTimestamp = timestamp
     },
     unselect() {
-      this.$refs.column.beforeUnSelect()
+      this.$refs.field.beforeUnSelect()
       this.$nextTick(() => {
         this.selected = false
       })
diff --git a/web-frontend/modules/database/components/view/grid/GridViewFieldBoolean.vue b/web-frontend/modules/database/components/view/grid/GridViewFieldBoolean.vue
index ebf0807de..acb9f5886 100644
--- a/web-frontend/modules/database/components/view/grid/GridViewFieldBoolean.vue
+++ b/web-frontend/modules/database/components/view/grid/GridViewFieldBoolean.vue
@@ -18,6 +18,19 @@ import gridField from '@baserow/modules/database/mixins/gridField'
 export default {
   mixins: [gridField],
   methods: {
+    select() {
+      // While the field is selected we want to toggle the value by pressing the enter
+      // key.
+      this.$el.keydownEvent = (event) => {
+        if (event.keyCode === 13) {
+          this.toggle(this.value)
+        }
+      }
+      document.body.addEventListener('keydown', this.$el.keydownEvent)
+    },
+    beforeUnSelect() {
+      document.body.removeEventListener('keydown', this.$el.keydownEvent)
+    },
     toggle(value) {
       if (!this.selected) {
         return
diff --git a/web-frontend/modules/database/components/view/grid/GridViewFieldNumber.vue b/web-frontend/modules/database/components/view/grid/GridViewFieldNumber.vue
index be822f12b..936fcf7f6 100644
--- a/web-frontend/modules/database/components/view/grid/GridViewFieldNumber.vue
+++ b/web-frontend/modules/database/components/view/grid/GridViewFieldNumber.vue
@@ -25,42 +25,17 @@
 <script>
 import gridField from '@baserow/modules/database/mixins/gridField'
 import gridFieldInput from '@baserow/modules/database/mixins/gridFieldInput'
+import numberField from '@baserow/modules/database/mixins/numberField'
 
 export default {
-  mixins: [gridField, gridFieldInput],
+  mixins: [gridField, gridFieldInput, numberField],
   methods: {
-    getError() {
-      if (this.copy === null || this.copy === '') {
-        return null
-      }
-      if (isNaN(parseFloat(this.copy)) || !isFinite(this.copy)) {
-        return 'Invalid number'
-      }
-      return null
-    },
-    isValid() {
-      return this.getError() === null
-    },
     afterEdit() {
       this.$nextTick(() => {
         this.$refs.input.focus()
         this.$refs.input.selectionStart = this.$refs.input.selectionEnd = 100000
       })
     },
-    beforeSave(value) {
-      if (value === '' || isNaN(value) || value === undefined) {
-        return null
-      }
-      const decimalPlaces =
-        this.field.number_type === 'DECIMAL'
-          ? this.field.number_decimal_places
-          : 0
-      let number = parseFloat(value)
-      if (!this.field.number_negative && number < 0) {
-        number = 0
-      }
-      return number.toFixed(decimalPlaces)
-    },
   },
 }
 </script>
diff --git a/web-frontend/modules/database/components/view/grid/GridViewFieldType.vue b/web-frontend/modules/database/components/view/grid/GridViewFieldType.vue
index 04c9172ed..d75ed4c88 100644
--- a/web-frontend/modules/database/components/view/grid/GridViewFieldType.vue
+++ b/web-frontend/modules/database/components/view/grid/GridViewFieldType.vue
@@ -15,72 +15,23 @@
       >
         <i class="fas fa-caret-down"></i>
       </a>
-      <Context ref="context">
-        <ul class="context-menu">
-          <li>
-            <a
-              ref="updateFieldContextLink"
-              class="grid-view-description-options"
-              @click="
-                $refs.updateFieldContext.toggle(
-                  $refs.updateFieldContextLink,
-                  'bottom',
-                  'right',
-                  0
-                )
-              "
-            >
-              <i class="context-menu-icon fas fa-fw fa-pen"></i>
-              Edit field
-            </a>
-            <UpdateFieldContext
-              ref="updateFieldContext"
-              :field="field"
-              @update="$refs.context.hide()"
-            ></UpdateFieldContext>
-          </li>
-          <li v-if="!field.primary">
-            <a @click="deleteField(field)">
-              <i class="context-menu-icon fas fa-fw fa-trash"></i>
-              Delete field
-            </a>
-          </li>
-        </ul>
-      </Context>
+      <FieldContext ref="context" :field="field"></FieldContext>
       <slot></slot>
     </div>
   </div>
 </template>
 
 <script>
-import { notifyIf } from '@baserow/modules/core/utils/error'
-import UpdateFieldContext from '@baserow/modules/database/components/field/UpdateFieldContext'
+import FieldContext from '@baserow/modules/database/components/field/FieldContext'
 
 export default {
   name: 'GridViewFieldType',
-  components: { UpdateFieldContext },
+  components: { FieldContext },
   props: {
     field: {
       type: Object,
       required: true,
     },
   },
-  methods: {
-    setLoading(field, value) {
-      this.$store.dispatch('field/setItemLoading', { field, value })
-    },
-    async deleteField(field) {
-      this.$refs.context.hide()
-      this.setLoading(field, true)
-
-      try {
-        await this.$store.dispatch('field/delete', field)
-      } catch (error) {
-        notifyIf(error, 'field')
-      }
-
-      this.setLoading(field, false)
-    },
-  },
 }
 </script>
diff --git a/web-frontend/modules/database/fieldTypes.js b/web-frontend/modules/database/fieldTypes.js
index 76534f361..dc46e67fc 100644
--- a/web-frontend/modules/database/fieldTypes.js
+++ b/web-frontend/modules/database/fieldTypes.js
@@ -7,6 +7,10 @@ import GridViewFieldText from '@baserow/modules/database/components/view/grid/Gr
 import GridViewFieldNumber from '@baserow/modules/database/components/view/grid/GridViewFieldNumber'
 import GridViewFieldBoolean from '@baserow/modules/database/components/view/grid/GridViewFieldBoolean'
 
+import RowEditFieldText from '@baserow/modules/database/components/row/RowEditFieldText'
+import RowEditFieldNumber from '@baserow/modules/database/components/row/RowEditFieldNumber'
+import RowEditFieldBoolean from '@baserow/modules/database/components/row/RowEditFieldBoolean'
+
 export class FieldType extends Registerable {
   /**
    * The font awesome 5 icon name that is used as convenience for the user to
@@ -37,7 +41,9 @@ export class FieldType extends Registerable {
   }
 
   /**
-   * @TODO make this depending on the view type.
+   * This grid view field component should represent the related row value of this
+   * type. It will only be used in the grid view and it also responsible for editing
+   * the value.
    */
   getGridViewFieldComponent() {
     throw new Error(
@@ -45,6 +51,17 @@ export class FieldType extends Registerable {
     )
   }
 
+  /**
+   * The row edit field should represent a the related row value of this type. It
+   * will be used in the row edit modal, but can also be used in other forms. It is
+   * responsible for editing the value.
+   */
+  getRowEditFieldComponent() {
+    throw new Error(
+      'Not implement error. This method should return a component.'
+    )
+  }
+
   /**
    * Because we want to show a new row immediately after creating we need to have an
    * empty value to show right away.
@@ -90,6 +107,13 @@ export class FieldType extends Registerable {
       name: this.name,
     }
   }
+
+  /**
+   * Should return a for humans readable representation of the value.
+   */
+  toHumanReadableString(field, value) {
+    return value
+  }
 }
 
 export class TextFieldType extends FieldType {
@@ -113,6 +137,10 @@ export class TextFieldType extends FieldType {
     return GridViewFieldText
   }
 
+  getRowEditFieldComponent() {
+    return RowEditFieldText
+  }
+
   getEmptyValue(field) {
     return field.text_default
   }
@@ -138,6 +166,10 @@ export class NumberFieldType extends FieldType {
   getGridViewFieldComponent() {
     return GridViewFieldNumber
   }
+
+  getRowEditFieldComponent() {
+    return RowEditFieldNumber
+  }
 }
 
 export class BooleanFieldType extends FieldType {
@@ -157,6 +189,10 @@ export class BooleanFieldType extends FieldType {
     return GridViewFieldBoolean
   }
 
+  getRowEditFieldComponent() {
+    return RowEditFieldBoolean
+  }
+
   getEmptyValue(field) {
     return false
   }
diff --git a/web-frontend/modules/database/mixins/gridFieldInput.js b/web-frontend/modules/database/mixins/gridFieldInput.js
index 91566d549..8ba7cabb2 100644
--- a/web-frontend/modules/database/mixins/gridFieldInput.js
+++ b/web-frontend/modules/database/mixins/gridFieldInput.js
@@ -8,11 +8,11 @@ export default {
   data() {
     return {
       /**
-       * Indicates whether of the user is editing the value.
+       * Indicates whether the user is editing the value.
        */
       editing: false,
       /**
-       *  A temporary copy of the value when editing.
+       * A temporary copy of the value when editing.
        */
       copy: null,
     }
diff --git a/web-frontend/modules/database/mixins/numberField.js b/web-frontend/modules/database/mixins/numberField.js
new file mode 100644
index 000000000..49c860abf
--- /dev/null
+++ b/web-frontend/modules/database/mixins/numberField.js
@@ -0,0 +1,43 @@
+/**
+ * This mixin contains some method overrides for validating and formatting the
+ * number field. This mixin is used in both the GridViewFieldNumber and
+ * RowEditFieldNumber components.
+ */
+export default {
+  methods: {
+    /**
+     * Generates a human readable error for the user if something is wrong.
+     */
+    getError() {
+      if (this.copy === null || this.copy === '') {
+        return null
+      }
+      if (isNaN(parseFloat(this.copy)) || !isFinite(this.copy)) {
+        return 'Invalid number'
+      }
+      return null
+    },
+    isValid() {
+      return this.getError() === null
+    },
+    /**
+     * Formats the value based on the field's settings. The number will be rounded
+     * if to much decimal places are provided and if negative numbers aren't allowed
+     * they will be set to 0.
+     */
+    beforeSave(value) {
+      if (value === '' || isNaN(value) || value === undefined) {
+        return null
+      }
+      const decimalPlaces =
+        this.field.number_type === 'DECIMAL'
+          ? this.field.number_decimal_places
+          : 0
+      let number = parseFloat(value)
+      if (!this.field.number_negative && number < 0) {
+        number = 0
+      }
+      return number.toFixed(decimalPlaces)
+    },
+  },
+}
diff --git a/web-frontend/modules/database/mixins/rowEditField.js b/web-frontend/modules/database/mixins/rowEditField.js
new file mode 100644
index 000000000..0c7dc1f43
--- /dev/null
+++ b/web-frontend/modules/database/mixins/rowEditField.js
@@ -0,0 +1,25 @@
+/**
+ * A mixin that can be used by a row edit modal component. It introduces the props that
+ * will be passed by the RowEditModalField component.
+ */
+export default {
+  props: {
+    /**
+     * Contains the field type object. Because each field type can have different
+     * settings you need this in order to render the correct component or implement
+     * correct validation.
+     */
+    field: {
+      type: Object,
+      required: true,
+    },
+    /**
+     * The value of the grid field, this could for example for a number field 10,
+     * text field 'Random string' etc.
+     */
+    value: {
+      type: [String, Number, Object, Boolean],
+      required: false,
+    },
+  },
+}
diff --git a/web-frontend/modules/database/mixins/rowEditFieldInput.js b/web-frontend/modules/database/mixins/rowEditFieldInput.js
new file mode 100644
index 000000000..7fdd58e76
--- /dev/null
+++ b/web-frontend/modules/database/mixins/rowEditFieldInput.js
@@ -0,0 +1,84 @@
+/**
+ * This mixin can be used with a row edit field if the field only needs an input. For
+ * example for the text and number fields. It depends on the rowEditField mixin.
+ */
+export default {
+  data() {
+    return {
+      /**
+       * Indicates whether the user is editing the value.
+       */
+      editing: false,
+      /**
+       * A temporary copy of the value when editing.
+       */
+      copy: null,
+    }
+  },
+  watch: {
+    value(value) {
+      if (!this.editing) {
+        this.copy = value
+      }
+    },
+  },
+  mounted() {
+    this.copy = this.value
+  },
+  methods: {
+    /**
+     * Event that is called when the user starts editing the value. In this case we
+     * will only enable the editing state.
+     */
+    select() {
+      this.editing = true
+    },
+    /**
+     * Event that is called when the user finishes editing. If the value is not
+     * valid we aren't going to do anything because it can't be changed anyway and
+     * we want to give the user a change to fix the value.
+     */
+    unselect() {
+      if (!this.isValid() || !this.editing) {
+        return
+      }
+
+      this.editing = false
+      this.save()
+    },
+    /**
+     * Saves the value if it has changed. Should only be called by the unselect
+     * method and not directly.
+     */
+    save() {
+      const newValue = this.beforeSave(this.copy)
+
+      // If the value hasn't changed we don't want to do anything.
+      if (newValue === this.value) {
+        this.copy = this.value
+      } else {
+        this.$emit('update', newValue, this.value)
+        this.afterSave()
+      }
+    },
+    /**
+     * This method is called before saving the value. Optionally the value can be
+     * changed or formatted here if necessary.
+     */
+    beforeSave(value) {
+      return value
+    },
+    /**
+     * Method that is called after saving the value. This can be overridden in the
+     * component.
+     */
+    afterSave() {},
+    /**
+     * Should return a boolean if the copy that is going to be saved is valid. If it
+     * returns false unselecting is not possible.
+     */
+    isValid() {
+      return true
+    },
+  },
+}
diff --git a/web-frontend/modules/database/store/view/grid.js b/web-frontend/modules/database/store/view/grid.js
index 02804f02b..4070fc382 100644
--- a/web-frontend/modules/database/store/view/grid.js
+++ b/web-frontend/modules/database/store/view/grid.js
@@ -1,3 +1,4 @@
+import Vue from 'vue'
 import axios from 'axios'
 import _ from 'lodash'
 
@@ -5,7 +6,10 @@ import GridService from '@baserow/modules/database/services/view/grid'
 import RowService from '@baserow/modules/database/services/row'
 
 export function populateRow(row) {
-  row._ = { loading: false }
+  row._ = {
+    loading: false,
+    hover: false,
+  }
   return row
 }
 
@@ -118,7 +122,10 @@ export const mutations = {
   ADD_FIELD(state, { field, value }) {
     const name = `field_${field.id}`
     state.rows.forEach((row) => {
-      row[name] = value
+      // We have to use the Vue.set function here to make it reactive immediately.
+      // If we don't do this the value in the field components of the grid and modal
+      // don't have the correct value and will act strange.
+      Vue.set(row, name, value)
     })
   },
   SET_ROW_LOADING(state, { row, value }) {
@@ -136,6 +143,9 @@ export const mutations = {
       })
     }
   },
+  SET_ROW_HOVER(state, { row, value }) {
+    row._.hover = value
+  },
 }
 
 // Contains the timeout needed for the delayed delayed scroll top action.
@@ -549,6 +559,9 @@ export const actions = {
       values,
     })
   },
+  setRowHover({ commit }, { row, value }) {
+    commit('SET_ROW_HOVER', { row, value })
+  },
 }
 
 export const getters = {