diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index e72d5b4106..9ece815488 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -57,6 +57,7 @@ Changelog
* Maintenance: Remove unused `docs/autobuild.sh` script (Sævar Öfjörð Magnússon)
* Maintenance: Replace `urlparse` with `urlsplit` to improve performance (Jake Howard)
* Maintenance: Optimise embed finder lookups (Jake Howard)
+ * Maintenance: Improve performance of initial admin loading by moving sprite hashing out of module import time (Jake Howard)
6.1.2 (30.05.2024)
diff --git a/docs/releases/6.2.md b/docs/releases/6.2.md
index 9dfcfd37de..7e55794ec8 100644
--- a/docs/releases/6.2.md
+++ b/docs/releases/6.2.md
@@ -81,6 +81,7 @@ This feature was implemented by Albina Starykova, with support from the Wagtail
* Remove unused `docs/autobuild.sh` script (Sævar Öfjörð Magnússon)
* Replace `urlparse` with `urlsplit` to improve performance (Jake Howard)
* Optimise embed finder lookups (Jake Howard)
+ * Improve performance of initial admin loading by moving sprite hashing out of module import time (Jake Howard)
## Upgrade considerations - changes affecting all projects
diff --git a/wagtail/admin/icons.py b/wagtail/admin/icons.py
new file mode 100644
index 0000000000..1426eaa875
--- /dev/null
+++ b/wagtail/admin/icons.py
@@ -0,0 +1,43 @@
+import hashlib
+import itertools
+import re
+from functools import lru_cache
+
+from django.conf import settings
+from django.template.loader import render_to_string
+from django.urls import reverse
+
+from wagtail import hooks
+
+icon_comment_pattern = re.compile(r"")
+
+
+@lru_cache(maxsize=None)
+def get_icons():
+ icon_hooks = hooks.get_hooks("register_icons")
+ all_icons = sorted(itertools.chain.from_iterable(hook([]) for hook in icon_hooks))
+ combined_icon_markup = ""
+ for icon in all_icons:
+ symbol = (
+ render_to_string(icon)
+ .replace('xmlns="http://www.w3.org/2000/svg"', "")
+ .replace("svg", "symbol")
+ )
+ symbol = icon_comment_pattern.sub("", symbol)
+ combined_icon_markup += symbol
+
+ return render_to_string(
+ "wagtailadmin/shared/icons.html", {"icons": combined_icon_markup}
+ )
+
+
+@lru_cache(maxsize=None)
+def get_icon_sprite_hash():
+ # SECRET_KEY is used to prevent exposing the Wagtail version
+ return hashlib.sha1(
+ (get_icons() + settings.SECRET_KEY).encode("utf-8")
+ ).hexdigest()[:8]
+
+
+def get_icon_sprite_url():
+ return reverse("wagtailadmin_sprite") + f"?h={get_icon_sprite_hash()}"
diff --git a/wagtail/admin/templates/wagtailadmin/skeleton.html b/wagtail/admin/templates/wagtailadmin/skeleton.html
index 2072c2d36d..16410daefd 100644
--- a/wagtail/admin/templates/wagtailadmin/skeleton.html
+++ b/wagtail/admin/templates/wagtailadmin/skeleton.html
@@ -19,7 +19,7 @@
-
+