diff --git a/ee/app/assets/javascripts/pages/subscriptions/groups/new/index.js b/ee/app/assets/javascripts/pages/subscriptions/groups/new/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..593adc02f372d078b9f3ee0d43cb85ddc196e0f8
--- /dev/null
+++ b/ee/app/assets/javascripts/pages/subscriptions/groups/new/index.js
@@ -0,0 +1,3 @@
+import mountGroupCreationApp from 'ee/subscriptions/groups/new';
+
+mountGroupCreationApp();
diff --git a/ee/app/assets/javascripts/subscriptions/groups/new/components/subscription_group_selector.vue b/ee/app/assets/javascripts/subscriptions/groups/new/components/subscription_group_selector.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8e9c9b9b3203965057c5bf7209e5073350e157fa
--- /dev/null
+++ b/ee/app/assets/javascripts/subscriptions/groups/new/components/subscription_group_selector.vue
@@ -0,0 +1,178 @@
+<script>
+import {
+  GlAccordion,
+  GlAccordionItem,
+  GlButton,
+  GlCard,
+  GlCollapsibleListbox,
+  GlFormGroup,
+} from '@gitlab/ui';
+import * as Sentry from '~/sentry/sentry_browser_wrapper';
+import { __, s__, sprintf } from '~/locale';
+import { visitUrl } from '~/lib/utils/url_utility';
+
+export default {
+  name: 'SubscriptionGroupSelector',
+  components: {
+    GlAccordion,
+    GlAccordionItem,
+    GlButton,
+    GlCard,
+    GlCollapsibleListbox,
+    GlFormGroup,
+  },
+  props: {
+    eligibleGroups: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
+    plansData: {
+      type: Object,
+      required: true,
+    },
+    rootUrl: {
+      type: String,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      selectedGroupId: null,
+      isValidated: false,
+      isLoading: false,
+    };
+  },
+  computed: {
+    selectedGroup() {
+      return this.eligibleGroups.find((group) => group.id === this.selectedGroupId);
+    },
+    toggleText() {
+      return this.selectedGroup
+        ? this.selectedGroup.name
+        : this.$options.i18n.groupSelection.placeholder;
+    },
+    groupOptions() {
+      return this.eligibleGroups.map(({ id, name }) => ({ text: name, value: id }));
+    },
+    hasValidGroupSelection() {
+      return Boolean(this.selectedGroupId);
+    },
+    groupValidationError() {
+      if (!this.isValidated || this.hasValidGroupSelection) {
+        return null;
+      }
+      return this.$options.i18n.groupSelection.validationMessage;
+    },
+    planName() {
+      switch (this.plansData.code) {
+        case 'premium':
+          return s__('BillingPlans|Premium');
+        case 'ultimate':
+          return s__('BillingPlans|Ultimate');
+        default:
+          return this.plansData.name;
+      }
+    },
+    title() {
+      return sprintf(
+        s__('SubscriptionGroupsNew|Select a group for your %{planName} subscription'),
+        { planName: this.planName },
+      );
+    },
+  },
+  methods: {
+    handleGroupSelection(value) {
+      this.selectedGroupId = value;
+    },
+    continueWithSelection() {
+      this.isValidated = true;
+
+      if (!this.hasValidGroupSelection) {
+        return;
+      }
+
+      this.isLoading = true;
+      this.navigateToPurchaseFlow(this.selectedGroupId);
+    },
+    navigateToPurchaseFlow(groupId) {
+      // We should always have a purchase link available. In the unlikely scenario where
+      // we don't, we want to know about it, so let's report the error to Sentry
+      if (!this.plansData.purchase_link?.href) {
+        this.reportError(`Missing purchase link for plan ${JSON.stringify(this.plansData)}`);
+        return;
+      }
+
+      const purchaseLink = `${this.plansData.purchase_link.href}&gl_namespace_id=${groupId}`;
+      visitUrl(purchaseLink);
+    },
+    reportError(error) {
+      Sentry.captureException(error, {
+        tags: {
+          vue_component: this.$options.name,
+        },
+      });
+    },
+  },
+  i18n: {
+    groupSelection: {
+      placeholder: __('Select a group'),
+      label: __('Group'),
+      description: s__('Checkout|Your subscription will be applied to this group'),
+      validationMessage: s__('SubscriptionGroupsNew|Select a group for your subscription'),
+    },
+    accordion: {
+      title: s__(`SubscriptionGroupsNew|Why can't I find my group?`),
+      description: s__(
+        'SubscriptionGroupsNew|Your group will only be displayed in the list above if:',
+      ),
+      reasonOne: s__(`SubscriptionGroupsNew|You're assigned the Owner role of the group`),
+      reasonTwo: s__('SubscriptionGroupsNew|The group is a top-level group on a Free tier'),
+    },
+  },
+};
+</script>
+<template>
+  <div class="gl-flex gl-justify-center">
+    <div class="gl-max-w-88">
+      <h2>{{ title }}</h2>
+      <gl-card class="gl-max-w-62 gl-mx-auto gl-p-5 gl-mt-10">
+        <label class="gl-block gl-mb-1">{{ $options.i18n.groupSelection.label }}</label>
+        <span class="gl-text-secondary">{{ $options.i18n.groupSelection.description }}</span>
+        <gl-form-group
+          :state="!groupValidationError"
+          :invalid-feedback="groupValidationError"
+          data-testid="group-selector"
+        >
+          <gl-collapsible-listbox
+            v-model="selectedGroupId"
+            block
+            fluid-width
+            :items="groupOptions"
+            :toggle-text="toggleText"
+            category="secondary"
+            :variant="groupValidationError ? 'danger' : 'default'"
+            @select="handleGroupSelection"
+          />
+        </gl-form-group>
+        <gl-accordion :header-level="3">
+          <gl-accordion-item :title="$options.i18n.accordion.title">
+            {{ $options.i18n.accordion.description }}
+            <ul class="gl-mt-4">
+              <li>{{ $options.i18n.accordion.reasonOne }}</li>
+              <li>{{ $options.i18n.accordion.reasonTwo }}</li>
+            </ul>
+          </gl-accordion-item>
+        </gl-accordion>
+        <gl-button
+          class="gl-mt-5 gl-w-full"
+          category="primary"
+          variant="confirm"
+          :loading="isLoading"
+          @click="continueWithSelection"
+          >{{ __('Continue') }}</gl-button
+        >
+      </gl-card>
+    </div>
+  </div>
+</template>
diff --git a/ee/app/assets/javascripts/subscriptions/groups/new/index.js b/ee/app/assets/javascripts/subscriptions/groups/new/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..84886c853892ff7b6a8f23b17daa8dda733006b3
--- /dev/null
+++ b/ee/app/assets/javascripts/subscriptions/groups/new/index.js
@@ -0,0 +1,28 @@
+import Vue from 'vue';
+import App from './components/subscription_group_selector.vue';
+
+export default () => {
+  const el = document.getElementById('js-new-subscription-group');
+
+  if (!el) return null;
+
+  const { rootUrl } = el.dataset;
+  const plansData = JSON.parse(el.dataset.plansData);
+  const eligibleGroups = JSON.parse(el.dataset.eligibleGroups);
+
+  return new Vue({
+    el,
+    components: {
+      App,
+    },
+    render(createElement) {
+      return createElement(App, {
+        props: {
+          rootUrl,
+          plansData,
+          eligibleGroups,
+        },
+      });
+    },
+  });
+};
diff --git a/ee/app/views/subscriptions/groups/new.html.haml b/ee/app/views/subscriptions/groups/new.html.haml
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0348613be375f636b1bf26414e913225543b6d09 100644
--- a/ee/app/views/subscriptions/groups/new.html.haml
+++ b/ee/app/views/subscriptions/groups/new.html.haml
@@ -0,0 +1 @@
+#js-new-subscription-group{ data: { root_url: root_url, plans_data: @plan_data.to_json, eligible_groups: present_groups(@eligible_groups).to_json } }
diff --git a/ee/spec/frontend/subscriptions/groups/subscription_group_selector_spec.js b/ee/spec/frontend/subscriptions/groups/subscription_group_selector_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..0206ca5ead06babd03c0448c0c49f1ab410ba27e
--- /dev/null
+++ b/ee/spec/frontend/subscriptions/groups/subscription_group_selector_spec.js
@@ -0,0 +1,165 @@
+import { nextTick } from 'vue';
+import {
+  GlAccordion,
+  GlAccordionItem,
+  GlCollapsibleListbox,
+  GlButton,
+  GlFormGroup,
+} from '@gitlab/ui';
+import * as Sentry from '~/sentry/sentry_browser_wrapper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import Component from 'ee/subscriptions/groups/new/components/subscription_group_selector.vue';
+import { stubComponent } from 'helpers/stub_component';
+import { visitUrl } from '~/lib/utils/url_utility';
+
+jest.mock('~/sentry/sentry_browser_wrapper');
+jest.mock('~/lib/utils/url_utility', () => ({
+  ...jest.requireActual('~/lib/utils/url_utility'),
+  visitUrl: jest.fn(),
+}));
+
+describe('SubscriptionGroupSelector component', () => {
+  let wrapper;
+
+  const eligibleGroups = [
+    { id: 1, name: 'Group one' },
+    { id: 2, name: 'Group two' },
+    { id: 3, name: 'Group three' },
+    { id: 4, name: 'Group four' },
+  ];
+
+  const plansData = {
+    code: 'premium',
+    id: 'premium-plan-id',
+    purchase_link: { href: 'path/to/purchase?plan_id=premium-plan-id' },
+  };
+
+  const rootUrl = 'https://gitlab.com/';
+
+  const defaultPropsData = { eligibleGroups, plansData, rootUrl };
+
+  const findAccordion = () => wrapper.findComponent(GlAccordion);
+  const findAccordionItem = () => wrapper.findComponent(GlAccordionItem);
+  const findCollapsibleListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+  const findGroupSelectionFormGroup = () => wrapper.findByTestId('group-selector');
+  const findContinueButton = () => wrapper.findComponent(GlButton);
+  const findHeader = () => wrapper.find('h2');
+
+  const createComponent = (propsData = {}) => {
+    wrapper = shallowMountExtended(Component, {
+      propsData: {
+        ...defaultPropsData,
+        ...propsData,
+      },
+      stubs: {
+        GlFormGroup: stubComponent(GlFormGroup, {
+          props: ['state', 'invalidFeedback'],
+        }),
+      },
+    });
+  };
+
+  beforeEach(() => {
+    createComponent();
+  });
+
+  describe('title', () => {
+    it('renders title correctly for premium plan', () => {
+      expect(findHeader().text()).toBe(`Select a group for your Premium subscription`);
+    });
+
+    it('renders title correctly for ultimate plan', () => {
+      createComponent({ plansData: { ...plansData, code: 'ultimate' } });
+
+      expect(findHeader().text()).toBe(`Select a group for your Ultimate subscription`);
+    });
+
+    it('renders title correctly for other plans', () => {
+      createComponent({ plansData: { ...plansData, code: 'non-premium', name: 'SaaS' } });
+
+      expect(findHeader().text()).toBe(`Select a group for your SaaS subscription`);
+    });
+  });
+
+  describe('group selection', () => {
+    it('renders collapsible list box with correct options', () => {
+      const expectedResult = eligibleGroups.map(({ id, name }) => ({ value: id, text: name }));
+
+      expect(findCollapsibleListbox().props().items).toEqual(expectedResult);
+    });
+
+    it('renders collapsible list box with correct variant', () => {
+      expect(findCollapsibleListbox().props('variant')).toBe('default');
+    });
+
+    it('does not show validation message on initial render', () => {
+      expect(findGroupSelectionFormGroup().props('state')).toBe(true);
+    });
+
+    it('shows validation message when no group is selected', async () => {
+      findContinueButton().vm.$emit('click');
+
+      await nextTick();
+
+      expect(findGroupSelectionFormGroup().props('state')).toBe(false);
+      expect(findGroupSelectionFormGroup().props('invalidFeedback')).toBe(
+        'Select a group for your subscription',
+      );
+      expect(findCollapsibleListbox().props('variant')).toBe('danger');
+    });
+
+    it('does not redirect when no group is selected', async () => {
+      findContinueButton().vm.$emit('click');
+
+      await nextTick();
+
+      expect(visitUrl).not.toHaveBeenCalled();
+    });
+
+    it('redirects to purchase flow when a valid group is selected', async () => {
+      const selectedGroupId = eligibleGroups[2].id;
+      const expectedUrl = `${plansData.purchase_link.href}&gl_namespace_id=${selectedGroupId}`;
+
+      findCollapsibleListbox().vm.$emit('select', selectedGroupId);
+      findContinueButton().vm.$emit('click');
+
+      await nextTick();
+
+      expect(visitUrl).toHaveBeenCalledWith(expectedUrl);
+    });
+
+    it('reports an error when no purchase link URL is provided', async () => {
+      const plansDataProp = { ...plansData, purchase_link: null };
+      const error = `Missing purchase link for plan ${JSON.stringify(plansDataProp)}`;
+
+      createComponent({ plansData: plansDataProp });
+
+      findCollapsibleListbox().vm.$emit('select', eligibleGroups[2].id);
+      findContinueButton().vm.$emit('click');
+
+      await nextTick();
+
+      expect(visitUrl).not.toHaveBeenCalled();
+      expect(Sentry.captureException).toHaveBeenCalledWith(error, {
+        tags: { vue_component: 'SubscriptionGroupSelector' },
+      });
+    });
+  });
+
+  describe('accordion', () => {
+    it('renders accordion', () => {
+      expect(findAccordion().props('headerLevel')).toBe(3);
+    });
+
+    it('renders accordion item', () => {
+      const accordionItem = findAccordionItem();
+
+      expect(accordionItem.props('title')).toBe(`Why can't I find my group?`);
+      expect(accordionItem.text()).toContain(
+        `Your group will only be displayed in the list above if:`,
+      );
+      expect(accordionItem.text()).toContain(`You're assigned the Owner role of the group`);
+      expect(accordionItem.text()).toContain(`The group is a top-level group on a Free tier`);
+    });
+  });
+});
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5714b73b1c1d8350a662d6cb99fd146d63b9034a..a1e410d06fef01305f452165e0b7cae877293a5e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -51719,6 +51719,24 @@ msgstr ""
 msgid "SubscriptionBanner|Upload new license"
 msgstr ""
 
+msgid "SubscriptionGroupsNew|Select a group for your %{planName} subscription"
+msgstr ""
+
+msgid "SubscriptionGroupsNew|Select a group for your subscription"
+msgstr ""
+
+msgid "SubscriptionGroupsNew|The group is a top-level group on a Free tier"
+msgstr ""
+
+msgid "SubscriptionGroupsNew|Why can't I find my group?"
+msgstr ""
+
+msgid "SubscriptionGroupsNew|You're assigned the Owner role of the group"
+msgstr ""
+
+msgid "SubscriptionGroupsNew|Your group will only be displayed in the list above if:"
+msgstr ""
+
 msgid "SubscriptionMangement|If you'd like to add more seats, upgrade your plan, or purchase additional products, contact your GitLab sales representative."
 msgstr ""