diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 30d3be453be68f93967ad1ce008a1e6e7b0fcc8c..67c0c419713b977fb69cb54507f9bcbaa0f0510a 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -2,7 +2,8 @@
 /* global Vue */
 /* global Sortable */
 
-require('./board_blank_state');
+import boardBlankState from './board_blank_state';
+
 require('./board_delete');
 require('./board_list');
 
@@ -17,7 +18,7 @@ require('./board_list');
     components: {
       'board-list': gl.issueBoards.BoardList,
       'board-delete': gl.issueBoards.BoardDelete,
-      'board-blank-state': gl.issueBoards.BoardBlankState
+      boardBlankState,
     },
     props: {
       list: Object,
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js
index d76314c1892b5e3a7bcf06012dda6ad50b651a8d..52893d4642b9d656f2c3428c49c18b552023ff0f 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js
+++ b/app/assets/javascripts/boards/components/board_blank_state.js
@@ -1,53 +1,84 @@
-/* eslint-disable space-before-function-paren, comma-dangle */
-/* global Vue */
 /* global ListLabel */
+/* global Cookies */
+const Store = gl.issueBoards.BoardsStore;
 
-(() => {
-  const Store = gl.issueBoards.BoardsStore;
+export default {
+  template: `
+    <div class="board-blank-state">
+      <p>
+        Add the following default lists to your Issue Board with one click:
+      </p>
+      <ul class="board-blank-state-list">
+        <li v-for="label in predefinedLabels">
+          <span
+            class="label-color"
+            :style="{ backgroundColor: label.color }">
+          </span>
+          {{ label.title }}
+        </li>
+      </ul>
+      <p>
+        Starting out with the default set of lists will get you right on the way to making the most of your board.
+      </p>
+      <button
+        class="btn btn-create btn-inverted btn-block"
+        type="button"
+        @click.stop="addDefaultLists">
+        Add default lists
+      </button>
+      <button
+        class="btn btn-default btn-block"
+        type="button"
+        @click.stop="clearBlankState">
+        Nevermind, I'll use my own
+      </button>
+    </div>
+  `,
+  data() {
+    return {
+      predefinedLabels: [
+        new ListLabel({ title: 'To Do', color: '#F0AD4E' }),
+        new ListLabel({ title: 'Doing', color: '#5CB85C' }),
+      ],
+    };
+  },
+  methods: {
+    addDefaultLists() {
+      this.clearBlankState();
 
-  window.gl = window.gl || {};
-  window.gl.issueBoards = window.gl.issueBoards || {};
-
-  gl.issueBoards.BoardBlankState = Vue.extend({
-    data () {
-      return {
-        predefinedLabels: [
-          new ListLabel({ title: 'To Do', color: '#F0AD4E' }),
-          new ListLabel({ title: 'Doing', color: '#5CB85C' })
-        ]
-      };
-    },
-    methods: {
-      addDefaultLists () {
-        this.clearBlankState();
-
-        this.predefinedLabels.forEach((label, i) => {
-          Store.addList({
+      this.predefinedLabels.forEach((label, i) => {
+        Store.addList({
+          title: label.title,
+          position: i,
+          list_type: 'label',
+          label: {
             title: label.title,
-            position: i,
-            list_type: 'label',
-            label: {
-              title: label.title,
-              color: label.color
-            }
-          });
+            color: label.color,
+          },
         });
+      });
 
-        Store.state.lists = _.sortBy(Store.state.lists, 'position');
+      Store.state.lists = _.sortBy(Store.state.lists, 'position');
 
-        // Save the labels
-        gl.boardService.generateDefaultLists()
-          .then((resp) => {
-            resp.json().forEach((listObj) => {
-              const list = Store.findList('title', listObj.title);
+      // Save the labels
+      gl.boardService.generateDefaultLists()
+        .then((resp) => {
+          resp.json().forEach((listObj) => {
+            const list = Store.findList('title', listObj.title);
 
-              list.id = listObj.id;
-              list.label.id = listObj.label.id;
-              list.getIssues();
-            });
+            list.id = listObj.id;
+            list.label.id = listObj.label.id;
+            list.getIssues();
           });
-      },
-      clearBlankState: Store.removeBlankState.bind(Store)
-    }
-  });
-})();
+        })
+        .catch(() => {
+          Store.removeList(undefined, 'label');
+          Cookies.remove('issue_board_welcome_hidden', {
+            path: '',
+          });
+          Store.addBlankState();
+        });
+    },
+    clearBlankState: Store.removeBlankState.bind(Store),
+  },
+};
diff --git a/app/views/projects/boards/components/_blank_state.html.haml b/app/views/projects/boards/components/_blank_state.html.haml
deleted file mode 100644
index 0af40ddf8fe75461a9d906ef53e3d9c459c55d36..0000000000000000000000000000000000000000
--- a/app/views/projects/boards/components/_blank_state.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-%board-blank-state{ "inline-template" => true,
-  "v-if" => 'list.id == "blank"' }
-  .board-blank-state
-    %p
-      Add the following default lists to your Issue Board with one click:
-    %ul.board-blank-state-list
-      %li{ "v-for" => "label in predefinedLabels" }
-        %span.label-color{ ":style" =>  "{ backgroundColor: label.color } " }
-        {{ label.title }}
-    %p
-      Starting out with the default set of lists will get you right on the way to making the most of your board.
-    %button.btn.btn-create.btn-inverted.btn-block{ type: "button", "@click.stop" => "addDefaultLists" }
-      Add default lists
-    %button.btn.btn-default.btn-block{ type: "button", "@click.stop" => "clearBlankState" }
-      Nevermind, I'll use my own
diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml
index 72bce4049de4dca431fcba8c65133cec7cb1df32..0bca6a786cbaa3760c940012fcdc01f56a4a15bd 100644
--- a/app/views/projects/boards/components/_board.html.haml
+++ b/app/views/projects/boards/components/_board.html.haml
@@ -32,4 +32,4 @@
       ":root-path" => "rootPath",
       "ref" => "board-list" }
     - if can?(current_user, :admin_list, @project)
-      = render "projects/boards/components/blank_state"
+      %board-blank-state{ "v-if" => 'list.id == "blank"' }
diff --git a/spec/javascripts/boards/board_blank_state_spec.js b/spec/javascripts/boards/board_blank_state_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..47baf83512fa801aa55aad6b8ea7fda4210dd4de
--- /dev/null
+++ b/spec/javascripts/boards/board_blank_state_spec.js
@@ -0,0 +1,93 @@
+/* global BoardService */
+import Vue from 'vue';
+import '~/boards/stores/boards_store';
+import boardBlankState from '~/boards/components/board_blank_state';
+import './mock_data';
+
+describe('Boards blank state', () => {
+  let vm;
+  let fail = false;
+
+  beforeEach((done) => {
+    const Comp = Vue.extend(boardBlankState);
+
+    gl.issueBoards.BoardsStore.create();
+    gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
+
+    spyOn(gl.boardService, 'generateDefaultLists').and.callFake(() => new Promise((resolve, reject) => {
+      if (fail) {
+        reject();
+      } else {
+        resolve({
+          json() {
+            return [{
+              id: 1,
+              title: 'To Do',
+              label: { id: 1 },
+            }, {
+              id: 2,
+              title: 'Doing',
+              label: { id: 2 },
+            }];
+          },
+        });
+      }
+    }));
+
+    vm = new Comp();
+
+    setTimeout(() => {
+      vm.$mount();
+      done();
+    });
+  });
+
+  it('renders pre-defined labels', () => {
+    expect(
+      vm.$el.querySelectorAll('.board-blank-state-list li').length,
+    ).toBe(2);
+
+    expect(
+      vm.$el.querySelectorAll('.board-blank-state-list li')[0].textContent.trim(),
+    ).toEqual('To Do');
+
+    expect(
+      vm.$el.querySelectorAll('.board-blank-state-list li')[1].textContent.trim(),
+    ).toEqual('Doing');
+  });
+
+  it('clears blank state', (done) => {
+    vm.$el.querySelector('.btn-default').click();
+
+    setTimeout(() => {
+      expect(gl.issueBoards.BoardsStore.welcomeIsHidden()).toBeTruthy();
+
+      done();
+    });
+  });
+
+  it('creates pre-defined labels', (done) => {
+    vm.$el.querySelector('.btn-create').click();
+
+    setTimeout(() => {
+      expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+      expect(gl.issueBoards.BoardsStore.state.lists[0].title).toEqual('To Do');
+      expect(gl.issueBoards.BoardsStore.state.lists[1].title).toEqual('Doing');
+
+      done();
+    });
+  });
+
+  it('resets the store if request fails', (done) => {
+    fail = true;
+
+    vm.$el.querySelector('.btn-create').click();
+
+    setTimeout(() => {
+      expect(gl.issueBoards.BoardsStore.welcomeIsHidden()).toBeFalsy();
+      expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+
+      done();
+    });
+  });
+});