diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb
index 65501b2745176a587dd28e4491aff1940998a36d..e780b13de6eb0a1a931694eca4e1e78043d2fb0e 100644
--- a/app/views/pwa/manifest.json.erb
+++ b/app/views/pwa/manifest.json.erb
@@ -1,3 +1,4 @@
+<%- cache current_appearance do %>
 {
   "name": "<%= appearance_pwa_name %>",
   "short_name": "<%= appearance_pwa_short_name %>",
@@ -33,3 +34,4 @@
   }
   <% end -%>]
 }
+<% end %>
diff --git a/spec/views/pwa/manifest.json.erb_spec.rb b/spec/views/pwa/manifest.json.erb_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a5075bfe6fe1b3fc2c71fbd82cc166606100dd05
--- /dev/null
+++ b/spec/views/pwa/manifest.json.erb_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'pwa/manifest', feature_category: :navigation do
+  describe 'view caching', :use_clean_rails_memory_store_fragment_caching do
+    let(:appearance) { build_stubbed(:appearance, pwa_name: 'My GitLab') }
+
+    context 'when appearance is unchanged' do
+      it 'reuses the cached view' do
+        allow(view).to receive(:current_appearance).and_return(appearance)
+        allow(view).to receive(:appearance_pwa_name).and_call_original
+        render
+        render
+
+        expect(view).to have_received(:appearance_pwa_name).once
+      end
+    end
+
+    context 'when appearance has changed' do
+      let(:changed_appearance) { build_stubbed(:appearance, pwa_name: 'My new GitLab') }
+
+      it 'does not use the cached view' do
+        allow(view).to receive(:current_appearance).and_return(appearance)
+        allow(view).to receive(:appearance_pwa_name).and_call_original
+        render
+
+        allow(view).to receive(:current_appearance).and_return(changed_appearance)
+        render
+
+        expect(view).to have_received(:appearance_pwa_name).twice
+        expect(rendered).to have_content 'My new GitLab'
+      end
+    end
+  end
+end