diff --git a/app/assets/javascripts/cycle_analytics/components/banner.vue b/app/assets/javascripts/cycle_analytics/components/banner.vue
new file mode 100644
index 0000000000000000000000000000000000000000..732697c134e5d2321444e057cf000c03311f288a
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/banner.vue
@@ -0,0 +1,55 @@
+<script>
+  import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
+
+  export default {
+    props: {
+      documentationLink: {
+        type: String,
+        required: true,
+      },
+    },
+    computed: {
+      iconCycleAnalyticsSplash() {
+        return iconCycleAnalyticsSplash;
+      },
+    },
+    methods: {
+      dismissOverviewDialog() {
+        this.$emit('dismiss-overview-dialog');
+      },
+    },
+  };
+</script>
+<template>
+  <div class="landing content-block">
+    <button
+      class="js-ca-dismiss-button dismiss-button"
+      type="button"
+      :aria-label="__('Dismiss Cycle Analytics introduction box')"
+      @click="dismissOverviewDialog">
+      <i
+        class="fa fa-times"
+        aria-hidden="true">
+      </i>
+    </button>
+    <div class="svg-container" v-html="iconCycleAnalyticsSplash">
+    </div>
+    <div class="inner-content">
+      <h4>
+        {{__('Introducing Cycle Analytics')}}
+      </h4>
+      <p>
+        {{ __('Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.') }}
+      </p>
+      <p>
+        <a
+          :href="documentationLink"
+          target="_blank"
+          rel="nofollow"
+          class="btn">
+          {{__('Read more')}}
+        </a>
+      </p>
+    </div>
+  </div>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 991fcf114daa992d582b6da95dda0dbe819c17ad..cdf5e3c0290c2dc468b463ca32adde0aef5d05ee 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -3,6 +3,7 @@
 import Vue from 'vue';
 import Cookies from 'js-cookie';
 import Translate from '../vue_shared/translate';
+import banner from './components/banner.vue';
 import stageCodeComponent from './components/stage_code_component.vue';
 import stagePlanComponent from './components/stage_plan_component.vue';
 import stageComponent from './components/stage_component.vue';
@@ -44,6 +45,7 @@ $(() => {
       },
     },
     components: {
+      banner,
       'stage-issue-component': stageComponent,
       'stage-plan-component': stagePlanComponent,
       'stage-code-component': stageCodeComponent,
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index c06e9f323af2225a9f168ba94223de241a3e1d63..71d30da14a9106cabfff94f9e20553a43404e651 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -6,18 +6,9 @@
 
 #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
   - if @cycle_analytics_no_data
-    .landing.content-block{ "v-if" => "!isOverviewDialogDismissed" }
-      %button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box', "@click" => "dismissOverviewDialog()" }
-        = icon("times")
-      .svg-container
-        = custom_icon('icon_cycle_analytics_splash')
-      .inner-content
-        %h4
-          {{ __('Introducing Cycle Analytics') }}
-        %p
-          {{ __('Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.') }}
-        %p
-          = link_to _('Read more'), help_page_path('user/project/cycle_analytics'), target: '_blank', class: 'btn'
+    %banner{ "v-if" => "!isOverviewDialogDismissed",
+      "documentation-link": help_page_path('user/project/cycle_analytics'),
+      "v-on:dismiss-overview-dialog" => "dismissOverviewDialog()" }
   = icon("spinner spin", "v-show" => "isLoading")
   .wrapper{ "v-show" => "!isLoading && !hasError" }
     .panel.panel-default
diff --git a/spec/javascripts/cycle_analytics/banner_spec.js b/spec/javascripts/cycle_analytics/banner_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..fb6b7fee1682bd43388e9f66859e138720697a75
--- /dev/null
+++ b/spec/javascripts/cycle_analytics/banner_spec.js
@@ -0,0 +1,41 @@
+import Vue from 'vue';
+import banner from '~/cycle_analytics/components/banner.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Cycle analytics banner', () => {
+  let vm;
+
+  beforeEach(() => {
+    const Component = Vue.extend(banner);
+    vm = mountComponent(Component, {
+      documentationLink: 'path',
+    });
+  });
+
+  afterEach(() => {
+    vm.$destroy();
+  });
+
+  it('should render cycle analytics information', () => {
+    expect(
+      vm.$el.querySelector('h4').textContent.trim(),
+    ).toEqual('Introducing Cycle Analytics');
+    expect(
+      vm.$el.querySelector('p').textContent.trim(),
+    ).toContain('Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.');
+    expect(
+      vm.$el.querySelector('a').textContent.trim(),
+    ).toEqual('Read more');
+    expect(
+      vm.$el.querySelector('a').getAttribute('href'),
+    ).toEqual('path');
+  });
+
+  it('should emit an event when close button is clicked', () => {
+    spyOn(vm, '$emit');
+
+    vm.$el.querySelector('.js-ca-dismiss-button').click();
+
+    expect(vm.$emit).toHaveBeenCalled();
+  });
+});