diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml
index ffd8bc3de27e179fc3a05c010c0f8d1549c28cb2..2bc977feb24cae3bf123bf347e0f2ae25cf7e63b 100644
--- a/app/views/profiles/gpg_keys/_form.html.haml
+++ b/app/views/profiles/gpg_keys/_form.html.haml
@@ -8,3 +8,5 @@
 
     .gl-mt-3
       = f.submit s_('Profiles|Add key'), pajamas_button: true
+      = render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-2 js-toggle-button' }) do
+        = _('Cancel')
diff --git a/app/views/profiles/gpg_keys/_key.html.haml b/app/views/profiles/gpg_keys/_key.html.haml
index d8b8dda29dce51673ca0e36fedb972314efbddae..f8520cb430dab983d056578d58c1231908248bdd 100644
--- a/app/views/profiles/gpg_keys/_key.html.haml
+++ b/app/views/profiles/gpg_keys/_key.html.haml
@@ -1,24 +1,29 @@
-%li.key-list-item
-  .float-left.gl-mr-3
-    = sprite_icon('key', css_class: "settings-list-icon d-none d-sm-block gl-mt-4")
-  .key-list-item-info
+%tr.key-list-item
+  %td{ data: { label: s_('Profiles|Key') } }
+    %div{ class: 'gl-display-flex! gl-pl-0!' }
+      = sprite_icon('key', css_class: "settings-list-icon d-none d-sm-inline gl-mr-2")
+      .gl-display-flex.gl-flex-direction-column.gl-text-truncate
+        %p.gl-text-truncate.gl-m-0
+          %code= key.fingerprint
+        - if key.subkeys.present?
+          .subkeys.gl-mt-3{ class: 'gl-text-left!' }
+            %span.gl-font-sm
+              = _('Subkeys:')
+            %ul.subkeys-list
+              - key.subkeys.each do |subkey|
+                %li
+                  %p.gl-text-truncate.gl-m-0
+                    %code= subkey.fingerprint
+
+  %td{ data: { label: _('Status') } }
     - key.emails_with_verified_status.map do |email, verified|
-      = render partial: 'shared/email_with_badge', locals: { email: email, verified: verified }
+      %div{ class: 'gl-text-left!' }
+        = render partial: 'shared/email_with_badge', locals: { email: email, verified: verified }
+
+  %td{ data: { label: _('Created') } }
+    = html_escape(s_('Created %{time_ago}')) % { time_ago: time_ago_with_tooltip(key.created_at) }
 
-    %span.text-truncate
-      %code= key.fingerprint
-    - if key.subkeys.present?
-      .subkeys
-        %span.bold
-          = _('Subkeys')
-          = ':'
-        %ul.subkeys-list
-          - key.subkeys.each do |subkey|
-            %li
-              %code= subkey.fingerprint
-  .float-right
-    %span.key-created-at
-      = html_escape(s_('Profiles|Created %{time_ago}')) % { time_ago: time_ago_with_tooltip(key.created_at) }
-    = link_button_to nil, profile_gpg_key_path(key), data: { confirm: _('Are you sure? Removing this GPG key does not affect already signed commits.') }, method: :delete, class: 'gl-ml-3', variant: :danger, icon: 'remove', 'aria-label': _('Remove')
-    = link_button_to revoke_profile_gpg_key_path(key), data: { confirm: _('Are you sure? All commits that were signed with this GPG key will be unverified.') }, method: :put, class: 'gl-ml-3', variant: :danger, 'aria-label': _('Revoke') do
+  %td{ class: 'gl-py-3!', data: { label: _('Actions') } }
+    = link_button_to nil, profile_gpg_key_path(key), data: { confirm: _('Are you sure? Removing this GPG key does not affect already signed commits.'), confirm_btn_variant: 'danger' }, method: :delete, class: 'has-tooltip', icon: 'remove', category: :secondary, 'title': _('Remove'), 'aria-label': _('Remove')
+    = link_button_to revoke_profile_gpg_key_path(key), data: { confirm: _('Are you sure? All commits that were signed with this GPG key will be unverified.'), confirm_btn_variant: 'danger' }, method: :put, class: 'gl-ml-3', category: :secondary, variant: :danger, 'aria-label': _('Revoke') do
       = _('Revoke')
diff --git a/app/views/profiles/gpg_keys/_key_table.html.haml b/app/views/profiles/gpg_keys/_key_table.html.haml
index ebbd1c8f67233298b1ac8a36bb9356e7a7446031..0a50ce55b5031464c39f2346808e3297dc944d59 100644
--- a/app/views/profiles/gpg_keys/_key_table.html.haml
+++ b/app/views/profiles/gpg_keys/_key_table.html.haml
@@ -1,10 +1,19 @@
 - is_admin = local_assigns.fetch(:admin, false)
+- hide_class = local_assigns.fetch(:hide_class, false)
 
 - if @gpg_keys.any?
-  %ul.content-list
-    = render partial: 'profiles/gpg_keys/key', collection: @gpg_keys, locals: { is_admin: is_admin }
+  .table-holder
+    %table.table.b-table.gl-table.b-table-stacked-md.gl-mt-n1.gl-mb-n2.ssh-keys-list{ data: { qa_selector: 'ssh_keys_list' } }
+      %thead.d-none.d-md-table-header-group
+        %tr
+          %th= s_('Profiles|Key')
+          %th= _('Status')
+          %th= _('Created')
+          %th= _('Actions')
+      = render partial: 'profiles/gpg_keys/key', collection: @gpg_keys, locals: { is_admin: is_admin }
+
 - else
-  %p.settings-message.text-center
+  %p.gl-new-card-empty.gl-px-5.gl-py-4.js-toggle-content{ class: hide_class }
     - if is_admin
       = _('There are no GPG keys associated with this account.')
     - else
diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml
index 2dfd6c7860f8255c40a7addf68b116ea8943a637..7e38771d81d794fc67e485ada30f9b8fe3c0ce27 100644
--- a/app/views/profiles/gpg_keys/index.html.haml
+++ b/app/views/profiles/gpg_keys/index.html.haml
@@ -1,6 +1,8 @@
 - page_title _('GPG Keys')
 - add_page_specific_style 'page_bundles/profile'
 - @force_desktop_expanded_sidebar = true
+- add_form_class = 'gl-display-none' if !form_errors(@gpg_key)
+- hide_class = 'gl-display-none' if form_errors(@gpg_key)
 
 .settings-section.js-search-settings-section
   .settings-sticky-header
@@ -10,17 +12,24 @@
   %p.gl-text-secondary
     = _('GPG keys allow you to verify signed commits.')
 
-  %h5.gl-font-lg.gl-mt-0
-    = _('Add a GPG key')
-  %p
-    - help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/repository/gpg_signed_commits/index.md') }
-    = _('Add a GPG key for secure access to GitLab. %{help_link_start}Learn more%{help_link_end}.').html_safe % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
-  = render 'form'
+  = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
+    - c.with_header do
+      .gl-new-card-title-wrapper
+        %h3.gl-new-card-title
+          = _('Your GPG keys')
+        .gl-new-card-count
+          = sprite_icon('key', css_class: 'gl-mr-2')
+          = @gpg_keys.count
+      .gl-new-card-actions
+        = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content #{hide_class}" }) do
+          = _('Add a GPG key')
+    - c.with_body do
+      .gl-new-card-add-form.gl-m-3.js-toggle-content{ class: add_form_class }
+        %h4.gl-mt-0
+          = _('Add a GPG key')
+        %p
+          - help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/repository/gpg_signed_commits/index.md') }
+          = _('Add a GPG key for secure access to GitLab. %{help_link_start}Learn more%{help_link_end}.').html_safe % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
+        = render 'form'
 
-.settings-section.js-search-settings-section
-  .settings-sticky-header
-    .settings-sticky-header-inner
-      %h4.gl-my-0
-        = _('Your GPG keys (%{count})') % { count: @gpg_keys.count }
-  .gl-mb-3
-    = render 'key_table'
+      = render 'key_table', hide_class: hide_class
diff --git a/app/views/shared/_email_with_badge.html.haml b/app/views/shared/_email_with_badge.html.haml
index a1398f85513d9db75232ea3b5dc507d291893d7c..14201c0d23a5f39101d69eb3969b7b0e1b1f273c 100644
--- a/app/views/shared/_email_with_badge.html.haml
+++ b/app/views/shared/_email_with_badge.html.haml
@@ -1,6 +1,7 @@
 - variant = verified ? :success : :danger
 - text = verified ? _('Verified') : _('Unverified')
 
-%span.gl-mr-3
-  = email
+- if email
+  %span.gl-mr-2
+    = email
 = gl_badge_tag text, { variant: variant, size: :sm }
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d74a9ee1a75189b1461050fb76e7926c071e9ea1..910c33da5b8d3401acc25b88ee81fca6afb09403 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -13519,6 +13519,9 @@ msgstr ""
 msgid "Created %{timeAgo} by %{author}"
 msgstr ""
 
+msgid "Created %{time_ago}"
+msgstr ""
+
 msgid "Created %{timestamp}"
 msgstr ""
 
@@ -35415,9 +35418,6 @@ msgstr ""
 msgid "Profiles|Control emails linked to your account"
 msgstr ""
 
-msgid "Profiles|Created %{time_ago}"
-msgstr ""
-
 msgid "Profiles|Created%{time_ago}"
 msgstr ""
 
@@ -44774,7 +44774,7 @@ msgstr ""
 msgid "Subject Key Identifier:"
 msgstr ""
 
-msgid "Subkeys"
+msgid "Subkeys:"
 msgstr ""
 
 msgid "Submit"
@@ -53549,7 +53549,7 @@ msgstr ""
 msgid "Your Free top-level group, %{group_name}, has more than %{free_users_limit} users and uses more than %{free_storage_limit} of data. After usage limits are applied to Free top-level groups, projects in this group will be in a %{read_only_link_start}read-only state%{link_end}. You should reduce the number of users or upgrade to a paid tier %{strong_start}before%{strong_end} you manage your storage usage. Otherwise, your Free top-level group will become read-only immediately because the 5-user limit applies. For more information, see our %{faq_link_start}FAQ%{link_end}."
 msgstr ""
 
-msgid "Your GPG keys (%{count})"
+msgid "Your GPG keys"
 msgstr ""
 
 msgid "Your GitLab Ultimate free trial lasts for 30 days. After this period, you can maintain a GitLab Free account forever or upgrade to a paid plan."
diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb
index f39d9ddaf5615448157cce3bc3754da544afb000..3adda251e40f6f977d0ecdbe5e9241510e8fbd65 100644
--- a/spec/features/profiles/gpg_keys_spec.rb
+++ b/spec/features/profiles/gpg_keys_spec.rb
@@ -15,6 +15,7 @@
     end
 
     it 'saves the new key' do
+      click_button('Add a GPG key')
       fill_in('Key', with: GpgHelpers::User2.public_key)
       click_button('Add key')
 
@@ -24,6 +25,7 @@
     end
 
     it 'with multiple subkeys' do
+      click_button('Add a GPG key')
       fill_in('Key', with: GpgHelpers::User3.public_key)
       click_button('Add key')
 
@@ -52,7 +54,10 @@
 
     click_link('Remove')
 
-    expect(page).to have_content('Your GPG keys (0)')
+    expect(page).to have_content('Your GPG keys')
+    page.within('.gl-new-card-count') do
+      expect(page).to have_content('0')
+    end
   end
 
   it 'user revokes a key via the key index' do
@@ -63,7 +68,10 @@
 
     click_link('Revoke')
 
-    expect(page).to have_content('Your GPG keys (0)')
+    expect(page).to have_content('Your GPG keys')
+    page.within('.gl-new-card-count') do
+      expect(page).to have_content('0')
+    end
 
     expect(gpg_signature.reload).to have_attributes(
       verification_status: 'unknown_key',