diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index 06fc00891c3fff81887e2ea012f1f29ce6553042..6a919f6024c1f109962b4c941bc7ff94ddedd6c4 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -208,7 +208,7 @@ export default {
   <form
     ref="form"
     :class="{ 'was-validated': wasValidated }"
-    class="gl-mt-3 gl-mb-3 needs-validation"
+    class="needs-validation"
     novalidate
     @submit.prevent.stop="onSubmit"
   >
@@ -264,14 +264,13 @@ export default {
       </p>
     </div>
 
-    <div v-if="!inModal" class="form-group" data-testid="action-buttons">
+    <div v-if="!inModal" class="gl-flex gl-gap-3" data-testid="action-buttons">
       <gl-button
         :loading="isSaving"
         type="submit"
         variant="confirm"
         category="primary"
         data-testid="add-badge-button"
-        class="gl-mr-3"
       >
         {{ saveText }}
       </gl-button>
diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue
index 45a3d7ba2b92b21460af68ce1f9be0a856a35bbf..7c307a5d280b084a53c65cef7cacdf263ed6c27e 100644
--- a/app/assets/javascripts/badges/components/badge_list.vue
+++ b/app/assets/javascripts/badges/components/badge_list.vue
@@ -31,6 +31,8 @@ export default {
     GlTooltip: GlTooltipDirective,
   },
   i18n: {
+    edit: __('Edit'),
+    delete: __('Delete'),
     emptyGroupMessage: s__('Badges|This group has no badges. Add an existing badge or create one.'),
     emptyProjectMessage: s__('Badges|This project has no badges. Start by adding a new badge.'),
   },
@@ -138,24 +140,26 @@ export default {
             data-testid="badge-actions"
           >
             <gl-button
+              v-gl-tooltip
               v-gl-modal.edit-badge-modal
               :disabled="item.isDeleting"
-              variant="default"
               category="tertiary"
               icon="pencil"
               size="medium"
-              :aria-label="__('Edit')"
+              :title="$options.i18n.edit"
+              :aria-label="$options.i18n.edit"
               data-testid="edit-badge-button"
               @click="editBadge(item)"
             />
             <gl-button
+              v-gl-tooltip
               v-gl-modal.delete-badge-modal
               :disabled="item.isDeleting"
               category="tertiary"
-              variant="danger"
               icon="remove"
               size="medium"
-              :aria-label="__('Delete')"
+              :title="$options.i18n.delete"
+              :aria-label="$options.i18n.delete"
               data-testid="delete-badge"
               @click="updateBadgeInModal(item)"
             />
diff --git a/app/assets/javascripts/badges/components/badge_settings.vue b/app/assets/javascripts/badges/components/badge_settings.vue
index 1892213a233d8f4ee64e23c3d3e644593795c4a9..ebf71d6a5af55ef6230388ee9f5658defc2b15fc 100644
--- a/app/assets/javascripts/badges/components/badge_settings.vue
+++ b/app/assets/javascripts/badges/components/badge_settings.vue
@@ -1,5 +1,5 @@
 <script>
-import { GlButton, GlModal, GlSprintf } from '@gitlab/ui';
+import { GlModal, GlSprintf } from '@gitlab/ui';
 // eslint-disable-next-line no-restricted-imports
 import { mapState, mapActions } from 'vuex';
 import { createAlert, VARIANT_INFO } from '~/alert';
@@ -12,13 +12,12 @@ import BadgeList from './badge_list.vue';
 export default {
   name: 'BadgeSettings',
   components: {
+    CrudComponent,
     Badge,
     BadgeForm,
     BadgeList,
-    GlButton,
     GlModal,
     GlSprintf,
-    CrudComponent,
   },
   i18n: {
     title: s__('Badges|Your badges'),
@@ -28,11 +27,6 @@ export default {
       'Badges|If you delete this badge, you %{strongStart}cannot%{strongEnd} restore it.',
     ),
   },
-  data() {
-    return {
-      addFormVisible: false,
-    };
-  },
   computed: {
     ...mapState(['badges', 'badgeInModal', 'isEditing']),
     saveProps() {
@@ -55,11 +49,8 @@ export default {
   },
   methods: {
     ...mapActions(['deleteBadge']),
-    showAddForm() {
-      this.addFormVisible = !this.addFormVisible;
-    },
     closeAddForm() {
-      this.addFormVisible = false;
+      this.$refs.badgesCrud.hideForm();
     },
     onSubmitEditModal() {
       this.$refs.editForm.onSubmit();
@@ -85,25 +76,19 @@ export default {
 
 <template>
   <crud-component
+    ref="badgesCrud"
     :title="$options.i18n.title"
     icon="labels"
     :count="badges.length"
-    class="badge-settings"
+    :toggle-text="$options.i18n.addFormTitle"
+    data-testid="badge-settings"
   >
-    <template #actions>
-      <gl-button
-        v-if="!addFormVisible"
-        size="small"
-        data-testid="show-badge-add-form"
-        @click="showAddForm"
-        >{{ $options.i18n.addButton }}</gl-button
-      >
-    </template>
-
-    <div v-if="addFormVisible" class="gl-new-card-add-form gl-m-5">
+    <template #form>
       <h4 class="gl-mt-0">{{ $options.i18n.addFormTitle }}</h4>
       <badge-form :is-editing="false" @close-add-form="closeAddForm" />
-    </div>
+    </template>
+
+    <badge-list />
 
     <gl-modal
       modal-id="edit-badge-modal"
@@ -136,6 +121,5 @@ export default {
         </gl-sprintf>
       </p>
     </gl-modal>
-    <badge-list />
   </crud-component>
 </template>
diff --git a/app/assets/javascripts/vue_shared/components/crud_component.vue b/app/assets/javascripts/vue_shared/components/crud_component.vue
index 22826c8582f868588340db1d7db83624f44c88b2..5028e804ea6f5af9553a0acf636114da86dc1b10 100644
--- a/app/assets/javascripts/vue_shared/components/crud_component.vue
+++ b/app/assets/javascripts/vue_shared/components/crud_component.vue
@@ -88,7 +88,7 @@ export default {
         </div>
         <div class="gl-flex gl-items-baseline gl-gap-3" data-testid="crud-actions">
           <gl-button
-            v-if="toggleText"
+            v-if="toggleText && !isFormVisible"
             size="small"
             data-testid="crud-form-toggle"
             @click="toggleForm"
diff --git a/qa/qa/page/component/badges.rb b/qa/qa/page/component/badges.rb
index 118e5072f0f1aa5464d3c0da52ea99af30ce394c..a97c3ae8d66f82c250ec7981fda105d0bd7e2861 100644
--- a/qa/qa/page/component/badges.rb
+++ b/qa/qa/page/component/badges.rb
@@ -4,10 +4,6 @@ module QA
   module Page
     module Component
       class Badges < Page::Base
-        view 'app/assets/javascripts/badges/components/badge_settings.vue' do
-          element 'show-badge-add-form'
-        end
-
         view 'app/assets/javascripts/badges/components/badge_form.vue' do
           element 'badge-name-field'
           element 'badge-link-url-field'
@@ -25,7 +21,7 @@ class Badges < Page::Base
         end
 
         def show_badge_add_form
-          click_element 'show-badge-add-form'
+          click_element 'crud-form-toggle'
         end
 
         def fill_name(name)
diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb
index 1ae61853748f896817258a3e82a1083924c7ff78..51a003e18315eeb4fc18d15271a0383cb9467ad2 100644
--- a/spec/features/groups/settings/group_badges_spec.rb
+++ b/spec/features/groups/settings/group_badges_spec.rb
@@ -21,10 +21,10 @@
   end
 
   it 'shows a list of badges', :js do
-    page.within '.badge-settings' do
+    within_testid('badge-settings') do
       wait_for_requests
 
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 2
       expect(rows[0]).to have_content badge_1.link_url
       expect(rows[1]).to have_content badge_2.link_url
@@ -33,8 +33,8 @@
 
   context 'adding a badge', :js do
     it 'user can preview a badge' do
-      click_button 'Add badge'
-      page.within '.badge-settings form' do
+      click_button 'Add new badge'
+      within_testid('crud-form') do
         fill_in 'badge-link-url', with: badge_link_url
         fill_in 'badge-image-url', with: badge_image_url
         within '#badge-preview' do
@@ -45,15 +45,15 @@
     end
 
     it do
-      click_button 'Add badge'
-      page.within '.badge-settings' do
+      click_button 'Add new badge'
+      within_testid('badge-settings') do
         fill_in 'badge-link-url', with: badge_link_url
         fill_in 'badge-image-url', with: badge_image_url
 
         click_button 'Add badge'
         wait_for_requests
 
-        within '.gl-card-body' do
+        within_testid('crud-body') do
           expect(find('a')[:href]).to eq badge_link_url
           expect(find('a img')[:src]).to eq badge_image_url
         end
@@ -63,9 +63,9 @@
 
   context 'editing a badge', :js do
     it 'form is shown when clicking edit button in list' do
-      page.within '.badge-settings' do
+      within_testid('badge-settings') do
         wait_for_requests
-        rows = all('.gl-card-body tbody tr')
+        rows = all('tbody tr')
         expect(rows.length).to eq 2
         rows[1].find('[aria-label="Edit"]').click
       end
@@ -77,9 +77,9 @@
     end
 
     it 'updates a badge when submitting the edit form' do
-      page.within '.badge-settings' do
+      within_testid('badge-settings') do
         wait_for_requests
-        rows = all('.gl-card-body tbody tr')
+        rows = all('tbody tr')
         expect(rows.length).to eq 2
         rows[1].find('[aria-label="Edit"]').click
       end
@@ -92,8 +92,8 @@
         wait_for_requests
       end
 
-      page.within '.badge-settings' do
-        rows = all('.gl-card-body tbody tr')
+      within_testid('badge-settings') do
+        rows = all('tbody tr')
         expect(rows.length).to eq 2
         expect(rows[1]).to have_content badge_link_url
       end
@@ -107,7 +107,7 @@ def click_delete_button(badge_row)
 
     it 'shows a modal when deleting a badge' do
       wait_for_requests
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 2
 
       click_delete_button(rows[1])
@@ -117,14 +117,14 @@ def click_delete_button(badge_row)
 
     it 'deletes a badge when confirming the modal' do
       wait_for_requests
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 2
       click_delete_button(rows[1])
 
       find('.modal .btn-danger').click
       wait_for_requests
 
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 1
       expect(rows[0]).to have_content badge_1.link_url
     end
diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb
index ee02f648dec2e69d0845bea3b16fc3bc2ef74e46..36fbdce40180353ae6b08d0f78a7e0b97a2bbaef 100644
--- a/spec/features/projects/settings/project_badges_spec.rb
+++ b/spec/features/projects/settings/project_badges_spec.rb
@@ -22,10 +22,10 @@
   end
 
   it 'shows a list of badges', :js do
-    page.within '.badge-settings' do
+    within_testid('badge-settings') do
       wait_for_requests
 
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 2
       expect(rows[0]).to have_content group_badge.link_url
       expect(rows[1]).to have_content project_badge.link_url
@@ -34,8 +34,8 @@
 
   context 'adding a badge', :js do
     it 'user can preview a badge' do
-      click_button 'Add badge'
-      page.within '.badge-settings form' do
+      click_button 'Add new badge'
+      within_testid('crud-form') do
         fill_in 'badge-link-url', with: badge_link_url
         fill_in 'badge-image-url', with: badge_image_url
         within '#badge-preview' do
@@ -46,15 +46,15 @@
     end
 
     it do
-      click_button 'Add badge'
-      page.within '.badge-settings' do
+      click_button 'Add new badge'
+      within_testid('badge-settings') do
         fill_in 'badge-link-url', with: badge_link_url
         fill_in 'badge-image-url', with: badge_image_url
 
         click_button 'Add badge'
         wait_for_requests
 
-        within '.gl-card-body' do
+        within_testid('crud-body') do
           expect(find('a')[:href]).to eq badge_link_url
           expect(find('a img')[:src]).to eq badge_image_url
         end
@@ -64,9 +64,9 @@
 
   context 'editing a badge', :js do
     it 'form is shown when clicking edit button in list' do
-      page.within '.badge-settings' do
+      within_testid('badge-settings') do
         wait_for_requests
-        rows = all('.gl-card-body tbody tr')
+        rows = all('tbody tr')
         expect(rows.length).to eq 2
         rows[1].find('[aria-label="Edit"]').click
       end
@@ -78,9 +78,9 @@
     end
 
     it 'updates a badge when submitting the edit form' do
-      page.within '.badge-settings' do
+      within_testid('badge-settings') do
         wait_for_requests
-        rows = all('.gl-card-body tbody tr')
+        rows = all('tbody tr')
         expect(rows.length).to eq 2
         rows[1].find('[aria-label="Edit"]').click
       end
@@ -93,8 +93,8 @@
         wait_for_requests
       end
 
-      page.within '.badge-settings' do
-        rows = all('.gl-card-body tbody tr')
+      within_testid('badge-settings') do
+        rows = all('tbody tr')
         expect(rows.length).to eq 2
         expect(rows[1]).to have_content badge_link_url
       end
@@ -108,7 +108,7 @@ def click_delete_button(badge_row)
 
     it 'shows a modal when deleting a badge' do
       wait_for_requests
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 2
 
       click_delete_button(rows[1])
@@ -118,14 +118,14 @@ def click_delete_button(badge_row)
 
     it 'deletes a badge when confirming the modal' do
       wait_for_requests
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 2
       click_delete_button(rows[1])
 
       find('.modal .btn-danger').click
       wait_for_requests
 
-      rows = all('.gl-card-body tbody tr')
+      rows = all('tbody tr')
       expect(rows.length).to eq 1
       expect(rows[0]).to have_content group_badge.link_url
     end
diff --git a/spec/frontend/badges/components/badge_settings_spec.js b/spec/frontend/badges/components/badge_settings_spec.js
index 5a904857f8912d9dc61dd596054fdfe6368ce842..e88cc09f6337a9bc0f4659ffdc144465d110b83b 100644
--- a/spec/frontend/badges/components/badge_settings_spec.js
+++ b/spec/frontend/badges/components/badge_settings_spec.js
@@ -1,8 +1,8 @@
-import { GlCard, GlTable } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlTable } from '@gitlab/ui';
 import Vue from 'vue';
 // eslint-disable-next-line no-restricted-imports
 import Vuex from 'vuex';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 import CrudComponent from '~/vue_shared/components/crud_component.vue';
 import BadgeSettings from '~/badges/components/badge_settings.vue';
 import BadgeList from '~/badges/components/badge_list.vue';
@@ -21,10 +21,10 @@ describe('BadgeSettings component', () => {
     store.state.kind = 'project';
     store.state.isEditing = isEditing;
 
-    wrapper = shallowMount(BadgeSettings, {
+    wrapper = shallowMountExtended(BadgeSettings, {
       store,
       stubs: {
-        GlCard,
+        CrudComponent,
         GlTable,
         'badge-list': BadgeList,
         'badge-form': BadgeForm,
@@ -38,11 +38,11 @@ describe('BadgeSettings component', () => {
 
   it('renders a header with the badge count', () => {
     createComponent();
-    const findCrudComponent = () => wrapper.findComponent(CrudComponent);
+    const cardTitle = wrapper.findByTestId('crud-title');
+    const cardCount = wrapper.findByTestId('crud-count');
 
-    expect(findCrudComponent().props('title')).toBe('Your badges');
-    expect(findCrudComponent().props('icon')).toBe('labels');
-    expect(findCrudComponent().props('count')).toBe(1);
+    expect(cardTitle.text()).toContain('Your badges');
+    expect(cardCount.text()).toContain('1');
   });
 
   it('displays a table', () => {
diff --git a/spec/frontend/vue_shared/components/crud_component_spec.js b/spec/frontend/vue_shared/components/crud_component_spec.js
index 1d6eed62d6d5c57ecd93f60f97cceb49712f39a4..3e052ffcd668f406912aa05741d6db323016c0af 100644
--- a/spec/frontend/vue_shared/components/crud_component_spec.js
+++ b/spec/frontend/vue_shared/components/crud_component_spec.js
@@ -77,6 +77,7 @@ describe('CRUD Component', () => {
     findFormToggle().vm.$emit('click');
     await nextTick();
 
+    expect(findFormToggle().exists()).toBe(false);
     expect(findForm().text()).toBe('Form slot');
   });