Perf
Free Claude Code audit toolkit: accessibility, performance, SEO, privacy, design, deploy safety, codebase audits. 14 slash commands, MIT licensed.
npx -y skills add Surfrrosa/claude-skills --skill perfAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Audit performance — bundle size, image weight, render-blocking resources, and Core Web Vitals via PageSpeed
SKILL.md
13.8 KB, as published. Nobody here has run it
Performance Audit
Analyze a project for performance problems that kill load times and conversions. Two modes: source analysis (what you can catch from code) and live analysis (what Google PageSpeed Insights sees).
Sample output
A successful run produces something like:
## Performance Audit — marketing-site
Stack: Next.js 16
### Images (3.4MB total)
- public/hero.png — 1.8MB PNG, should be WebP (~250KB)
- public/team-photo.jpg — 920KB, unresponsive (no srcset)
- 6 images below the fold loading eagerly (missing loading="lazy")
### JavaScript (480KB bundled)
- src/components/Analytics.tsx — loads PostHog synchronously in <head>; should be async or deferred
- 3 client components could be server components (no hooks, no event handlers)
### Fonts
- Google Fonts <link> missing &display=swap — invisible text during font load
### Caching & Compression
- public/_headers missing — no long cache on static assets
### Summary
- Critical: 2 (hero image weight, synchronous analytics)
- Warning: 3
- Top priority: convert hero.png to WebP (saves ~1.5MB on first load)
Arguments
The user may specify:
- No args: audit current working directory (source only)
- A project name or path (e.g.,
myapp,marketing-site) --live: also hit PageSpeed Insights API for Core Web Vitals on the live URL--fix: auto-fix safe categoriesall --live: audit all web projects against their live URLs
Parse the argument string. Examples:
/perf-- source audit on current project/perf myapp --live-- source + PageSpeed for myapp/perf all --live-- PageSpeed for all sites (skip source, just compare live scores)/perf myapp --fix-- source audit + auto-fix for myapp
Project Registry
| Alias | Path | Live URL | Stack |
|---|---|---|---|
myapp | (add your projects here) | (add your projects here) | (add your projects here) |
Source Audit
Step 0: Detect the project
- Resolve path from alias or cwd
- Detect stack: Next.js, Astro, Flask/Jinja, static HTML
- Read
CLAUDE.mdand build config (next.config.*,astro.config.*,vite.config.*) for relevant settings
Step 1: Image Weight
Images are the #1 cause of slow pages for most sites. Check thoroughly.
1a. Image formats
Search public/, static/, src/assets/, src/images/, and any other asset directories for image files.
- Flag unoptimized formats:
.pngand.jpg/.jpegfiles that should likely be.webpor.avif. Exceptions:- Favicons (
favicon.ico,favicon.png) -- these need broad format support - OG/social images -- some platforms don't support WebP in meta tags yet. Check on a case-by-case basis.
- SVGs are fine as-is
- Favicons (
- Flag oversized files: Any image over 200KB. Any image over 500KB is critical.
- Count total image weight: Sum all image file sizes in the project. Compare to a budget:
- Under 1MB total: good
- 1-3MB: warning
- Over 3MB: critical
1b. Image optimization pipeline
Check whether the project has an image optimization strategy:
| Stack | What to check |
|---|---|
| Next.js | Is next/image or <Image> used? Check for raw <img> tags in components. next/image auto-optimizes. |
| Astro | Is astro:assets or <Image> from astro:assets used? Check for raw <img> tags. |
| Flask/Jinja | No built-in optimization. Check for a build step (sharp, imagemin, squoosh) or CDN with transforms. |
| Static HTML | Check for raw <img> tags pointing to unoptimized files. |
Flag: Raw <img> tags pointing to large files when the framework provides an optimized alternative.
1c. Responsive images
- Check
<img>tags forsrcsetandsizesattributes (or framework equivalents that generate them). - Check for images with fixed
width/heightin pixels that are larger than needed on mobile. - Above-the-fold hero images should have
loading="eager"orfetchpriority="high". All other images should haveloading="lazy".
Flag: Large images without responsive variants, no lazy loading on below-fold images, no priority hints on hero images.
Step 2: JavaScript Weight
2a. Bundle analysis
For projects with a build step:
| Stack | How to check |
|---|---|
| Next.js | Look for next build output in .next/ or run the build and check the output summary. Check for @next/bundle-analyzer in dependencies. |
| Astro | Astro ships zero JS by default. Check for client:* directives -- each one adds a JS island. Count them. |
| Vite | Check dist/assets/ for JS file sizes after build. |
| Static HTML | Count and size all <script> tags. |
- Flag: Any single JS bundle over 200KB (gzipped). Any total JS payload over 500KB.
- Flag: Third-party scripts loaded synchronously in
<head>withoutasyncordefer.
2b. Third-party scripts
Search for external script URLs in templates/layouts:
- Analytics scripts (PostHog, Google Analytics, Plausible, etc.)
- Chat widgets, support tools
- Font loaders
- Ad scripts
- Social embeds
For each, check:
- Is it loaded with
asyncordefer? - Is it loaded from a CDN or self-hosted?
- Could it be loaded later (after interaction, after page load)?
Flag: Synchronous third-party scripts in <head>, more than 3 third-party scripts total, any third-party script without async/defer.
2c. Unused JavaScript
- For Next.js: check for
use clientcomponents that could be server components. Any component that doesn't use hooks, event handlers, or browser APIs doesn't needuse client. - For Astro: check for
client:loaddirectives that could beclient:idleorclient:visible(defer hydration until needed). - Search for large libraries imported but potentially underused:
moment(usedate-fns),lodash(use individual imports), full@mui/materialor@chakra-ui/reactimports.
Flag: Client components that could be server components, eager hydration that could be deferred, heavy libraries with lighter alternatives.
Step 3: CSS Weight
3a. CSS delivery
- Check for large CSS files loaded in
<head>(render-blocking). - Check for inline critical CSS in the
<head>with deferred loading of the full stylesheet. - For Tailwind projects: check if CSS purging is configured (it is by default in v3+, but verify in
tailwind.config.*).
Flag: CSS files over 50KB in <head> without critical CSS extraction, Tailwind without purge.
3b. Unused CSS
- For Tailwind: this is handled by purge. Just verify it's on.
- For vanilla CSS: check if the project has multiple stylesheets with overlapping selectors or dead selectors (hard to check exhaustively from source, but flag obviously large CSS files).
Step 4: Font Loading
Search for @font-face declarations, Google Fonts links, or font files in assets:
- Flag: Google Fonts loaded via
<link>in<head>withoutdisplay=swapparameter (blocks rendering while fonts load). - Flag: More than 3 font files/variants loaded (each is a separate network request).
- Flag: Font files over 100KB each (should be subset or use woff2).
- Check: Is
font-display: swapset in@font-facedeclarations? Missing = invisible text during load. - Good: Self-hosted woff2 fonts with
font-display: swapand preload hints.
Step 5: Caching and Compression
Check deploy/server config for:
- Cache headers: Check
vercel.json,_headers(Netlify),railway.json, server code, or.htaccessfor cache-control headers on static assets. Images, fonts, and hashed JS/CSS should have long cache (max-age=31536000, immutable). HTML should have short/no cache. - Compression: Most CDNs (Vercel, Netlify, Cloudflare) handle gzip/brotli automatically. For self-hosted (Railway/Flask): check for compression middleware (
flask-compress,gzipmiddleware, nginx config).
Flag: No compression for self-hosted projects, no cache headers for static assets.
Step 6: Stack-Specific Checks
Next.js
next/imageusage for all content imagesnext/fontfor font loading (auto-optimizes and self-hosts)next/scriptfor third-party scripts withstrategypropgenerateStaticParamsfor static generation of dynamic routes- ISR/SSG vs SSR: are pages that could be static being server-rendered on every request?
- Check for
dynamic = 'force-dynamic'orrevalidate = 0on pages that don't need it
Astro
- Minimal client directives (zero JS is the goal)
<Image>component for auto-optimization- Static output mode when possible
- Check
astro.config.mjsforcompressHTML: true
Flask/Jinja
- Static files served via CDN or with cache headers?
flask-compressor equivalent for gzip?- Template caching enabled in production?
- Are database queries in views optimized? (N+1 queries, missing indexes -- hard to check from source but flag obvious patterns like queries in loops)
Static HTML
- Files minified? (HTML, CSS, JS)
- Are assets fingerprinted/hashed for cache busting?
- CDN in front of hosting?
Live Audit (--live flag)
Use the Google PageSpeed Insights API via WebFetch:
https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url={URL}&category=performance&strategy=mobile
Also run with strategy=desktop for comparison.
Parse the response for:
Core Web Vitals
| Metric | Good | Needs Work | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 2.5-4s | > 4s |
| INP (Interaction to Next Paint) | < 200ms | 200-500ms | > 500ms |
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1-0.25 | > 0.25 |
Additional Metrics
- FCP (First Contentful Paint)
- Speed Index
- Total Blocking Time (TBT)
- Time to Interactive (TTI)
Opportunities
Parse the lighthouseResult.audits for failed audits and their savings estimates. The most common ones:
render-blocking-resources-- CSS/JS blocking first paintunused-javascript-- JS loaded but not executedunused-css-rules-- CSS loaded but not appliedmodern-image-formats-- images that should be WebP/AVIFefficiently-encode-images-- images that could be compressed furtherproperly-size-images-- images larger than their display sizeoffscreen-images-- below-fold images loaded eagerly
Output Format
Single Project (source)
## Performance Audit -- [project name]
**Stack:** [detected stack]
### Images ([total weight])
- [findings]
### JavaScript ([total weight])
- [findings]
### CSS
- [findings]
### Fonts
- [findings]
### Caching & Compression
- [findings]
### Stack-Specific
- [findings]
### Summary
- **Critical:** [count] -- [list]
- **Warning:** [count] -- [list]
- **Top priority:** [the single most impactful fix]
Single Project (source + live)
Same as above, plus:
### PageSpeed Scores
| | Mobile | Desktop |
|---|--------|---------|
| Performance | [score] | [score] |
| LCP | [value] | [value] |
| INP | [value] | [value] |
| CLS | [value] | [value] |
| FCP | [value] | [value] |
| TBT | [value] | [value] |
### Top Opportunities (from Lighthouse)
1. [opportunity] -- est. [savings]
2. [opportunity] -- est. [savings]
3. [opportunity] -- est. [savings]
All Projects (--live only)
## Performance Audit -- All Sites
| Site | Mobile | Desktop | LCP | CLS | INP | Top Issue |
|------|--------|---------|-----|-----|-----|-----------|
| myapp | 82 | 95 | 2.1s | 0.02 | 120ms | -- |
| myapp | 94 | 99 | 1.2s | 0.01 | 80ms | -- |
| ... | ... | ... | ... | ... | ... | ... |
Sort by mobile score ascending (worst first).
Fix Mode (--fix flag)
Safe to auto-fix:
- Add
loading="lazy"to below-fold<img>tags that are missing it - Add
fetchpriority="high"to above-fold hero images - Add
decoding="async"to images - Add
asyncordeferto synchronous<script>tags in<head>(unless they must be synchronous) - Add
font-display: swapto@font-facedeclarations missing it - Add
&display=swapto Google Fonts<link>URLs missing it - Replace
<img>with framework<Image>component where applicable and straightforward - Add
compressHTML: trueto Astro config if missing
Requires confirmation:
- Replacing image formats (PNG/JPG to WebP) -- needs build pipeline
- Adding
next/fontor self-hosted fonts -- changes font loading behavior - Changing
client:loadtoclient:visiblein Astro -- may break user expectations - Converting SSR routes to SSG -- changes data freshness model
Never auto-fix:
- Bundle splitting strategy
- Third-party script removal
- Image content resizing (need to know target dimensions)
- Caching policy changes (affects freshness)
- Database query optimization
Rules
- Images first. For most sites, image optimization gives the biggest performance win for the least effort. Always lead with image findings.
- Mobile matters more. PageSpeed mobile scores are what Google uses for ranking. Always show mobile prominently.
- Don't shame slow scores. A 60 on mobile for a dynamic Flask app with no CDN is expected. Context matters. Focus on what's fixable.
- Be specific about savings. "Optimize images" is useless. "Convert
hero.png(1.2MB) to WebP (~180KB) -- saves ~1MB on first load" is actionable. - Don't duplicate other skills. This is not
/seo(no meta tags), not/a11y(no accessibility), not/ship(no deployment checks). Strictly performance. - PageSpeed API is free but rate-limited. Don't hammer it. One request per URL per strategy is enough. If running
all, add a brief pause between requests. - Source analysis is always valuable. Even without
--live, you can catch most issues from the code. Live mode adds real numbers but the source audit is where fixes come from.
Skill Run Logging
After completing this skill, append a log entry to ~/.claude/skill-log.md under the Run Log table. New entries at the top (most recent first).
Format: | YYYY-MM-DD | /skill-name | target (details) | one-line result |