diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue b/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
index 7bc4b05546fb72cdd9d39ae20c4eb41d4bc50130..3832896261512a2129332f1e4d8df5112aff34d8 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
@@ -1,7 +1,7 @@
 <script>
 // eslint-disable-next-line no-restricted-imports
 import { mapActions, mapState, mapGetters } from 'vuex';
-import { GlEmptyState } from '@gitlab/ui';
+import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
 import { refreshCurrentPage } from '~/lib/utils/url_utility';
 import { VSA_METRICS_GROUPS, FLOW_METRICS_QUERY_TYPE } from '~/analytics/shared/constants';
 import {
@@ -28,6 +28,7 @@ export default {
     PageHeading,
     DurationChartLoader,
     GlEmptyState,
+    GlLoadingIcon,
     TypeOfWorkChartsLoader,
     StageTable,
     PathNavigation,
@@ -98,9 +99,6 @@ export default {
     isWaitingForNextAggregation() {
       return Boolean(this.selectedValueStream && !this.aggregation.lastRunAt);
     },
-    shouldRenderEmptyState() {
-      return this.isLoadingValueStreams || !this.hasValueStreams;
-    },
     shouldRenderAggregationWarning() {
       return this.isWaitingForNextAggregation;
     },
@@ -110,13 +108,6 @@ export default {
     selectedStageReady() {
       return !this.hasNoAccessError && this.selectedStage;
     },
-    shouldDisplayCreateMultipleValueStreams() {
-      return Boolean(
-        this.enableCustomizableStages &&
-          !this.shouldRenderEmptyState &&
-          !this.isLoadingValueStreams,
-      );
-    },
     hasDateRangeSet() {
       return this.createdAfter && this.createdBefore;
     },
@@ -212,9 +203,11 @@ export default {
 </script>
 <template>
   <div>
+    <div v-if="isLoadingValueStreams" class="gl-p-7 gl-text-center">
+      <gl-loading-icon size="lg" />
+    </div>
     <value-stream-empty-state
-      v-if="shouldRenderEmptyState"
-      :is-loading="isLoadingValueStreams"
+      v-else-if="!hasValueStreams"
       :empty-state-svg-path="emptyStateSvgPath"
       :has-date-range-error="!hasDateRangeSet"
       :can-edit="canEdit"
@@ -224,7 +217,7 @@ export default {
       <div
         class="gl-mb-6 gl-flex gl-flex-col gl-justify-between gl-gap-3 sm:gl-flex-row sm:gl-items-center"
       >
-        <value-stream-select v-if="shouldDisplayCreateMultipleValueStreams" :can-edit="canEdit" />
+        <value-stream-select v-if="enableCustomizableStages" :can-edit="canEdit" />
         <value-stream-aggregation-status v-if="isAggregationStatusAvailable" :data="aggregation" />
       </div>
       <value-stream-filters
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_empty_state.vue b/ee/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_empty_state.vue
index acd390919e87dbabc19124dc02a304cb345685c2..fa4e02efed95d3e6bddf02d0b3506e51cbdb3329 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_empty_state.vue
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_empty_state.vue
@@ -1,5 +1,5 @@
 <script>
-import { GlButton, GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
+import { GlButton, GlEmptyState } from '@gitlab/ui';
 import { helpPagePath } from '~/helpers/help_page_helper';
 import Tracking from '~/tracking';
 import {
@@ -16,16 +16,10 @@ export default {
   components: {
     GlButton,
     GlEmptyState,
-    GlLoadingIcon,
   },
   mixins: [Tracking.mixin()],
   inject: ['newValueStreamPath'],
   props: {
-    isLoading: {
-      type: Boolean,
-      required: true,
-      default: false,
-    },
     hasDateRangeError: {
       type: Boolean,
       required: true,
@@ -67,31 +61,25 @@ export default {
 };
 </script>
 <template>
-  <div>
-    <div v-if="isLoading" class="gl-p-7 gl-text-center">
-      <gl-loading-icon size="lg" />
-    </div>
-    <gl-empty-state
-      v-else
-      :svg-path="emptyStateSvgPath"
-      :title="title"
-      :description="description"
-      data-testid="vsa-empty-state"
-    >
-      <template v-if="!hasDateRangeError && canEdit" #actions>
-        <gl-button
-          :href="newValueStreamPath"
-          class="gl-mx-2 gl-mb-3"
-          variant="confirm"
-          data-testid="create-value-stream-button"
-          data-track-action="click_button"
-          data-track-label="empty_state_create_value_stream_form_open"
-          >{{ $options.i18n.EMPTY_STATE_ACTION_TEXT }}</gl-button
-        >
-        <gl-button class="gl-mx-2 gl-mb-3" data-testid="learn-more-link" :href="$options.docsPath"
-          >{{ $options.i18n.EMPTY_STATE_SECONDARY_TEXT }}
-        </gl-button>
-      </template>
-    </gl-empty-state>
-  </div>
+  <gl-empty-state
+    :svg-path="emptyStateSvgPath"
+    :title="title"
+    :description="description"
+    data-testid="vsa-empty-state"
+  >
+    <template v-if="!hasDateRangeError && canEdit" #actions>
+      <gl-button
+        :href="newValueStreamPath"
+        class="gl-mx-2 gl-mb-3"
+        variant="confirm"
+        data-testid="create-value-stream-button"
+        data-track-action="click_button"
+        data-track-label="empty_state_create_value_stream_form_open"
+        >{{ $options.i18n.EMPTY_STATE_ACTION_TEXT }}</gl-button
+      >
+      <gl-button class="gl-mx-2 gl-mb-3" data-testid="learn-more-link" :href="$options.docsPath"
+        >{{ $options.i18n.EMPTY_STATE_SECONDARY_TEXT }}
+      </gl-button>
+    </template>
+  </gl-empty-state>
 </template>
diff --git a/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js b/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
index 5eefb42209b6a0e0a96ed5971c521ab1d61e589c..ccc897ef68dbf9402808d1d467bd7cc847c52989 100644
--- a/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
+++ b/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
@@ -1,4 +1,4 @@
-import { GlEmptyState } from '@gitlab/ui';
+import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
 import { shallowMount, mount } from '@vue/test-utils';
 import axios from 'axios';
 import MockAdapter from 'axios-mock-adapter';
@@ -11,7 +11,10 @@ import TypeOfWorkChartsLoader from 'ee/analytics/cycle_analytics/components/task
 import ValueStreamSelect from 'ee/analytics/cycle_analytics/components/value_stream_select.vue';
 import ValueStreamAggregationStatus from 'ee/analytics/cycle_analytics/components/value_stream_aggregation_status.vue';
 import ValueStreamEmptyState from 'ee/analytics/cycle_analytics/components/value_stream_empty_state.vue';
-import createStore from 'ee/analytics/cycle_analytics/store';
+import * as actions from 'ee/analytics/cycle_analytics/store/actions';
+import * as getters from 'ee/analytics/cycle_analytics/store/getters';
+import mutations from 'ee/analytics/cycle_analytics/store/mutations';
+import state from 'ee/analytics/cycle_analytics/store/state';
 import waitForPromises from 'helpers/wait_for_promises';
 import {
   currentGroup,
@@ -22,6 +25,7 @@ import {
   initialPaginationQuery,
   selectedProjects as rawSelectedProjects,
 } from 'jest/analytics/cycle_analytics/mock_data';
+import filters from '~/vue_shared/components/filtered_search_bar/store/modules/filters';
 import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue';
 import { toYmd } from '~/analytics/shared/utils';
 import PathNavigation from '~/analytics/cycle_analytics/components/path_navigation.vue';
@@ -137,18 +141,32 @@ describe('EE Value Stream Analytics component', () => {
       withStageSelected = false,
       features = {},
       initialState = initialCycleAnalyticsState,
+      initializeStore = true,
+      stateOverrides = {},
       props = {},
       selectedStage = null,
     } = options;
 
-    store = createStore();
-    await store.dispatch('initializeCycleAnalytics', {
-      ...initialState,
-      features: {
-        ...features,
+    store = new Vuex.Store({
+      actions,
+      getters,
+      mutations,
+      state: {
+        ...state(),
+        ...stateOverrides,
       },
+      modules: { filters },
     });
 
+    if (initializeStore) {
+      await store.dispatch('initializeCycleAnalytics', {
+        ...initialState,
+        features: {
+          ...features,
+        },
+      });
+    }
+
     const func = shallow ? shallowMount : mount;
     const comp = func(Component, {
       store,
@@ -190,16 +208,32 @@ describe('EE Value Stream Analytics component', () => {
   const findValueStreamSelect = () => wrapper.findComponent(ValueStreamSelect);
   const findUrlSync = () => wrapper.findComponent(UrlSync);
 
-  describe('with no value streams', () => {
+  beforeEach(() => {
+    mock = new MockAdapter(axios);
+  });
+
+  afterEach(() => {
+    mock.restore();
+  });
+
+  describe('when loading', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       wrapper = await createComponent({
-        initialState: { ...initialCycleAnalyticsState, valueStreams: [] },
+        initializeStore: false,
+        stateOverrides: { isLoadingValueStreams: true },
       });
     });
 
-    afterEach(() => {
-      mock.restore();
+    it('displays the loading icon', () => {
+      expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+    });
+  });
+
+  describe('with no value streams', () => {
+    beforeEach(async () => {
+      wrapper = await createComponent({
+        initialState: { ...initialCycleAnalyticsState, valueStreams: [] },
+      });
     });
 
     it('displays an empty state', () => {
@@ -225,7 +259,6 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('the user does not have access to the group', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
 
       wrapper = await createComponent({
@@ -260,15 +293,10 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('the user has access to the group', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
       wrapper = await createComponent({ withStageSelected: true });
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('hides the empty state', () => {
       expect(wrapper.findComponent(GlEmptyState).exists()).toBe(false);
     });
@@ -301,7 +329,6 @@ describe('EE Value Stream Analytics component', () => {
 
     describe('without the overview stage selected', () => {
       beforeEach(async () => {
-        mock = new MockAdapter(axios);
         mockRequiredRoutes(mock, { selectedStageEvents: [{}] });
         wrapper = await createComponent({ selectedStage: issueStage });
       });
@@ -331,7 +358,6 @@ describe('EE Value Stream Analytics component', () => {
 
       describe('without issue events', () => {
         beforeEach(async () => {
-          mock = new MockAdapter(axios);
           mockRequiredRoutes(mock, { selectedStageEvents: [] });
           wrapper = await createComponent({ selectedStage: issueStage });
         });
@@ -378,14 +404,9 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('with failed requests while loading', () => {
     beforeEach(() => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('will display an error if the fetchGroupStagesAndEvents request fails', async () => {
       expect(createAlert).not.toHaveBeenCalled();
 
@@ -425,14 +446,9 @@ describe('EE Value Stream Analytics component', () => {
     const overviewStage = { id: OVERVIEW_STAGE_ID, title: 'Overview' };
 
     beforeEach(() => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('when a stage is selected', async () => {
       wrapper = await createComponent();
 
@@ -477,14 +493,9 @@ describe('EE Value Stream Analytics component', () => {
       commonUtils.historyPushState = jest.fn();
       urlUtils.mergeUrlParams = jest.fn();
 
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     describe('with minimal parameters set', () => {
       beforeEach(async () => {
         wrapper = await createComponent();
@@ -558,14 +569,9 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('with`groupLevelAnalyticsDashboard=true`', () => {
     beforeEach(() => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('renders a link to the value streams dashboard', async () => {
       wrapper = await createComponent({
         withStageSelected: true,
@@ -584,7 +590,6 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('with `enableTasksByTypeChart=false`', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
       wrapper = await createComponent({
         withStageSelected: true,
@@ -594,10 +599,6 @@ describe('EE Value Stream Analytics component', () => {
       });
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('does not display the tasks by type chart', () => {
       expect(findTypeOfWorkCharts().exists()).toBe(false);
     });
@@ -605,7 +606,6 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('with `enableCustomizableStages=false`', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
       wrapper = await createComponent({
         withStageSelected: true,
@@ -617,10 +617,6 @@ describe('EE Value Stream Analytics component', () => {
       });
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('does not display the value stream selector', () => {
       expect(findValueStreamSelect().exists()).toBe(false);
     });
@@ -628,7 +624,6 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('with `enableProjectsFilter=false`', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
       wrapper = await createComponent({
         withStageSelected: true,
@@ -640,10 +635,6 @@ describe('EE Value Stream Analytics component', () => {
       });
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('does not display the project filter', () => {
       expect(findFilterBar().props('hasProjectFilter')).toBe(false);
     });
@@ -651,7 +642,6 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('with a project namespace', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
       wrapper = await createComponent({
         withStageSelected: true,
@@ -666,10 +656,6 @@ describe('EE Value Stream Analytics component', () => {
       });
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('renders a link to the value streams dashboard', () => {
       expect(findOverviewMetrics().props('dashboardsPath')).toBe(
         '/some/cool/path/-/analytics/dashboards/value_streams_dashboard',
@@ -679,7 +665,6 @@ describe('EE Value Stream Analytics component', () => {
 
   describe('when dashboard link is disabled for the project namespace`', () => {
     beforeEach(async () => {
-      mock = new MockAdapter(axios);
       mockRequiredRoutes(mock);
       wrapper = await createComponent({
         withStageSelected: true,
@@ -694,10 +679,6 @@ describe('EE Value Stream Analytics component', () => {
       });
     });
 
-    afterEach(() => {
-      mock.restore();
-    });
-
     it('does not render a link to the value streams dashboard', () => {
       expect(findOverviewMetrics().props('dashboardsPath')).toBeNull();
     });
diff --git a/ee/spec/frontend/analytics/cycle_analytics/components/value_stream_empty_state_spec.js b/ee/spec/frontend/analytics/cycle_analytics/components/value_stream_empty_state_spec.js
index 5942a768513c31b0dfbad46637e714ba0f8ddb0a..0f558ba534758904463161c498bcb40aba3d86da 100644
--- a/ee/spec/frontend/analytics/cycle_analytics/components/value_stream_empty_state_spec.js
+++ b/ee/spec/frontend/analytics/cycle_analytics/components/value_stream_empty_state_spec.js
@@ -1,4 +1,4 @@
-import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
+import { GlEmptyState } from '@gitlab/ui';
 import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 import ValueStreamEmptyState from 'ee/analytics/cycle_analytics/components/value_stream_empty_state.vue';
 import {
@@ -20,7 +20,6 @@ describe('ValueStreamEmptyState', () => {
     wrapper = shallowMountExtended(ValueStreamEmptyState, {
       propsData: {
         emptyStateSvgPath,
-        isLoading: false,
         hasDateRangeError: false,
         canEdit: true,
         ...props,
@@ -44,10 +43,6 @@ describe('ValueStreamEmptyState', () => {
       createComponent();
     });
 
-    it('does not render the loading icon', () => {
-      expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
-    });
-
     it('renders the empty state title message', () => {
       expect(findTitle()).toEqual(EMPTY_STATE_TITLE);
     });
@@ -89,20 +84,6 @@ describe('ValueStreamEmptyState', () => {
     });
   });
 
-  describe('isLoading = true', () => {
-    beforeEach(() => {
-      createComponent({
-        props: {
-          isLoading: true,
-        },
-      });
-    });
-
-    it('renders the loading icon', () => {
-      expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
-    });
-  });
-
   describe('hasDateRangeError = true', () => {
     beforeEach(() => {
       createComponent({