diff --git a/app/assets/javascripts/usage_quotas/group_view_metadata.js b/app/assets/javascripts/usage_quotas/group_view_metadata.js
index 7ff3ff1aabfc1d0337b3a0241da5f4c7c7f89902..e90ae19bd7b93033de7cf2e1543ed5fd0afa862f 100644
--- a/app/assets/javascripts/usage_quotas/group_view_metadata.js
+++ b/app/assets/javascripts/usage_quotas/group_view_metadata.js
@@ -10,6 +10,7 @@ export const usageQuotasViewProvideData = {
 export const storageTabMetadata = {
   title: __('Storage'),
   component: NamespaceStorageApp,
+  shouldRender: () => document.querySelector(STORAGE_TAB_METADATA_EL_SELECTOR),
 };
 
 export const usageQuotasTabsMetadata = [storageTabMetadata];
diff --git a/ee/app/assets/javascripts/usage_quotas/constants.js b/ee/app/assets/javascripts/usage_quotas/constants.js
index fe57892d186d462c0c96857d47a685f55c7291cf..6c28045bb7e80535d09178fd19df78ae27291f5d 100644
--- a/ee/app/assets/javascripts/usage_quotas/constants.js
+++ b/ee/app/assets/javascripts/usage_quotas/constants.js
@@ -2,3 +2,5 @@ import { s__ } from '~/locale';
 
 export const USAGE_BY_MONTH_HEADER = s__('UsageQuota|Usage by month');
 export const USAGE_BY_PROJECT_HEADER = s__('UsageQuota|Usage by project');
+
+export const PIPELINES_TAB_METADATA_EL_SELECTOR = '#js-pipeline-usage-app';
diff --git a/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js b/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js
index 80c52028b37f1beba96de10fb8ec9164bd9f15c7..51785752c34ee9616fe1e4183754f1e2fc5df038 100644
--- a/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js
+++ b/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js
@@ -1,9 +1,22 @@
+import { __ } from '~/locale';
 import { storageTabMetadata } from '~/usage_quotas/group_view_metadata';
 import { STORAGE_TAB_METADATA_EL_SELECTOR } from '~/usage_quotas/constants';
+import PipelineUsageApp from './pipelines/components/app.vue';
 import { parseProvideData as parseStorageTabProvideData } from './storage/utils';
+import { parseProvideData as parsePipelinesTabProvideData } from './pipelines/utils';
+import { PIPELINES_TAB_METADATA_EL_SELECTOR } from './constants';
 
 export const usageQuotasViewProvideData = {
   ...parseStorageTabProvideData(document.querySelector(STORAGE_TAB_METADATA_EL_SELECTOR)),
+  ...parsePipelinesTabProvideData(document.querySelector(PIPELINES_TAB_METADATA_EL_SELECTOR)),
 };
 
-export const usageQuotasTabsMetadata = [storageTabMetadata];
+const pipelineTabMetadata = {
+  title: __('Pipelines'),
+  component: PipelineUsageApp,
+  shouldRender: () => document.querySelector(PIPELINES_TAB_METADATA_EL_SELECTOR),
+};
+
+export const usageQuotasTabsMetadata = [pipelineTabMetadata, storageTabMetadata].filter((tab) =>
+  tab.shouldRender(),
+);
diff --git a/ee/app/assets/javascripts/usage_quotas/pipelines/index.js b/ee/app/assets/javascripts/usage_quotas/pipelines/index.js
index 4597201af641c1c7fc5eac1bf1e55361f0a40a6d..bfb82de565180309608549a8250c6757063f6f1f 100644
--- a/ee/app/assets/javascripts/usage_quotas/pipelines/index.js
+++ b/ee/app/assets/javascripts/usage_quotas/pipelines/index.js
@@ -1,59 +1,23 @@
 import Vue from 'vue';
 import VueApollo from 'vue-apollo';
-import { parseBoolean } from '~/lib/utils/common_utils';
 import apolloProvider from 'ee/usage_quotas/shared/provider';
+import { PIPELINES_TAB_METADATA_EL_SELECTOR } from '../constants';
 import PipelineUsageApp from './components/app.vue';
+import { parseProvideData } from './utils';
 
 Vue.use(VueApollo);
 
 export default () => {
-  const el = document.getElementById('js-pipeline-usage-app');
+  const el = document.querySelector(PIPELINES_TAB_METADATA_EL_SELECTOR);
 
   if (!el) {
     return false;
   }
 
-  const {
-    pageSize,
-    namespacePath,
-    namespaceId,
-    namespaceActualPlanName,
-    userNamespace,
-    ciMinutesAnyProjectEnabled,
-    ciMinutesDisplayMinutesAvailableData,
-    ciMinutesLastResetDate,
-    ciMinutesMonthlyMinutesLimit,
-    ciMinutesMonthlyMinutesUsed,
-    ciMinutesMonthlyMinutesUsedPercentage,
-    ciMinutesPurchasedMinutesLimit,
-    ciMinutesPurchasedMinutesUsed,
-    ciMinutesPurchasedMinutesUsedPercentage,
-    buyAdditionalMinutesPath,
-    buyAdditionalMinutesTarget,
-  } = el.dataset;
-
   return new Vue({
     el,
     name: 'PipelinesUsageView',
-    provide: {
-      pageSize: Number(pageSize),
-      namespacePath,
-      namespaceId,
-      namespaceActualPlanName,
-      userNamespace: parseBoolean(userNamespace),
-      ciMinutesAnyProjectEnabled: parseBoolean(ciMinutesAnyProjectEnabled),
-      ciMinutesDisplayMinutesAvailableData: parseBoolean(ciMinutesDisplayMinutesAvailableData),
-      ciMinutesLastResetDate,
-      // Limit and Usage could be a number or a string (e.g. `Unlimited`) so we shouldn't parse these
-      ciMinutesMonthlyMinutesLimit,
-      ciMinutesMonthlyMinutesUsed,
-      ciMinutesMonthlyMinutesUsedPercentage,
-      ciMinutesPurchasedMinutesLimit,
-      ciMinutesPurchasedMinutesUsed,
-      ciMinutesPurchasedMinutesUsedPercentage,
-      buyAdditionalMinutesPath,
-      buyAdditionalMinutesTarget,
-    },
+    provide: parseProvideData(el),
     apolloProvider,
     render(createElement) {
       return createElement(PipelineUsageApp);
diff --git a/ee/app/assets/javascripts/usage_quotas/pipelines/utils.js b/ee/app/assets/javascripts/usage_quotas/pipelines/utils.js
index 118422a1fc05175de9252a1c7ae91cce58b05af4..e5af13d11d68a661bcdda3357dc951de78af9596 100644
--- a/ee/app/assets/javascripts/usage_quotas/pipelines/utils.js
+++ b/ee/app/assets/javascripts/usage_quotas/pipelines/utils.js
@@ -1,3 +1,4 @@
+import { parseBoolean } from '~/lib/utils/common_utils';
 import { dateToYearMonthDate, newDateAsLocaleTime } from '~/lib/utils/datetime_utility';
 
 const formatMonthData = (cur) => {
@@ -53,3 +54,48 @@ export const formatIso8601Date = (year, monthIndex, day) => {
     .map((s) => s.padStart(2, '0'))
     .join('-');
 };
+
+export const parseProvideData = (el) => {
+  if (!el) {
+    return {};
+  }
+
+  const {
+    pageSize,
+    namespacePath,
+    namespaceId,
+    namespaceActualPlanName,
+    userNamespace,
+    ciMinutesAnyProjectEnabled,
+    ciMinutesDisplayMinutesAvailableData,
+    ciMinutesLastResetDate,
+    ciMinutesMonthlyMinutesLimit,
+    ciMinutesMonthlyMinutesUsed,
+    ciMinutesMonthlyMinutesUsedPercentage,
+    ciMinutesPurchasedMinutesLimit,
+    ciMinutesPurchasedMinutesUsed,
+    ciMinutesPurchasedMinutesUsedPercentage,
+    buyAdditionalMinutesPath,
+    buyAdditionalMinutesTarget,
+  } = el.dataset;
+
+  return {
+    pageSize: Number(pageSize),
+    namespacePath,
+    namespaceId,
+    namespaceActualPlanName,
+    userNamespace: parseBoolean(userNamespace),
+    ciMinutesAnyProjectEnabled: parseBoolean(ciMinutesAnyProjectEnabled),
+    ciMinutesDisplayMinutesAvailableData: parseBoolean(ciMinutesDisplayMinutesAvailableData),
+    ciMinutesLastResetDate,
+    // Limit and Usage could be a number or a string (e.g. `Unlimited`) so we shouldn't parse these
+    ciMinutesMonthlyMinutesLimit,
+    ciMinutesMonthlyMinutesUsed,
+    ciMinutesMonthlyMinutesUsedPercentage,
+    ciMinutesPurchasedMinutesLimit,
+    ciMinutesPurchasedMinutesUsed,
+    ciMinutesPurchasedMinutesUsedPercentage,
+    buyAdditionalMinutesPath,
+    buyAdditionalMinutesTarget,
+  };
+};
diff --git a/ee/app/views/groups/usage_quotas/index.html.haml b/ee/app/views/groups/usage_quotas/index.html.haml
index 029088200ae0b7b472cbac4d0dd4afc6dffc0a5d..2cc3dc900feaccfe4f552120c7059212ee2d50aa 100644
--- a/ee/app/views/groups/usage_quotas/index.html.haml
+++ b/ee/app/views/groups/usage_quotas/index.html.haml
@@ -9,6 +9,8 @@
 - if Feature.enabled?(:usage_quotas_for_all_editions, @group)
   #js-usage-quotas-view{ data: { namespace_name: @group.name } }
     #js-namespace-storage-app{ data: storage_usage_app_data(@group) }
+    - if can? current_user, :admin_ci_minutes, @group
+      #js-pipeline-usage-app{ data: pipeline_usage_app_data(@group) }
 - else
   - if show_product_purchase_success_alert?
     = render 'product_purchase_success_alert', product_name: params[:purchased_product]