How to Use Cypress and Google Lighthouse for Performance Testing
Use Cypress to reach a repeatable page state, then Lighthouse to audit it. Keep test-run time separate from page performance metrics.
Use Cypress to drive a repeatable user journey, then use Google Lighthouse to audit the page state that journey reaches. Cypress checks application behavior and controls the browser; Lighthouse audits a web page and reports performance measurements and opportunities. Cypress suite duration is not a Lighthouse measurement of your visitors’ page speed.
For reliable comparisons, audit the same URL and state with the same browser, device, navigation mode, throttling, storage treatment, and tool versions. Establish a baseline first, inspect the underlying metrics and audit details, and confirm surprising results with another controlled run. Lighthouse lab results estimate performance under test conditions; PageSpeed Insights may also show CrUX field data from real Chrome users when available.
1. Decide which performance question you are answering
| Question | Use | What the result means |
|---|---|---|
| How long does my test suite take? | Cypress run output and Cypress performance diagnostics | Test execution time, which includes Cypress, browser, app, and CI machine overhead. |
| How does a page perform after a user journey? | Cypress for the journey; Lighthouse for a repeatable audit of its destination or state | A lab audit of the page under the Lighthouse run’s conditions. |
| How do real users experience the site? | Field data such as CrUX, available through PageSpeed Insights for eligible pages | Observed real-user field experience, distinct from a Lighthouse lab run. |
Cypress explicitly cautions that it is not built as a performance testing tool: its instrumentation, network interception, and control of test steps add overhead. Its window.performance access can be useful for application checks, but do not present Cypress test timing as uninstrumented user-facing page performance. See the Cypress FAQ and its test performance guide.
2. Install the tools and establish a baseline
Lighthouse can run in Chrome DevTools, from the command line, or as a Node module. The CLI and Node workflows require Chrome installed locally. Use the same installed tool versions in later comparisons; record them with the baseline.
# In an existing Cypress project, install Lighthouse as a development dependency.
npm install --save-dev lighthouse
# Check the installed tools and browser.
npx cypress version
npx lighthouse --version
npx cypress info
# Run the Cypress suite.
npx cypress run
Before changing code, audit the target page once and save the report. In Chrome DevTools, open Lighthouse, choose the categories and device configuration relevant to the question, and run the audit. For an initial controlled baseline, use the same URL/state you plan to recheck. Record:
- URL and the steps or setup needed to reach the audited state.
- Browser, Lighthouse, Cypress, and application versions.
- Mobile or desktop configuration, navigation mode, throttling, and storage/cache treatment.
- Whether authentication, consent, or other state is required.
- Metric values, audit findings, and the aggregate score.
Chrome’s Lighthouse overview describes the supported run modes. Its performance scoring guide explains why the aggregate is composed from metric scores. Do not rely on a score alone: inspect the relevant metrics and audit details. Score variation can reflect changed test conditions, so rerun unexpected results before treating them as a regression.
3. Write a stable Cypress journey
This example visits a local app, performs a meaningful action, verifies the resulting page, and saves its URL. The saved URL gives the separate Lighthouse process a clear audit target. It works best when the destination can be loaded directly, such as a public route or a route whose state is encoded in its URL.
// cypress/e2e/performance-journey.cy.js
// Set CYPRESS_BASE_URL to the app's local or test deployment URL.
describe('performance audit destination', () => {
it('reaches and records the report page', () => {
cy.visit('/');
cy.get('[data-cy="open-report"]').click();
cy.get('[data-cy="report-title"]').should('be.visible');
cy.location('href').then((url) => {
cy.writeFile('artifacts/lighthouse-target.json', { url });
});
});
});
Replace the selectors and interaction with your real journey. Prefer assertions tied to expected application state over fixed-duration waits. Cypress retries queries and assertions; an arbitrary cy.wait(3000) can waste time when the page is ready sooner and still fail to describe the condition you need.
Run the app and journey, ensuring the artifacts directory exists:
mkdir -p artifacts
# In a separate terminal, start your application using its normal dev or preview command.
# For example, if your project defines this script:
npm run dev
# In another terminal:
CYPRESS_BASE_URL=http://localhost:3000 npx cypress run --spec cypress/e2e/performance-journey.cy.js
The application start command depends on the project; replace npm run dev and the port with the project’s actual command and local URL. Cypress can also visit a deployed test environment by setting CYPRESS_BASE_URL to that environment.
4. Audit the recorded page with Lighthouse
Run Lighthouse after Cypress has completed. This simple script reads the saved URL, launches the Lighthouse CLI through Node, and writes an HTML report. It is intentionally a separate process so the measurement is clearly identified as Lighthouse output, not Cypress suite timing.
// scripts/run-lighthouse.cjs
const fs = require('node:fs');
const { execFileSync } = require('node:child_process');
const target = JSON.parse(
fs.readFileSync('artifacts/lighthouse-target.json', 'utf8')
).url;
if (!target || !/^https?:\/\//.test(target)) {
throw new Error(`Expected an absolute http(s) URL, received: ${target}`);
}
fs.mkdirSync('artifacts', { recursive: true });
execFileSync(
process.execPath,
[
'node_modules/lighthouse/cli/index.js',
target,
'--output=html',
'--output-path=artifacts/lighthouse-report.html',
'--only-categories=performance',
'--chrome-flags=--headless',
],
{ stdio: 'inherit' }
);
# package.json scripts (merge these entries into the existing scripts object)
{
"scripts": {
"test:journey": "cypress run --spec cypress/e2e/performance-journey.cy.js",
"audit:lighthouse": "node scripts/run-lighthouse.cjs",
"performance:check": "npm run test:journey && npm run audit:lighthouse"
}
}
Then run:
mkdir -p artifacts
CYPRESS_BASE_URL=http://localhost:3000 npm run performance:check
The CLI invocation uses Lighthouse’s documented command-line workflow. Review the current Lighthouse documentation when adjusting CLI flags or output formats. Keep the HTML report as a CI artifact, and compare like-for-like runs rather than comparing a desktop run to a mobile run or a warm-cache run to a cold-cache run.
5. Handle authenticated or interaction-only states
The separate CLI example starts a fresh browser context. It does not inherit Cypress cookies, local storage, JavaScript memory, or the browser state created by earlier clicks. If the audited state requires login or interaction, choose a reproducible way to make that state available to the Lighthouse browser:
- Prefer a direct, deterministic test URL. If your application supports a test account and a route that can render the desired state directly, use a controlled test environment and ensure the Lighthouse process can access it.
- Use an authenticated Lighthouse workflow only after validating it. Lighthouse supports Node use, but carrying browser state between Cypress and a separate Lighthouse run requires deliberate browser/session handling. Verify the current API, browser requirements, and security handling against the installed Lighthouse version before implementing it.
- Keep secrets out of reports and source control. Use CI secret storage for credentials. Avoid writing session cookies, access tokens, or private page content into publicly retained artifacts.
- Use DevTools for exploratory interaction states. When the state exists only after a sequence of clicks and cannot be represented by a stable route, use Chrome DevTools to inspect and audit that state manually, or build a dedicated controlled setup that recreates it.
A Cypress Lighthouse plugin may provide a tighter integration, but community plugins are not automatically official Cypress integrations. Before adopting one, check its current maintenance, Cypress and browser compatibility, configuration, and how it handles thresholds. The official Cypress plugin directory distinguishes community-owned extensions: Cypress plugins directory. This guide avoids a package-specific integration recipe because a current canonical compatibility and installation matrix has not been established here.
6. Configure comparable Lighthouse runs
For each audit, deliberately choose settings that reflect the question, then keep them fixed between baseline and follow-up:
- Device profile: mobile or desktop. Mobile and desktop conditions produce different results; do not mix them in a trend line.
- Navigation mode: use the same mode for every run. A normal page-load audit and an audit of an already-open page do not represent the same operation.
- Throttling: keep network and CPU conditions consistent. If you change them to model another environment, start a separate baseline.
- Storage and cache: define whether runs begin with cleared storage/cache or a warm state. Authentication and consent storage can affect what the page renders.
- Audit categories: performance is the focus here; other Lighthouse categories answer different audit questions. Keep the selected categories consistent when comparing reports.
- Target URL and content: audit the same route and state. A changing deployment, API dataset, or third-party response can change the result independently of your code change.
Lighthouse reports can include request count and transfer size, DOM size, and third-party code impact. These findings help investigate likely causes; an opportunity is not, by itself, proof of a user-visible problem or a direct change to the aggregate score. Audit names and groupings can change between Lighthouse releases, so compare versions and read the current report details. Chrome notes that Lighthouse 13 reorganized some audits; see Lighthouse performance audits.
7. Use the report to find regressions
- Compare the same underlying metric values, not only the overall score.
- Open the audits related to a metric that regressed. Examine requests, transfer sizes, DOM size, and third-party impact where relevant.
- Use Chrome DevTools’ Performance panel to investigate the page behavior behind a finding.
- Change one likely cause at a time, then rerun the same journey and Lighthouse configuration.
- Repeat a surprising result under controlled conditions before failing a build. Set thresholds from repeated baselines and actual product needs, not from one noisy run.
- Check PageSpeed Insights and CrUX field data when available. Label these as field evidence; do not combine them with lab values as though they were the same measurement.
Chrome’s workflow guidance recommends starting with an audit, recording a baseline, and using the report to identify improvements: Lighthouse overview. For continuous audit workflows and regression prevention, see Lighthouse CI getting started.
8. Run the workflow in CI
A minimal CI job should install the locked dependencies, start the application in a test environment, wait for it to become available, run the Cypress journey, run Lighthouse, and retain the generated report. Make the exact same browser and app build available to both steps. Configure the CI runner with enough resources for the app, Cypress, and Chrome; resource pressure can make runs slower or less repeatable.
# Example sequence for a CI shell step; adapt the app start and readiness check.
npm ci
mkdir -p artifacts
npm run dev -- --host 0.0.0.0 &
APP_PID=$!
trap 'kill "$APP_PID" 2>/dev/null || true' EXIT
# Replace this readiness check with the health check appropriate to your app.
for attempt in $(seq 1 60); do
if curl --fail --silent http://localhost:3000/ >/dev/null; then break; fi
sleep 1
done
CYPRESS_BASE_URL=http://localhost:3000 npm run test:journey
npm run audit:lighthouse
In a real CI configuration, ensure the readiness loop fails if the app never starts, and upload artifacts/lighthouse-report.html whether the audit passes or fails. If you add a score or metric threshold, first observe repeated runs on the same runner and configuration. Consider a Lighthouse CI workflow for managing audits over time rather than writing an unvalidated one-off score gate.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Cypress succeeds but Lighthouse audits the wrong state | The CLI starts a new browser and only receives the URL. | Use a directly loadable route or implement and validate an explicit authenticated/stateful Lighthouse setup. |
lighthouse: command not found |
Lighthouse is not installed or the local executable is not on PATH. | Install it as a project dependency and invoke it with npx lighthouse, or use the Node script shown above. |
| Chrome cannot launch | Chrome is missing, unavailable to the process, or blocked by the CI environment. | Install/provide a supported Chrome browser and follow the current Lighthouse CLI requirements for that environment. |
| The audit URL is missing or invalid | The journey did not write the artifact, failed before the write, or produced a relative URL. | Create the artifacts directory, confirm the Cypress test reached its final assertion, and validate the saved absolute URL. |
| Results vary substantially between runs | Different throttling, cache, device, browser versions, server load, or third-party responses. | Fix and document conditions, use the same build and runner, and rerun outliers before acting on them. |
| CI audit is much slower than local | Shared or constrained CPU/memory, cold dependencies, or a slow application server. | Inspect CI resource use, ensure the app is ready before tests, and compare equivalent runner conditions. |
| Score drops but the page appears unchanged | An aggregate score moved due to metric changes or test variation; the score alone does not identify a cause. | Inspect metric values and audit details, repeat the run, and investigate with DevTools Performance. |
| Cypress test is slow and Lighthouse score is poor | These may be separate issues: Cypress runtime includes test overhead, while Lighthouse audits the page. | Diagnose Cypress suite execution with Cypress tools and page performance with Lighthouse reports independently. |
10. Performance, reliability, and cost considerations
- Separate the clocks. Report Cypress suite duration and Lighthouse metrics separately. Cypress instrumentation and the CI machine affect test duration.
- Control variability. Repeat audits when results are surprising, and keep browser versions, machine load, app build, and network conditions as stable as possible.
- Keep the journey useful. Automate the critical interactions that establish the state; do not turn every Lighthouse audit into a large end-to-end test suite.
- Choose gates cautiously. A threshold that fails on ordinary variation creates noisy CI. Use repeated baselines and confirm metric behavior before making a build-blocking rule.
- Budget CI resources. Cypress, Chrome, the app server, and supporting services share runner resources. Resource constraints can increase runtime and instability.
- Retain evidence efficiently. Save reports needed for comparison and investigation, and apply your normal CI artifact retention policy to reports that may contain private page data.
Or skip the browser setup
If your goal is to capture a page image for a report, test record, or visual review rather than run a Lighthouse audit, ScreenshotNeo can return a screenshot or PDF from one API request. It complements this workflow; it does not replace Lighthouse performance audits.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Cookie banners are accepted and removed before the shot, along with known newsletter popups and chat widgets. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Can Cypress catch performance regressions?
It can check application behavior and collect browser performance values, but Cypress adds instrumentation overhead. Use Lighthouse for repeatable page audits and interpret Cypress timings as test-run measurements.
Does Lighthouse run inside Cypress?
Lighthouse is available as a CLI and Node module. An integration can connect the tools, but check the current package’s ownership, maintenance, compatibility, and state-handling behavior before relying on it.
Is the Lighthouse score the same as real-user performance?
No. Lighthouse is a lab audit. PageSpeed Insights may show CrUX field data when available, which represents real Chrome user experiences.
Should every Lighthouse score change fail CI?
Only use thresholds that reflect repeated, comparable baselines and a meaningful product requirement. Verify unexpected changes before blocking a build.
Can Lighthouse audit a page after Cypress logs in?
Not through the separate CLI example alone. That process launches a fresh browser and does not inherit Cypress session state. Use a reproducible direct route or a separately validated stateful setup.


