perf-asset-optimisation
Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V. ✓ Copied. Switch to Claude and paste — ⌘/Ctrl+V.
Asset Optimisation Review
Audit images, video, fonts, and static assets for format, compression, sizing, and delivery pipeline gaps. Every unoptimised asset on the LCP critical path can cost 0.5–2 s.
Usage
/perf-asset-optimisation <URL or project-root-path> [--platform sitecore|optimizely|umbraco|headless]
Phase 1 — Asset Inventory
URL mode
From the Lighthouse network-requests audit, filter assets by type:
images: .jpg .jpeg .png .gif .webp .avif .svg
video: .mp4 .webm .ogv
fonts: .woff .woff2 .ttf .eot
scripts: .js .mjs
styles: .css
other: .json .xml data URIs
For each image: record URL, transferred size, decoded size, Content-Type, and cache headers.
Source mode
Scan project for image references:
grep -r "src=" --include="*.tsx" --include="*.jsx" --include="*.html" --include="*.cshtml" \
--include="*.astro" . | grep -E "\.(jpg|jpeg|png|gif|webp|avif|svg)"
Phase 2 — Image Audit
2.1 Format selection
| Current format | Replace with | Typical savings | When NOT to convert |
|---|---|---|---|
| JPEG (photos) | WebP | 25–35% | IE11 (if still supported — check requirements) |
| JPEG (photos, high quality) | AVIF | 40–50% | Older iOS (< 16) — check analytics |
| PNG (transparency) | WebP | 20–35% | Complex transparency on old browsers |
| PNG (opaque, photo) | WebP or JPEG | 30–50% | Never — always prefer compressed format |
| GIF (animated) | <video autoplay loop muted playsinline> |
60–80% | N/A |
| SVG | Keep SVG | N/A | SVGs are already vector/optimised |
2.2 Responsive images
Every image that renders at different sizes across breakpoints should use srcset + sizes:
<!-- Missing responsive variants — serves 1200 px image to a 375 px screen -->
<img src="/hero.webp" alt="Hero">
<!-- Correct: browser picks the best-fit variant -->
<img
src="/hero-800.webp"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w, /hero-1920.webp 1920w"
sizes="(max-width: 768px) 100vw, (max-width: 1280px) 50vw, 1200px"
alt="Hero"
width="1200"
height="600"
>
Flag images where Lighthouse uses-responsive-images shows potential savings > 50 KB.
2.3 Lazy loading
All images below the fold must have loading="lazy". All LCP candidate images must NOT
have loading="lazy" (it delays the LCP resource discovery):
<!-- Hero / LCP candidate — eager load + high priority -->
<img src="/hero.webp" loading="eager" fetchpriority="high" width="1440" height="680" alt="Hero">
<!-- Below-fold images — lazy load -->
<img src="/card-thumb.webp" loading="lazy" width="400" height="300" alt="Article thumbnail">
2.4 CLS-safe sizing
Every <img> and <video> must have explicit width and height attributes OR
aspect-ratio CSS. Missing dimensions = CLS risk.
<!-- Missing dimensions = CLS -->
<img src="/product.webp" alt="Product">
<!-- Explicit dimensions = browser reserves space before image loads -->
<img src="/product.webp" alt="Product" width="600" height="400">
<!-- Or CSS aspect-ratio (useful for fluid/responsive images) -->
<style>
.product-img { aspect-ratio: 3 / 2; width: 100%; }
</style>
<img class="product-img" src="/product.webp" alt="Product">
2.5 Platform-specific image delivery
Sitecore XM Cloud / Media Library
- Is the Media Library CDN URL configured? (
/~/media/should resolve to a CDN edge URL, not origin.) - Check if Sitecore JSS is using
next/imagewithsitecorecloud.ioinremotePatterns. - For media items: are
WidthandHeightfields populated? Affects CLS and correctsrcsetgeneration. - SXA Media Handler: confirm WebP output is configured on the media handler pipeline.
Optimizely Content Cloud / DXP
- Is the
ImageResizer/EPiServer.ImageSharpGlobalpackage installed? - Are image URLs including format query params (
?format=webp&width=800)? - DXP CDN: are
/globalassets/and/contentassets/paths cached at the edge?
Umbraco
- Is
Umbraco.Cms.Imaging.ImageSharpconfigured with WebP output enabled?// appsettings.json "Umbraco": { "CMS": { "Imaging": { "Cache": { "BrowserMaxAge": "7.00:00:00" }, "Resize": { "MaxWidth": 5000, "MaxHeight": 5000 } } } } - Are ImageSharp crop presets defined for common breakpoints?
- Is the
/media/path covered by Azure CDN / Front Door caching rules?
Next.js (headless)
- Are all images going through
next/image? Bypassed images miss WebP negotiation and lazy load. - Is
images.formatsset to['image/avif', 'image/webp']innext.config.js? - Are
remotePatternslimited to known CMS domains (nothostname: '*')?
Phase 3 — Video Audit
3.1 Replace GIFs with video
<!-- Before: 2.4 MB GIF, blocking, no pause control -->
<img src="/animation.gif" alt="Animation">
<!-- After: 420 KB WebM + 560 KB MP4 fallback, hardware-decoded, no autoplay jank -->
<video autoplay loop muted playsinline width="800" height="450">
<source src="/animation.webm" type="video/webm">
<source src="/animation.mp4" type="video/mp4">
</video>
3.2 Video loading strategy
- Background/hero videos: use
preload="none"+ poster image to prevent bandwidth waste on mobile. - LCP video: use
preload="metadata"andposterto avoid CLS. - Never autoplay with sound (triggers browser blocking in Chrome/Firefox).
<video
preload="metadata"
poster="/hero-poster.webp"
autoplay loop muted playsinline
width="1440" height="810"
>
<source src="/hero.webm" type="video/webm">
<source src="/hero.mp4" type="video/mp4">
</video>
Phase 4 — Font Asset Audit
4.1 Font file inventory
List all font files loaded. For each:
- Format:
woff2preferred;woffacceptable;ttf/eot= outdated (flag) - Weights/variants loaded vs. weights/variants used — excess variants waste bandwidth
- Subset needed: if only Latin characters are used, a Latin subset saves 40–60% vs. full Unicode
4.2 System font fallback stack
For each custom font, check that the font-family stack includes a system fallback:
/* Without fallback — FOUT leaves unstyled text until font loads */
font-family: 'Inter', sans-serif;
/* With size-adjusted fallback — reduces CLS from FOUT reflow */
@font-face {
font-family: 'Inter-Fallback';
src: local('Arial');
size-adjust: 96%; /* tune to match your custom font's metrics */
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
font-family: 'Inter', 'Inter-Fallback', sans-serif;
Phase 5 — Static Asset Delivery Check
For scripts and stylesheets, verify:
Compression: Are responses served with
Content-Encoding: br(Brotli) orgzip?- Brotli gives ~15–20% better compression than gzip for text assets.
- Flag if large assets (> 50 KB) lack compression.
Cache headers: Are static assets served with long TTLs + fingerprinted filenames? See
perf-caching-reviewfor the full cache audit.HTTP/2 or HTTP/3: Is the origin serving over HTTP/2+ for multiplexed requests? HTTP/1.1 with many assets = connection queue bottleneck.
Phase 6 — Report
## Asset Optimisation Report
**URL / Path:** [target]
**Platform:** [detected/specified]
### Asset Summary
| Type | Count | Total transferred | Potential savings |
|---|---|---|---|
| Images | N | XX KB | XX KB |
| Video | N | XX KB | XX KB |
| Fonts | N | XX KB | XX KB |
| Scripts | N | XX KB | XX KB |
| Styles | N | XX KB | XX KB |
### Findings (ranked by impact)
[findings]
### Quick Wins
[list]
Quality Checklist
- LCP image identified;
fetchpriority="high"andloading="eager"checked - All below-fold images have
loading="lazy" - All images have
width+heightoraspect-ratio(CLS safety) - GIFs identified and flagged for video replacement
- Font format: all
woff2; nottf/eotin production - Platform media delivery pipeline configuration checked
- Brotli/gzip compression verified on at least the top 5 assets by size
Technical details
- Plugin
- performance-auditor
- Author
- Dakota Smith
- Skill name
perf-asset-optimisation- Maturity
- experimental
- Security review
- No security review
- Owners
-
- engineering
- Argument hint
<URL or project-root-path> [--platform sitecore|optimizely|umbraco|headless]- Allowed tools
-
ReadWriteGlobGrepBash(npx lighthouse *)Bash(node *)Bash(cat *)Bash(ls *)WebFetch(domain:localhost)WebFetch(domain:web.dev)
- Source
plugins/performance-auditor/skills/perf-asset-optimisation/SKILL.md
Edit this skill on GitHub — opens GitHub's web editor. No git knowledge needed.