How to Run Lighthouse Performance Tests with Cypress
Add Lighthouse audits to Cypress, save reports, set score thresholds, and run stable checks in CI. Learn when to use a separate Lighthouse CI job.
To run Lighthouse from Cypress, connect a Lighthouse plugin to Cypress’s Node event setup, launch Chrome or Chromium with audit preparation, import the plugin command in the support file, then call cy.lighthouse() after cy.visit(). You can configure score thresholds and write the report to disk. This approach fits audits tied to a particular end-to-end flow; use Lighthouse CI (LHCI) separately when you mainly need repeatable URL collection, report uploads, and historical comparisons.
1. Install the Cypress Lighthouse integration
The cypress-lighthouse-plugin README documents this integration. Install it in your existing Cypress project:
npm install cypress-lighthouse-plugin
The package README says Lighthouse is installed as a peer dependency. Check the installed package metadata and release history against your Cypress, Lighthouse, Chrome, and Node versions before committing to a version set: the documentation does not establish a tested compatibility matrix for every current version. Google’s Lighthouse README currently says the Lighthouse Node CLI requires Node 22 LTS or later; verify the requirement for the specific package version you use.
2. Configure Cypress to launch Chrome and register the task
In cypress.config.ts, prepare the browser before launch and register the Lighthouse task. This example also writes the plugin’s report string as JSON:
import { defineConfig } from "cypress";
import { writeFile } from "node:fs/promises";
import { lighthouse, prepareAudit } from "cypress-lighthouse-plugin";
export default defineConfig({
e2e: {
specPattern: "cypress/**/*.cy.ts",
defaultBrowser: "chrome",
setupNodeEvents(on) {
on("before:browser:launch", (_browser, launchOptions) => {
prepareAudit(launchOptions);
});
on("task", {
lighthouse: lighthouse(async (lighthouseResult) => {
await writeFile("lighthouse-report.json", lighthouseResult.report);
console.log("Saved Lighthouse report as lighthouse-report.json.");
}),
});
},
},
});
The browser hook and task registration are required parts of the documented setup. Lighthouse works with Chrome or Chromium in this integration, so ensure the selected browser is installed and available in local and CI environments. Cypress configuration and launch behavior can vary with project structure; retain any existing configuration rather than replacing it wholesale.
3. Import the command and audit a visited page
In cypress/support/e2e.ts, import the command:
import "cypress-lighthouse-plugin/commands";
Then visit the page and run the audit in a spec such as cypress/e2e/lighthouse.cy.ts:
describe("Lighthouse audit", () => {
it("audits the product page", () => {
cy.visit("http://localhost:3030/products/example");
cy.lighthouse();
});
});
Run it with Chrome:
npx cypress run --browser chrome --spec cypress/e2e/lighthouse.cy.ts
The audit runs at the point in the spec where cy.lighthouse() is called. If the page requires login or setup, perform those steps before the audit and confirm the browser is on the intended page. Cypress commands and Lighthouse collection have different jobs: use Cypress to reach the desired state, then collect the audit.
4. Set score thresholds and Lighthouse options
Pass thresholds as the first argument. A score below any configured threshold fails the Cypress test. The plugin README’s sample values below are examples, not universal performance targets:
cy.lighthouse({
performance: 85,
accessibility: 90,
"best-practices": 85,
seo: 80,
});
The README says thresholds default to zero. Choose a threshold based on your own baseline, page purpose, and measurement consistency. A high score gate copied from an example can make a pipeline noisy or block changes for normal variation.
The second argument accepts Lighthouse options; the third accepts Lighthouse configuration. For example, choose desktop form factor and limit the categories:
cy.lighthouse(
{ performance: 85, accessibility: 90 },
{ formFactor: "desktop", screenEmulation: { disabled: true } },
{
extends: "lighthouse:default",
settings: { onlyCategories: ["performance", "accessibility"] },
},
);
In the README example, disabling screen emulation is paired with desktop form factor. Use a configuration that reflects the experience you intend to assess; changing device emulation changes the conditions under which the page is measured. The plugin also documents global settings under Cypress’s env.lighthouse key:
export default defineConfig({
e2e: {
setupNodeEvents(on) {
// Register the browser hook and lighthouse task as shown above.
},
},
env: {
lighthouse: {
thresholds: {
performance: 85,
accessibility: 90,
"best-practices": 85,
seo: 80,
},
options: {
formFactor: "desktop",
screenEmulation: { disabled: true },
},
config: {
extends: "lighthouse:default",
settings: { onlyCategories: ["performance", "accessibility"] },
},
},
},
});
Use per-test arguments when different pages need different gates or settings; use global configuration for a shared default. Consult the plugin README and the Lighthouse configuration documentation for options supported by the versions installed in your project.
5. Run the audit in CI after the app is ready
Start the application and wait until its URL responds before running Cypress. Cypress’s CI guide explains that starting a server in the background and immediately running tests creates a race. It documents start-server-and-test and wait-on approaches.
For example, install the readiness helper and define scripts in package.json:
npm install --save-dev start-server-and-test
{
"scripts": {
"start": "my-server -p 3030",
"cy:run": "cypress run --browser chrome",
"test:e2e": "start-server-and-test start http://localhost:3030 cy:run"
}
}
Replace my-server with your project’s actual start command. The readiness helper waits for the URL to respond before invoking Cypress and shuts down the server after the test command. If your development server does not answer HEAD, Cypress documents using an explicit GET check, for example http-get://localhost:3030. For local HTTPS with an untrusted development certificate, follow the Cypress CI guide’s documented insecure-local-certificate setting rather than silently disabling certificate checks more broadly.
Use a CI image with Chrome or Chromium installed and pin a deliberate image tag so browser and runtime changes are managed. Keep Lighthouse and Node versions aligned with the requirements of the installed packages. LHCI getting-started examples contain older Node and CLI versions; treat them as examples of pipeline shape, not current compatibility guidance. Cypress lists CI provider examples in its CI overview.
6. Keep the report and make failures actionable
The callback in the configuration writes lighthouseResult.report to lighthouse-report.json. Make sure CI preserves that file as an artifact if developers need it after a job ends; artifact upload syntax depends on the CI provider. A JSON report lets the team inspect details behind a score, rather than seeing only a pass or fail. Avoid overwriting reports from parallel jobs: use unique filenames or separate output directories if multiple specs or workers can write at once.
Begin with a baseline run and check how repeatable scores are in your chosen environment. Introduce a blocking threshold gradually, then adjust it when meaningful regressions can be distinguished from ordinary run-to-run variation. Run on consistent, adequately provisioned machines: Lighthouse CI’s guide notes that larger machines produce more stable results.
7. Choose Cypress audits or a separate Lighthouse CI job
| Need | Good fit |
|---|---|
| Audit the exact state reached during an end-to-end user journey | Cypress integration with cy.lighthouse() |
| Collect audits for a configured set of URLs in a dedicated performance job | Lighthouse CI |
| Keep a report file from a Cypress run | Plugin callback plus CI artifact retention |
| Upload reports, assert on results, or compare report history | Lighthouse CI with a suitable upload target or server |
Lighthouse CI Getting Started describes a separate lhci autorun flow, report upload targets, and a gradual rollout. Temporary public report storage can provide report links, but the guide says it does not provide historical storage, diffs, or build failures. Its configuration guide covers assertions and authenticated-page preparation using a Puppeteer script. Check current LHCI and Lighthouse runtime requirements before copying older pinned examples.
The two approaches can coexist: keep a Cypress audit where performance depends on a user flow, and use LHCI for broader URL collection and reporting. The Cypress plugin catalog identifies listed plugins as community-owned; review the integration’s maintenance and compatibility for your project.
Or skip the browser setup
For a page screenshot as a visual artifact, ScreenshotNeo provides a website screenshot API and MCP server. A screenshot is useful for reviewing page appearance alongside an audit, but it does not run Lighthouse or measure performance scores. See the ScreenshotNeo site and API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo removes cookie banners, popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| Lighthouse cannot start or browser launch fails | Chrome or Chromium is missing, the wrong browser is selected, or launch preparation was not registered. | Install/use Chrome or Chromium, set defaultBrowser or pass --browser chrome, and confirm the before:browser:launch hook calls prepareAudit. |
cy.lighthouse is undefined |
The command import is missing or placed in a support file Cypress does not load. | Import cypress-lighthouse-plugin/commands from the configured E2E support file and check the Cypress support-file setting. |
| The Lighthouse task is not registered | The Node event setup did not register the task or the project uses another Cypress config file. | Confirm setupNodeEvents registers lighthouse: lighthouse(...) and that the executed project loads this config. |
| Visit fails intermittently in CI | The app server is not ready when Cypress begins. | Wait for an HTTP readiness check with start-server-and-test or wait-on; avoid an arbitrary fixed sleep. |
| Scores vary or a threshold fails sporadically | Measurement conditions or available machine resources vary, or the gate is tighter than the baseline supports. | Use a consistent browser/runtime and CI machine, establish a baseline, and set a threshold that tolerates observed variation while catching material regressions. |
| Report file is missing or overwritten | The callback did not run, the working directory differs, or parallel runs used the same path. | Check callback errors and output directory, preserve the report as a CI artifact, and assign unique paths to parallel jobs. |
| Dependency or runtime errors after an upgrade | The plugin, Lighthouse, Cypress, Node, and browser versions may not be compatible. | Inspect package peer dependencies and release history, then verify the selected Lighthouse Node requirement and browser availability before updating CI. |
| Audit evaluates the wrong page state | The audit ran before navigation, authentication, or required app state completed. | Place cy.lighthouse() after the visit and all setup commands; confirm the final URL and state before collecting. |
Performance, reliability, and cost
An in-test Lighthouse run adds audit time to the Cypress job, so auditing every route in every end-to-end spec can make feedback slower. Select representative pages and flows for embedded audits, and use a dedicated collection job when you need coverage across many URLs. Report retention consumes CI artifact storage according to your provider’s policies; Lighthouse itself and the plugin have their own package and runtime requirements, so budget for maintaining Chrome/Node compatibility rather than assuming the audit is cost-free to operate.
For reliability, keep the browser and machine conditions consistent, wait for server readiness, save reports, and treat score thresholds as regression gates only after observing a baseline. A score is a measurement under the configured Lighthouse settings; it is not by itself proof that every real user sees the same result.
FAQ
Can I run Lighthouse against a page after a Cypress login?
Yes. Use Cypress to perform the login and navigate to the target state, then call cy.lighthouse(). For a separate LHCI job, its configuration documentation describes browser preparation with a Puppeteer script.
Does cy.lighthouse() only test performance?
No. The plugin’s documented thresholds include performance, accessibility, best practices, and SEO. Configure the categories relevant to your quality gate.
Should I use Lighthouse in Cypress for every pull request?
Use it when tying an audit to a user flow provides value and the measurement is stable enough for the team. For broad URL collection, reporting, and history, consider a dedicated LHCI job.
Can a screenshot replace the Lighthouse audit?
No. A screenshot shows rendered appearance; Lighthouse evaluates page quality categories and performance metrics. They can complement each other, but a screenshot API does not produce Lighthouse scores.


