diff --git a/app/assets/javascripts/jira_connect/branches/index.js b/app/assets/javascripts/jira_connect/branches/index.js
index 04510fcff4bfc8a611da8087c0e26942591aaa8e..a9a56a6362e682c87b58f5f771e874ea58025cd9 100644
--- a/app/assets/javascripts/jira_connect/branches/index.js
+++ b/app/assets/javascripts/jira_connect/branches/index.js
@@ -5,7 +5,7 @@ import createDefaultClient from '~/lib/graphql';
 
 Vue.use(VueApollo);
 
-export default async function initJiraConnectBranches() {
+export default function initJiraConnectBranches() {
   const el = document.querySelector('.js-jira-connect-create-branch');
   if (!el) {
     return null;
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/app.vue b/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
index c0504cbb6453fcfe1a515da7bfead189a2e75204..7fd4cc38f1146cf15bc615b2595f794c5f863519 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
@@ -7,6 +7,7 @@ import { SET_ALERT } from '../store/mutation_types';
 import SubscriptionsList from './subscriptions_list.vue';
 import AddNamespaceButton from './add_namespace_button.vue';
 import SignInButton from './sign_in_button.vue';
+import UserLink from './user_link.vue';
 
 export default {
   name: 'JiraConnectApp',
@@ -18,6 +19,7 @@ export default {
     SubscriptionsList,
     AddNamespaceButton,
     SignInButton,
+    UserLink,
   },
   inject: {
     usersPath: {
@@ -74,6 +76,8 @@ export default {
       </template>
     </gl-alert>
 
+    <user-link :user-signed-in="userSignedIn" :has-subscriptions="hasSubscriptions" />
+
     <h2 class="gl-text-center gl-mb-7">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
     <div class="jira-connect-app-body gl-mx-auto gl-px-5 gl-mb-7">
       <template v-if="hasSubscriptions">
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/user_link.vue b/app/assets/javascripts/jira_connect/subscriptions/components/user_link.vue
new file mode 100644
index 0000000000000000000000000000000000000000..fad3d2616d80175997c57448069562f033873038
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/user_link.vue
@@ -0,0 +1,67 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { getGitlabSignInURL } from '~/jira_connect/subscriptions/utils';
+
+export default {
+  components: {
+    GlLink,
+    GlSprintf,
+  },
+  inject: {
+    usersPath: {
+      default: '',
+    },
+    gitlabUserPath: {
+      default: '',
+    },
+  },
+  props: {
+    userSignedIn: {
+      type: Boolean,
+      required: true,
+    },
+    hasSubscriptions: {
+      type: Boolean,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      signInURL: '',
+    };
+  },
+  computed: {
+    gitlabUserHandle() {
+      return `@${gon.current_username}`;
+    },
+  },
+  async created() {
+    this.signInURL = await getGitlabSignInURL(this.usersPath);
+  },
+  i18n: {
+    signInText: __('Sign in to GitLab'),
+    signedInAsUserText: __('Signed in to GitLab as %{user_link}'),
+  },
+};
+</script>
+<template>
+  <div class="jira-connect-user gl-font-base">
+    <gl-sprintf v-if="userSignedIn" :message="$options.i18n.signedInAsUserText">
+      <template #user_link>
+        <gl-link data-testid="gitlab-user-link" :href="gitlabUserPath" target="_blank">
+          {{ gitlabUserHandle }}
+        </gl-link>
+      </template>
+    </gl-sprintf>
+
+    <gl-link
+      v-else-if="hasSubscriptions"
+      data-testid="sign-in-link"
+      :href="signInURL"
+      target="_blank"
+    >
+      {{ $options.i18n.signInText }}
+    </gl-link>
+  </div>
+</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/index.js b/app/assets/javascripts/jira_connect/subscriptions/index.js
index 8a7a80d885d85fe9b628da248fd6da09fe1b8c3f..cd1fc1d4455d861008b7694e0230f7accabbde18 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/index.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/index.js
@@ -7,25 +7,11 @@ import Translate from '~/vue_shared/translate';
 
 import JiraConnectApp from './components/app.vue';
 import createStore from './store';
-import { getGitlabSignInURL, sizeToParent } from './utils';
+import { sizeToParent } from './utils';
 
 const store = createStore();
 
-/**
- * Add `return_to` query param to all HAML-defined GitLab sign in links.
- */
-const updateSignInLinks = async () => {
-  await Promise.all(
-    Array.from(document.querySelectorAll('.js-jira-connect-sign-in')).map(async (el) => {
-      const updatedLink = await getGitlabSignInURL(el.getAttribute('href'));
-      el.setAttribute('href', updatedLink);
-    }),
-  );
-};
-
-export async function initJiraConnect() {
-  await updateSignInLinks();
-
+export function initJiraConnect() {
   const el = document.querySelector('.js-jira-connect-app');
   if (!el) {
     return null;
@@ -35,7 +21,7 @@ export async function initJiraConnect() {
   Vue.use(Translate);
   Vue.use(GlFeatureFlagsPlugin);
 
-  const { groupsPath, subscriptions, subscriptionsPath, usersPath } = el.dataset;
+  const { groupsPath, subscriptions, subscriptionsPath, usersPath, gitlabUserPath } = el.dataset;
   sizeToParent();
 
   return new Vue({
@@ -46,6 +32,7 @@ export async function initJiraConnect() {
       subscriptions: JSON.parse(subscriptions),
       subscriptionsPath,
       usersPath,
+      gitlabUserPath,
     },
     render(createElement) {
       return createElement(JiraConnectApp);
diff --git a/app/helpers/jira_connect_helper.rb b/app/helpers/jira_connect_helper.rb
index 475469a6df9d73842b7d693a004829f880bc23b4..9a0f0944fd16d90c5da0e6e1cd02af5e5f2e4431 100644
--- a/app/helpers/jira_connect_helper.rb
+++ b/app/helpers/jira_connect_helper.rb
@@ -8,7 +8,8 @@ def jira_connect_app_data(subscriptions)
       groups_path: api_v4_groups_path(params: { min_access_level: Gitlab::Access::MAINTAINER, skip_groups: skip_groups }),
       subscriptions: subscriptions.map { |s| serialize_subscription(s) }.to_json,
       subscriptions_path: jira_connect_subscriptions_path,
-      users_path: current_user ? nil : jira_connect_users_path
+      users_path: current_user ? nil : jira_connect_users_path, # users_path is used to determine if user is signed in
+      gitlab_user_path: current_user ? user_path(current_user) : nil
     }
   end
 
diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml
index be2be7288f8b9fb87a082aa12d164cde2f0fb3fd..d92c30c88405c5882098d091f84e1aae2f8af7bb 100644
--- a/app/views/jira_connect/subscriptions/index.html.haml
+++ b/app/views/jira_connect/subscriptions/index.html.haml
@@ -1,13 +1,6 @@
 %header.jira-connect-header.gl-display-flex.gl-align-items-center.gl-justify-content-center.gl-px-5.gl-border-b-solid.gl-border-b-gray-100.gl-border-b-1.gl-bg-white
   = link_to brand_header_logo, Gitlab.config.gitlab.url, target: '_blank', rel: 'noopener noreferrer'
 
-.jira-connect-user.gl-font-base
-  - if current_user
-    - user_link = link_to(current_user.to_reference, jira_connect_users_path, target: '_blank', rel: 'noopener noreferrer', class: 'js-jira-connect-sign-in')
-    = _('Signed in to GitLab as %{user_link}').html_safe % { user_link: user_link }
-  - elsif @subscriptions.present?
-    = link_to _('Sign in to GitLab'), jira_connect_users_path, target: '_blank', rel: 'noopener noreferrer', class: 'js-jira-connect-sign-in'
-
 %main.jira-connect-app.gl-px-5.gl-pt-7.gl-mx-auto
   .js-jira-connect-app{ data: jira_connect_app_data(@subscriptions) }
 
diff --git a/spec/frontend/jira_connect/subscriptions/components/app_spec.js b/spec/frontend/jira_connect/subscriptions/components/app_spec.js
index 8e46496845362b89e42e301482854501cecd7301..47fe96262eec299f5b089d1ea66f1d836ffd9761 100644
--- a/spec/frontend/jira_connect/subscriptions/components/app_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/components/app_spec.js
@@ -5,6 +5,7 @@ import JiraConnectApp from '~/jira_connect/subscriptions/components/app.vue';
 import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
 import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
 import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
+import UserLink from '~/jira_connect/subscriptions/components/user_link.vue';
 import createStore from '~/jira_connect/subscriptions/store';
 import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
 import { __ } from '~/locale';
@@ -12,6 +13,7 @@ import { mockSubscription } from '../mock_data';
 
 jest.mock('~/jira_connect/subscriptions/utils', () => ({
   retrieveAlert: jest.fn().mockReturnValue({ message: 'error message' }),
+  getGitlabSignInURL: jest.fn(),
 }));
 
 describe('JiraConnectApp', () => {
@@ -83,6 +85,22 @@ describe('JiraConnectApp', () => {
         });
       },
     );
+
+    it('renders UserLink component', () => {
+      createComponent({
+        provide: {
+          usersPath: '/user',
+          subscriptions: [],
+        },
+      });
+
+      const userLink = wrapper.findComponent(UserLink);
+      expect(userLink.exists()).toBe(true);
+      expect(userLink.props()).toEqual({
+        hasSubscriptions: false,
+        userSignedIn: false,
+      });
+    });
   });
 
   describe('alert', () => {
diff --git a/spec/frontend/jira_connect/subscriptions/components/user_link_spec.js b/spec/frontend/jira_connect/subscriptions/components/user_link_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..b98a36269a322e328e57bf5ea3f3c84868d455d1
--- /dev/null
+++ b/spec/frontend/jira_connect/subscriptions/components/user_link_spec.js
@@ -0,0 +1,91 @@
+import { GlSprintf } from '@gitlab/ui';
+import UserLink from '~/jira_connect/subscriptions/components/user_link.vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+jest.mock('~/jira_connect/subscriptions/utils', () => ({
+  getGitlabSignInURL: jest.fn().mockImplementation((path) => Promise.resolve(path)),
+}));
+
+describe('SubscriptionsList', () => {
+  let wrapper;
+
+  const createComponent = (propsData = {}, { provide } = {}) => {
+    wrapper = shallowMountExtended(UserLink, {
+      propsData,
+      provide,
+      stubs: {
+        GlSprintf,
+      },
+    });
+  };
+
+  const findSignInLink = () => wrapper.findByTestId('sign-in-link');
+  const findGitlabUserLink = () => wrapper.findByTestId('gitlab-user-link');
+  const findSprintf = () => wrapper.findComponent(GlSprintf);
+
+  afterEach(() => {
+    wrapper.destroy();
+  });
+
+  describe.each`
+    userSignedIn | hasSubscriptions | expectGlSprintf | expectGlLink
+    ${true}      | ${false}         | ${true}         | ${false}
+    ${false}     | ${true}          | ${false}        | ${true}
+    ${true}      | ${true}          | ${true}         | ${false}
+    ${false}     | ${false}         | ${false}        | ${false}
+  `(
+    'when `userSignedIn` is $userSignedIn and `hasSubscriptions` is $hasSubscriptions',
+    ({ userSignedIn, hasSubscriptions, expectGlSprintf, expectGlLink }) => {
+      it('renders template correctly', () => {
+        createComponent({
+          userSignedIn,
+          hasSubscriptions,
+        });
+
+        expect(findSprintf().exists()).toBe(expectGlSprintf);
+        expect(findSignInLink().exists()).toBe(expectGlLink);
+      });
+    },
+  );
+
+  describe('sign in link', () => {
+    it('renders with correct href', async () => {
+      const mockUsersPath = '/user';
+      createComponent(
+        {
+          userSignedIn: false,
+          hasSubscriptions: true,
+        },
+        { provide: { usersPath: mockUsersPath } },
+      );
+
+      await waitForPromises();
+
+      expect(findSignInLink().exists()).toBe(true);
+      expect(findSignInLink().attributes('href')).toBe(mockUsersPath);
+    });
+  });
+
+  describe('gitlab user link', () => {
+    window.gon = { current_username: 'root' };
+
+    beforeEach(() => {
+      createComponent(
+        {
+          userSignedIn: true,
+          hasSubscriptions: true,
+        },
+        { provide: { gitlabUserPath: '/root' } },
+      );
+    });
+
+    it('renders with correct href', () => {
+      expect(findGitlabUserLink().attributes('href')).toBe('/root');
+    });
+
+    it('contains GitLab user handle', () => {
+      expect(findGitlabUserLink().text()).toBe('@root');
+    });
+  });
+});
diff --git a/spec/frontend/jira_connect/subscriptions/index_spec.js b/spec/frontend/jira_connect/subscriptions/index_spec.js
deleted file mode 100644
index b97918a198e6df57fe542c3526e2a8a179cc0bea..0000000000000000000000000000000000000000
--- a/spec/frontend/jira_connect/subscriptions/index_spec.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { initJiraConnect } from '~/jira_connect/subscriptions';
-import { getGitlabSignInURL } from '~/jira_connect/subscriptions/utils';
-
-jest.mock('~/jira_connect/subscriptions/utils');
-
-describe('initJiraConnect', () => {
-  const mockInitialHref = 'https://gitlab.com';
-
-  beforeEach(() => {
-    setFixtures(`
-      <a class="js-jira-connect-sign-in" href="${mockInitialHref}">Sign In</a>
-      <a class="js-jira-connect-sign-in" href="${mockInitialHref}">Another Sign In</a>
-    `);
-  });
-
-  const assertSignInLinks = (expectedLink) => {
-    Array.from(document.querySelectorAll('.js-jira-connect-sign-in')).forEach((el) => {
-      expect(el.getAttribute('href')).toBe(expectedLink);
-    });
-  };
-
-  describe('Sign in links', () => {
-    it('are updated on initialization', async () => {
-      const mockSignInLink = `https://gitlab.com?return_to=${encodeURIComponent('/test/location')}`;
-      getGitlabSignInURL.mockResolvedValue(mockSignInLink);
-
-      // assert the initial state
-      assertSignInLinks(mockInitialHref);
-
-      await initJiraConnect();
-
-      // assert the update has occurred
-      assertSignInLinks(mockSignInLink);
-    });
-  });
-});
diff --git a/spec/helpers/jira_connect_helper_spec.rb b/spec/helpers/jira_connect_helper_spec.rb
index 55a5c724665df7ce507c08a565ae14a418f87fe5..0f78185dc7d69623b77470a92042b8c276647209 100644
--- a/spec/helpers/jira_connect_helper_spec.rb
+++ b/spec/helpers/jira_connect_helper_spec.rb
@@ -19,7 +19,9 @@
         is_expected.to include(
           :groups_path,
           :subscriptions_path,
-          :users_path
+          :users_path,
+          :subscriptions,
+          :gitlab_user_path
         )
       end
 
@@ -32,6 +34,10 @@
 
         expect(subject[:groups_path]).to include("#{skip_groups_param}=#{subscription.namespace.id}")
       end
+
+      it 'assigns gitlab_user_path to nil' do
+        expect(subject[:gitlab_user_path]).to be_nil
+      end
     end
 
     context 'user is logged in' do
@@ -42,6 +48,10 @@
       it 'assigns users_path to nil' do
         expect(subject[:users_path]).to be_nil
       end
+
+      it 'assigns gitlab_user_path correctly' do
+        expect(subject[:gitlab_user_path]).to eq(user_path(user))
+      end
     end
   end
 end
diff --git a/spec/views/jira_connect/subscriptions/index.html.haml_spec.rb b/spec/views/jira_connect/subscriptions/index.html.haml_spec.rb
deleted file mode 100644
index 0a4d283a983a0f7533b4bb004bcbb52b64b05916..0000000000000000000000000000000000000000
--- a/spec/views/jira_connect/subscriptions/index.html.haml_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'jira_connect/subscriptions/index.html.haml' do
-  let(:user) { build_stubbed(:user) }
-
-  before do
-    allow(view).to receive(:current_user).and_return(user)
-    assign(:subscriptions, create_list(:jira_connect_subscription, 1))
-  end
-
-  context 'when the user is signed in' do
-    it 'shows link to user profile' do
-      render
-
-      expect(rendered).to have_link(user.to_reference)
-    end
-  end
-
-  context 'when the user is not signed in' do
-    let(:user) { nil }
-
-    it 'shows "Sign in" link' do
-      render
-
-      expect(rendered).to have_link('Sign in to GitLab')
-    end
-  end
-end