> perf-lighthouse
Run Lighthouse audits locally via CLI or Node API, parse and interpret reports, and set performance budgets. Use when measuring site performance, understanding Lighthouse scores, setting up budgets, or integrating audits into CI. Triggers on: lighthouse, run lighthouse, lighthouse score, performance audit, performance budget. Do NOT use for fixing specific performance issues (use perf-web-optimization or core-web-vitals) or Astro-specific optimization (use perf-astro).
curl "https://skillshub.wtf/tech-leads-club/agent-skills/perf-lighthouse?format=md"Lighthouse Audits
CLI Quick Start
# Install
npm install -g lighthouse
# Basic audit
lighthouse https://example.com
# Mobile performance only (faster)
lighthouse https://example.com --preset=perf --form-factor=mobile
# Output JSON for parsing
lighthouse https://example.com --output=json --output-path=./report.json
# Output HTML report
lighthouse https://example.com --output=html --output-path=./report.html
Common Flags
--preset=perf # Performance only (skip accessibility, SEO, etc.)
--form-factor=mobile # Mobile device emulation (default)
--form-factor=desktop # Desktop
--throttling-method=devtools # More accurate throttling
--only-categories=performance,accessibility # Specific categories
--chrome-flags="--headless" # Headless Chrome
Performance Budgets
Create budget.json:
[
{
"resourceSizes": [
{ "resourceType": "script", "budget": 200 },
{ "resourceType": "image", "budget": 300 },
{ "resourceType": "stylesheet", "budget": 50 },
{ "resourceType": "total", "budget": 500 }
],
"resourceCounts": [{ "resourceType": "third-party", "budget": 5 }],
"timings": [
{ "metric": "interactive", "budget": 3000 },
{ "metric": "first-contentful-paint", "budget": 1500 },
{ "metric": "largest-contentful-paint", "budget": 2500 }
]
}
]
Run with budget:
lighthouse https://example.com --budget-path=./budget.json
Node API
import lighthouse from 'lighthouse'
import * as chromeLauncher from 'chrome-launcher'
async function runAudit(url) {
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] })
const result = await lighthouse(url, {
port: chrome.port,
onlyCategories: ['performance'],
formFactor: 'mobile',
throttling: {
cpuSlowdownMultiplier: 4,
},
})
await chrome.kill()
const { performance } = result.lhr.categories
const { 'largest-contentful-paint': lcp } = result.lhr.audits
return {
score: Math.round(performance.score * 100),
lcp: lcp.numericValue,
}
}
GitHub Actions
# .github/workflows/lighthouse.yml
name: Lighthouse
on:
pull_request:
push:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build site
run: npm ci && npm run build
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000
http://localhost:3000/about
budgetPath: ./budget.json
uploadArtifacts: true
temporaryPublicStorage: true
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
Lighthouse CI (LHCI)
For full CI integration with historical tracking:
# Install
npm install -g @lhci/cli
# Initialize config
lhci wizard
Creates lighthouserc.js:
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/about'],
startServerCommand: 'npm run start',
numberOfRuns: 3,
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['warn', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 1500 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
},
},
upload: {
target: 'temporary-public-storage', // or 'lhci' for self-hosted
},
},
}
Run:
lhci autorun
Parse JSON Report
import fs from 'fs'
const report = JSON.parse(fs.readFileSync('./report.json'))
// Overall scores (0-1, multiply by 100 for percentage)
const scores = {
performance: report.categories.performance.score,
accessibility: report.categories.accessibility.score,
seo: report.categories.seo.score,
}
// Core Web Vitals
const vitals = {
lcp: report.audits['largest-contentful-paint'].numericValue,
cls: report.audits['cumulative-layout-shift'].numericValue,
fcp: report.audits['first-contentful-paint'].numericValue,
tbt: report.audits['total-blocking-time'].numericValue,
}
// Failed audits
const failed = Object.values(report.audits)
.filter((a) => a.score !== null && a.score < 0.9)
.map((a) => ({ id: a.id, score: a.score, title: a.title }))
Compare Builds
# Save baseline
lighthouse https://prod.example.com --output=json --output-path=baseline.json
# Run on PR
lighthouse https://preview.example.com --output=json --output-path=pr.json
# Compare (custom script)
node compare-reports.js baseline.json pr.json
Simple comparison script:
const baseline = JSON.parse(fs.readFileSync(process.argv[2]))
const pr = JSON.parse(fs.readFileSync(process.argv[3]))
const metrics = ['largest-contentful-paint', 'cumulative-layout-shift', 'total-blocking-time']
metrics.forEach((metric) => {
const base = baseline.audits[metric].numericValue
const current = pr.audits[metric].numericValue
const diff = (((current - base) / base) * 100).toFixed(1)
const emoji = current <= base ? '✅' : '❌'
console.log(`${emoji} ${metric}: ${diff}% (${base.toFixed(0)} → ${current.toFixed(0)})`)
})
Troubleshooting
| Issue | Solution |
|---|---|
| Inconsistent scores | Run multiple times (--number-of-runs=3), use median |
| Chrome not found | Set CHROME_PATH env var |
| Timeouts | Increase with --max-wait-for-load=60000 |
| Auth required | Use --extra-headers or puppeteer script |
> related_skills --same-repo
> playwright-skill
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing. Do NOT use for quick page debugging or network inspection (use chrome-devtools instead).
> nx-workspace
Configure, explore, and optimize Nx monorepo workspaces. Use when setting up Nx, exploring workspace structure, configuring project boundaries, analyzing affected projects, optimizing build caching, or implementing CI/CD with affected commands. Keywords — nx, monorepo, workspace, projects, targets, affected. Do NOT use for running tasks (use nx-run-tasks) or code generation with generators (use nx-generate).
> nx-run-tasks
Execute build, test, lint, serve, and other tasks in an Nx workspace using single runs, run-many, and affected commands. Use when user says "run tests", "build my app", "lint affected", "serve the project", "run all tasks", or "nx affected". Do NOT use for code generation (use nx-generate) or workspace configuration (use nx-workspace).
> nx-generate
Generate code using Nx generators — scaffold projects, libraries, features, or run workspace-specific generators with proper discovery, validation, and verification. Use when user says "create a new library", "scaffold a component", "generate code with Nx", "run a generator", "nx generate", or any code scaffolding task in a monorepo. Prefers local workspace-plugin generators over external plugins. Do NOT use for running build/test/lint tasks (use nx-run-tasks) or workspace configuration (use nx-