diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index a9418dd0bb28ebf3599bfe87468bd8196637ac80..5c253bfd0c1455002d4cdb73e342accb07ca57a3 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -57,8 +57,14 @@ export default {
       store,
       state: store.state,
       formState: store.formState,
+      showForm: false,
     };
   },
+  computed: {
+    elementType() {
+      return this.showForm ? 'form' : 'div';
+    },
+  },
   components: {
     descriptionComponent,
     titleComponent,
@@ -90,6 +96,14 @@ export default {
         });
     },
   },
+  methods: {
+    openForm() {
+      this.showForm = true;
+      this.store.formState = {
+        title: this.state.titleText,
+      };
+    },
+  },
   created() {
     this.service = new Service(this.endpoint);
     this.poll = new Poll({
@@ -117,17 +131,21 @@ export default {
 
     eventHub.$on('delete.issuable', this.deleteIssuable);
     eventHub.$on('update.issuable', this.updateIssuable);
+    eventHub.$on('open.form', this.openForm);
   },
   beforeDestroy() {
     eventHub.$off('delete.issuable', this.deleteIssuable);
     eventHub.$off('update.issuable', this.updateIssuable);
+    eventHub.$on('open.form', this.openForm);
   },
 };
 </script>
 
 <template>
-  <div>
+  <div :is="elementType">
     <title-component
+      :store="store"
+      :show-form="showForm"
       :issuable-ref="issuableRef"
       :title-html="state.titleHtml"
       :title-text="state.titleText" />
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
new file mode 100644
index 0000000000000000000000000000000000000000..732a4021205c41031e93686546d05521c71b9a0f
--- /dev/null
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -0,0 +1,32 @@
+<script>
+  export default {
+    props: {
+      store: {
+        type: Object,
+        required: true,
+      },
+    },
+    data() {
+      return {
+        state: this.store.formState,
+      };
+    },
+  };
+</script>
+
+<template>
+  <fieldset>
+    <label
+      class="sr-only"
+      for="issue-title">
+      Title
+    </label>
+    <input
+      id="issue-title"
+      class="form-control"
+      type="text"
+      placeholder="Issue title"
+      aria-label="Issue title"
+      v-model="state.title" />
+  </fieldset>
+</template>
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index a9dabd4cff1a72a822943ebc45d81d79759b2085..a61ce414891e61bb5fbaab814d5a316febf3b993 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -1,8 +1,12 @@
 <script>
   import animateMixin from '../mixins/animate';
+  import titleField from './fields/title.vue';
 
   export default {
     mixins: [animateMixin],
+    components: {
+      titleField,
+    },
     data() {
       return {
         preAnimation: false,
@@ -23,6 +27,14 @@
         type: String,
         required: true,
       },
+      store: {
+        type: Object,
+        required: true,
+      },
+      showForm: {
+        type: Boolean,
+        required: true,
+      },
     },
     watch: {
       titleHtml() {
@@ -41,13 +53,19 @@
 </script>
 
 <template>
-  <h2
-    class="title"
-    :class="{
-      'issue-realtime-pre-pulse': preAnimation,
-      'issue-realtime-trigger-pulse': pulseAnimation
-    }"
-    v-html="titleHtml"
-  >
-  </h2>
+  <div>
+    <title-field
+      v-if="showForm"
+      :store="store" />
+    <h2
+      v-else
+      class="title"
+      :class="{
+        'issue-realtime-pre-pulse': preAnimation,
+        'issue-realtime-trigger-pulse': pulseAnimation
+      }"
+      v-html="titleHtml"
+    >
+    </h2>
+  </div>
 </template>
diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js
index 246ccf6632360792d11692c9d45e672ee2960ea0..58a8d0590e169cee27b7c21c80f448ef32ae8931 100644
--- a/app/assets/javascripts/issue_show/index.js
+++ b/app/assets/javascripts/issue_show/index.js
@@ -35,25 +35,8 @@ document.addEventListener('DOMContentLoaded', () => {
         initialTitle: issuableTitleElement.innerHTML,
         initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '',
         initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '',
-        showForm: false,
       };
     },
-    methods: {
-      openForm() {
-        this.showForm = true;
-      },
-      closeForm() {
-        this.showForm = false;
-      },
-    },
-    created() {
-      eventHub.$on('open.form', this.openForm);
-      eventHub.$on('close.form', this.closeForm);
-    },
-    beforeDestroy() {
-      eventHub.$off('open.form', this.openForm);
-      eventHub.$off('close.form', this.closeForm);
-    },
     render(createElement) {
       return createElement('issuable-app', {
         props: {
diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js
index b2c1b9d1c6e49e5b5de397526ce8318cc8f19cf2..0ab52c307a07fc72eb4460b30294aee8e3a1bf5f 100644
--- a/app/assets/javascripts/issue_show/stores/index.js
+++ b/app/assets/javascripts/issue_show/stores/index.js
@@ -12,7 +12,9 @@ export default class Store {
       taskStatus: '',
       updatedAt: '',
     };
-    this.formState = {};
+    this.formState = {
+      title: '',
+    };
   }
 
   updateState(data) {
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 1f46d836e1e71e610967e039909fc2cfece75a91..1c47ba49b187eb8eda70143584656255e3743c30 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -75,6 +75,18 @@ describe('Issuable output', () => {
     });
   });
 
+  it('changes element to `form` when open', (done) => {
+    vm.showForm = true;
+
+    Vue.nextTick(() => {
+      expect(
+        vm.$el.tagName,
+      ).toBe('FORM');
+
+      done();
+    });
+  });
+
   it('does not show actions if permissions are incorrect', (done) => {
     vm.showForm = true;
     vm.canUpdate = false;
diff --git a/spec/javascripts/issue_show/components/fields/title_spec.js b/spec/javascripts/issue_show/components/fields/title_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..69afcd24df9bd76902965f2ed48eedd71af5954d
--- /dev/null
+++ b/spec/javascripts/issue_show/components/fields/title_spec.js
@@ -0,0 +1,30 @@
+import Vue from 'vue';
+import Store from '~/issue_show/stores';
+import titleField from '~/issue_show/components/fields/title.vue';
+
+describe('Title field component', () => {
+  let vm;
+  let store;
+
+  beforeEach(() => {
+    const Component = Vue.extend(titleField);
+    store = new Store({
+      titleHtml: '',
+      descriptionHtml: '',
+      issuableRef: '',
+    });
+    store.formState.title = 'test';
+
+    vm = new Component({
+      propsData: {
+        store,
+      },
+    }).$mount();
+  });
+
+  it('renders form control with formState title', () => {
+    expect(
+      vm.$el.querySelector('.form-control').value,
+    ).toBe('test');
+  });
+});
diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/javascripts/issue_show/components/title_spec.js
index 2f953e7e92e4f66ac2c9905041b9dddb5fefbcc3..45ec8718eb627a7955892d12a1af1edc6954c908 100644
--- a/spec/javascripts/issue_show/components/title_spec.js
+++ b/spec/javascripts/issue_show/components/title_spec.js
@@ -1,4 +1,5 @@
 import Vue from 'vue';
+import Store from '~/issue_show/stores';
 import titleComponent from '~/issue_show/components/title.vue';
 
 describe('Title component', () => {
@@ -11,13 +12,19 @@ describe('Title component', () => {
         issuableRef: '#1',
         titleHtml: 'Testing <img />',
         titleText: 'Testing',
+        showForm: false,
+        store: new Store({
+          titleHtml: '',
+          descriptionHtml: '',
+          issuableRef: '',
+        }),
       },
     }).$mount();
   });
 
   it('renders title HTML', () => {
     expect(
-      vm.$el.innerHTML.trim(),
+      vm.$el.querySelector('h2').innerHTML.trim(),
     ).toBe('Testing <img>');
   });
 
@@ -39,12 +46,12 @@ describe('Title component', () => {
 
     Vue.nextTick(() => {
       expect(
-        vm.$el.classList.contains('issue-realtime-pre-pulse'),
+        vm.$el.querySelector('h2').classList.contains('issue-realtime-pre-pulse'),
       ).toBeTruthy();
 
       setTimeout(() => {
         expect(
-          vm.$el.classList.contains('issue-realtime-trigger-pulse'),
+          vm.$el.querySelector('h2').classList.contains('issue-realtime-trigger-pulse'),
         ).toBeTruthy();
 
         done();