diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue b/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue
index bfa46f54e771e84d773f06f4a8a5b275de69a9e6..a0ef1c02c30ec90790ad2710b2bd3aed8d2f4821 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue
+++ b/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue
@@ -218,8 +218,12 @@ export default {
         />
       </gl-form-group>
 
-      <gl-form-group class="col-lg-8" :label="$options.i18n.descriptionLabel">
-        <gl-form-textarea v-model="description" />
+      <gl-form-group
+        class="col-lg-8"
+        :label="$options.i18n.descriptionLabel"
+        label-for="description"
+      >
+        <gl-form-textarea id="description" v-model="description" />
       </gl-form-group>
     </div>
 
diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue b/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue
index 48e3f21864e56bf5bb8e6e2071f5eb9aceacea71..bba9d8ec60195c00b457171af7bb9fd8989408d0 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue
+++ b/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue
@@ -6,7 +6,6 @@ import { TABLE_FIELDS } from '../constants';
 import CustomRolesActions from './custom_roles_actions.vue';
 
 export default {
-  name: 'CustomRolesTable',
   components: {
     GlTable,
     CustomRolesActions,
@@ -33,6 +32,11 @@ export default {
       {{ $options.getIdFromGraphQLId(id) }}
     </template>
 
+    <template #cell(description)="{ item: { description } }">
+      <template v-if="description">{{ description }}</template>
+      <span v-else class="gl-text-gray-400">{{ s__('MemberRole|No description') }}</span>
+    </template>
+
     <template #cell(baseRole)="{ item: { baseAccessLevel } }">
       {{ getBaseRoleName(baseAccessLevel) }}
     </template>
diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue b/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue
index abd33aa31ec69880fa17b73f9b9932e1e5310a9e..22c61a34fc042ebba048bda0837ca82c481838d1 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue
+++ b/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue
@@ -19,8 +19,9 @@ import deleteMemberRoleMutation from '../graphql/delete_member_role.mutation.gra
 import CreateMemberRole from './create_member_role.vue';
 
 export const FIELDS = [
-  { key: 'name', label: s__('MemberRole|Name'), sortable: true },
   { key: 'id', label: s__('MemberRole|ID'), sortable: true },
+  { key: 'name', label: s__('MemberRole|Name'), sortable: true },
+  { key: 'description', label: s__('MemberRole|Description') },
   { key: 'baseAccessLevel', label: s__('MemberRole|Base role'), sortable: true },
   { key: 'permissions', label: s__('MemberRole|Permissions') },
   {
@@ -57,6 +58,7 @@ export default {
       'MemberRole|To delete custom role, remove role from all group members.',
     ),
     createSuccess: s__('MemberRole|Role successfully created.'),
+    noDescription: s__('MemberRole|No description'),
   },
   components: {
     CreateMemberRole,
@@ -104,10 +106,10 @@ export default {
 
         const memberRoles = nodes || [];
 
-        return memberRoles.map((member) => ({
+        return memberRoles.map(({ baseAccessLevel, enabledPermissions, ...member }) => ({
           ...member,
-          baseAccessLevel: ACCESS_LEVEL_LABELS[member.baseAccessLevel.integerValue],
-          permissions: member.enabledPermissions.nodes,
+          baseAccessLevel: ACCESS_LEVEL_LABELS[baseAccessLevel.integerValue],
+          permissions: enabledPermissions.nodes,
         }));
       },
       error() {
@@ -236,6 +238,10 @@ export default {
       <template #cell(id)="{ item }">
         {{ $options.getIdFromGraphQLId(item.id) }}
       </template>
+      <template #cell(description)="{ item: { description } }">
+        <template v-if="description">{{ description }}</template>
+        <span v-else class="gl-text-gray-400">{{ $options.i18n.noDescription }}</span>
+      </template>
       <template #cell(baseAccessLevel)="{ item: { baseAccessLevel } }">
         <gl-badge class="gl-my-n4">{{ baseAccessLevel }}</gl-badge>
       </template>
diff --git a/ee/spec/features/admin/member_roles_spec.rb b/ee/spec/features/admin/member_roles_spec.rb
index ab1afa28a7d80f1f26de29148ada43a623b1a0d9..633e898b67365fdeb08319fe9ba0d3eaca6d17e4 100644
--- a/ee/spec/features/admin/member_roles_spec.rb
+++ b/ee/spec/features/admin/member_roles_spec.rb
@@ -6,6 +6,7 @@
   let_it_be(:admin) { create(:admin) }
 
   let(:name) { 'My custom role' }
+  let(:description) { 'My role description' }
   let(:permissions) { { read_vulnerability: { name: 'read_vulnerability' } } }
   let(:permission) { :read_vulnerability }
   let(:permission_name) { permission.to_s.humanize }
@@ -15,18 +16,19 @@
     stub_licensed_features(custom_roles: true)
   end
 
-  def create_role(access_level, name, permissions)
+  def create_role(access_level, name, description, permissions)
     click_button 'New role'
     select access_level, from: 'Base role to use as template'
     fill_in 'Role name', with: name
+    fill_in 'Description', with: description
     permissions.each do |permission|
       page.check permission
     end
     click_button 'Create role'
   end
 
-  def created_role(name, id, access_level, permissions)
-    [name, id, access_level, *permissions].join(' ')
+  def created_role(id, name, description, access_level, permissions)
+    [id, name, description, access_level, *permissions].join(' ')
   end
 
   describe 'adding a new custom role', :enable_admin_mode do
@@ -38,14 +40,14 @@ def created_role(name, id, access_level, permissions)
 
     shared_examples 'creates a new custom role' do
       it 'and displays it' do
-        create_role(access_level, name, [permission_name])
+        create_role(access_level, name, description, [permission_name])
 
         created_member_role = MemberRole.permissions_where(permission => true)
           .find_by(name: name, base_access_level: Gitlab::Access.options[access_level])
 
         expect(created_member_role).not_to be_nil
 
-        role = created_role(name, created_member_role.id, access_level, [permission_name])
+        role = created_role(created_member_role.id, name, description, access_level, [permission_name])
         expect(page).to have_content(role)
       end
     end
diff --git a/ee/spec/features/groups/member_roles_spec.rb b/ee/spec/features/groups/member_roles_spec.rb
index e8a85f5c2fe5e96d43dea692160c4e5ecd2da415..636e229b66c93e78d74b79b3f2639a0a22cdbc52 100644
--- a/ee/spec/features/groups/member_roles_spec.rb
+++ b/ee/spec/features/groups/member_roles_spec.rb
@@ -7,6 +7,7 @@
   let_it_be(:group) { create(:group) }
 
   let(:name) { 'My custom role' }
+  let(:description) { 'My role description' }
   let(:permissions) { { read_vulnerability: { name: 'read_vulnerability' } } }
   let(:permission) { :read_vulnerability }
   let(:permission_name) { permission.to_s.humanize }
@@ -20,18 +21,19 @@
     stub_licensed_features(custom_roles: true)
   end
 
-  def create_role(access_level, name, permissions)
+  def create_role(access_level, name, description, permissions)
     click_button 'New role'
     select access_level, from: 'Base role to use as template'
     fill_in 'Role name', with: name
+    fill_in 'Description', with: description
     permissions.each do |permission|
       page.check permission
     end
     click_button 'Create role'
   end
 
-  def created_role(name, id, access_level, permissions)
-    [name, id, access_level, *permissions].join(' ')
+  def created_role(id, name, description, access_level, permissions)
+    [id, name, description, access_level, *permissions].join(' ')
   end
 
   describe 'adding a new custom role' do
@@ -43,14 +45,14 @@ def created_role(name, id, access_level, permissions)
 
     shared_examples 'creates a new custom role' do
       it 'and displays it' do
-        create_role(access_level, name, [permission_name])
+        create_role(access_level, name, description, [permission_name])
 
         created_member_role = MemberRole.permissions_where(permission => true)
           .find_by(name: name, base_access_level: Gitlab::Access.options[access_level])
 
         expect(created_member_role).not_to be_nil
 
-        role = created_role(name, created_member_role.id, access_level, [permission_name])
+        role = created_role(created_member_role.id, name, description, access_level, [permission_name])
         expect(page).to have_content(role)
       end
     end
@@ -88,7 +90,7 @@ def created_role(name, id, access_level, permissions)
         end
 
         it 'shows an error message' do
-          create_role(access_level, name, [permission_name])
+          create_role(access_level, name, description, [permission_name])
 
           expect(page).to have_content('Failed to create role')
         end
diff --git a/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js b/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js
index f0ba8e32fb89ebed3f114b9134103ae095055e55..38d2dab9b70e1bf126b201c358b4cee7c006984d 100644
--- a/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js
+++ b/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js
@@ -6,21 +6,18 @@ import { mockMemberRoles } from '../mock_data';
 describe('CustomRolesTable', () => {
   let wrapper;
 
-  const mockCustomRoles = mockMemberRoles.data.namespace.memberRoles.nodes;
-  const mockCustomRole = mockCustomRoles[0];
+  const customRoles = mockMemberRoles.data.namespace.memberRoles.nodes;
 
-  const createComponent = (props = {}) => {
+  const createComponent = () => {
     wrapper = mountExtended(CustomRolesTable, {
-      propsData: {
-        customRoles: mockCustomRoles,
-        ...props,
-      },
+      propsData: { customRoles },
     });
   };
 
   const findHeaders = () => wrapper.find('thead').find('tr').findAll('th');
-  const findCells = () => wrapper.findAllByRole('cell');
-  const findActions = () => wrapper.findAllComponents(CustomRolesActions).at(0);
+  const findRowCell = ({ row = 0, cell }) =>
+    wrapper.findAll('tbody tr').at(row).findAll('td').at(cell);
+  const findActions = () => wrapper.findComponent(CustomRolesActions);
 
   beforeEach(() => {
     createComponent();
@@ -38,35 +35,39 @@ describe('CustomRolesTable', () => {
     });
 
     it('renders the id', () => {
-      expect(findCells().at(0).text()).toContain('1');
+      expect(findRowCell({ cell: 0 }).text()).toContain('1');
     });
 
     it('renders the name', () => {
-      expect(findCells().at(1).text()).toContain('Test');
+      expect(findRowCell({ cell: 1 }).text()).toContain('Test');
     });
 
-    it('renders the description', () => {
-      expect(findCells().at(2).text()).toContain('Test description');
-    });
+    it.each`
+      row  | expectedDescription
+      ${0} | ${'Test description'}
+      ${1} | ${'No description'}
+    `(
+      'renders the description "$expectedDescription" for row $row',
+      ({ row, expectedDescription }) => {
+        expect(findRowCell({ row, cell: 2 }).text()).toBe(expectedDescription);
+      },
+    );
 
     it('renders the base access level', () => {
-      expect(findCells().at(3).text()).toContain('Reporter');
+      expect(findRowCell({ cell: 3 }).text()).toContain('Reporter');
     });
 
     it('renders the permissions', () => {
-      expect(findCells().at(4).text()).toContain('Read code');
-      expect(findCells().at(4).text()).toContain('Read vulnerability');
+      expect(findRowCell({ cell: 4 }).text()).toContain('Read code');
+      expect(findRowCell({ cell: 4 }).text()).toContain('Read vulnerability');
     });
 
     it('renders the member count', () => {
-      expect(findCells().at(5).text()).toContain('0');
+      expect(findRowCell({ cell: 5 }).text()).toContain('0');
     });
 
     it('renders the actions', () => {
       expect(findActions().exists()).toBe(true);
-
-      expect(findCells().at(6).text()).toContain('Edit role');
-      expect(findCells().at(6).text()).toContain('Delete role');
     });
   });
 
@@ -76,7 +77,7 @@ describe('CustomRolesTable', () => {
     });
 
     it('emits `delete-role` event', () => {
-      expect(wrapper.emitted('delete-role')).toEqual([[mockCustomRole]]);
+      expect(wrapper.emitted('delete-role')[0][0]).toBe(customRoles[0]);
     });
   });
 });
diff --git a/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js b/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js
index e317c1d4a00cb7d28c817923c9cef9e7761a4681..02121b868f0942d02d6e1a57cf17d40d61f70856 100644
--- a/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js
+++ b/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js
@@ -67,7 +67,8 @@ describe('ListMemberRoles', () => {
   const findModal = () => wrapper.findComponent(GlModal);
   const findTable = () => wrapper.findComponent(GlTable);
   const findCellByText = (text) => wrapper.findByRole('cell', { name: text });
-  const findCells = () => wrapper.findAllByRole('cell');
+  const findRowCell = ({ row = 0, cell }) =>
+    wrapper.findAll('tbody tr').at(row).findAll('td').at(cell);
 
   const expectSortableColumn = (fieldKey) => {
     const fields = findTable().props('fields');
@@ -225,8 +226,19 @@ describe('ListMemberRoles', () => {
       expectSortableColumn('baseAccessLevel');
     });
 
+    it.each`
+      row  | expectedDescription
+      ${0} | ${'Test description'}
+      ${1} | ${'No description'}
+    `(
+      'renders the description "$expectedDescription" for row $row',
+      ({ row, expectedDescription }) => {
+        expect(findRowCell({ row, cell: 2 }).text()).toBe(expectedDescription);
+      },
+    );
+
     it('shows list of permissions', () => {
-      const permissionsText = findCells().at(3).text();
+      const permissionsText = findRowCell({ cell: 4 }).text();
 
       expect(permissionsText).toContain('Read code');
       expect(permissionsText).toContain('Read vulnerability');
diff --git a/ee/spec/frontend/roles_and_permissions/mock_data.js b/ee/spec/frontend/roles_and_permissions/mock_data.js
index ea076dd4fab2bb86cc0600c321da1888f0e8c54f..12adc01c019920cde778e58d207335a1ea3cca82 100644
--- a/ee/spec/frontend/roles_and_permissions/mock_data.js
+++ b/ee/spec/frontend/roles_and_permissions/mock_data.js
@@ -63,7 +63,7 @@ export const mockMemberRoles = {
             },
             id: 'gid://gitlab/MemberRole/2',
             name: 'Test 2',
-            description: 'Test description',
+            description: '',
             membersCount: 1,
             enabledPermissions: {
               nodes: [
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6889995bf840f94fbb4c3a4a6c7832810a841a75..1ee6afaef416f9df6ebd8be81c4f8b35e4dfca64 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -31157,6 +31157,9 @@ msgstr ""
 msgid "MemberRole|No custom roles found"
 msgstr ""
 
+msgid "MemberRole|No description"
+msgstr ""
+
 msgid "MemberRole|Permissions"
 msgstr ""