If you’re new to Lighthouse development, start by reading up on the overall architecture and how configuration works before continuing.
Lighthouse plugins are a way to extend the functionality of Lighthouse with insight from domain experts (that’s you!) and easily share this extra functionality with other Lighthouse users. At its core, a plugin is a node module that implements a set of checks that will be run by Lighthouse and added to the report as a new category.
Plugins are easily shared and have a stable API that won’t change between minor version bumps but are also more limited in scope than a custom Lighthouse configuration. Before getting started with plugins, think about your current needs, and consult the table below to decide which is best for you.
Capability | Plugin | Custom Config |
---|---|---|
Include your own custom audits | ✅ | ✅ |
Add a custom category | ✅ | ✅ |
Easily shareable and extensible on NPM | ✅ | ❌ |
Semver-stable API | ✅ | ❌ |
Gather custom data from the page (artifacts) | ❌ | ✅ |
Modify core categories | ❌ | ✅ |
Modify config.settings properties |
❌ | ✅ |
To develop a Lighthouse plugin, you’ll need to write three things:
package.json
file to define your plugin’s dependencies and point to your plugin.js
file.plugin.js
file to declare your plugin’s audits, category name, and scoring.To see a fully functioning example, see our plugin recipe.
package.json
A Lighthouse plugin is just a node module with a name that starts with lighthouse-plugin-
. Any dependencies you need are up to you. However, do not depend on Lighthouse directly, use peerDependencies
to alert dependents, and devDependencies
for your own local development:
Example package.json
{
"name": "lighthouse-plugin-cats",
"main": "plugin.js",
"peerDependencies": {
"lighthouse": "^5.6.0"
},
"devDependencies": {
"lighthouse": "^5.6.0"
}
}
plugin.js
This file contains the configuration for your plugin. It can be called anything you like, just ensure it is referenced by the "main"
property in your package.json
.
Example plugin.js
module.exports = {
// Additional audits to run on information Lighthouse gathered.
audits: [{path: 'lighthouse-plugin-cats/audits/has-cat-images.js'}],
// A new category in the report for the plugin output.
category: {
title: 'Cats',
description:
'When integrated into your website effectively, cats deliver delight and bemusement.',
auditRefs: [{id: 'has-cat-images-id', weight: 1}],
},
};
These files contain the logic that will generate results for the Lighthouse report. An audit is a class with two important properties:
meta
- This contains important information about how the audit will be referenced and how it will be displayed in the HTML report.audit
- This is a function that should return the audit’s results. See API > Plugin Audits.Example audits/has-cat-images.js
const Audit = require('lighthouse').Audit;
class CatAudit extends Audit {
static get meta() {
return {
id: 'has-cat-images-id',
title: 'Page has least one cat image',
failureTitle: 'Page does not have at least one cat image',
description:
'Pages should have lots of cat images to keep users happy. ' +
'Consider adding a picture of a cat to your page improve engagement.',
requiredArtifacts: ['ImageElements'],
};
}
static audit(artifacts) {
// Artifacts requested in `requiredArtifacts` above are passed to your audit.
// See the "API -> Plugin Audits" section below for what artifacts are available.
const images = artifacts.ImageElements;
const catImages = images.filter(image => image.src.toLowerCase().includes('cat'));
return {
// Give users a 100 if they had a cat image, 0 if they didn't.
score: catImages.length > 0 ? 1 : 0,
// Also return the total number of cat images that can be used by report JSON consumers.
numericValue: catImages.length,
};
}
}
module.exports = CatAudit;
# be in your plugin directory, and have lighthouse as a devDependency.
NODE_PATH=.. yarn lighthouse https://example.com --plugins=lighthouse-plugin-example --only-categories=lighthouse-plugin-example --view
# Note: we add the parent directory to NODE_PATH as a hack to allow Lighthouse to find this plugin.
# This is useful for local development, but is not necessary when your plugin consuming from NPM as
# a node module.
The plugin config file (see plugin.js
in the example and recipe) is a subset of the available configuration for full custom Lighthouse config files.
A plugin config is an object that has at least two properties: audits
and category
.
audits
Defines the new audits the plugin adds. It is an array of string paths to the audit files. Each path should be treated as an absolute string a user of your module might pass to require
, so use paths of the form lighthouse-plugin-<your plugin>/path/to/audits/audit-file.js
.
Type: Array<{path: string}>
category
Defines the display strings of the plugin’s category and configures audit scoring and grouping. It is an object with at least two properties title
and auditRefs
.
title: string
REQUIRED - The display name of the plugin’s category in the report.description: string
OPTIONAL - A more detailed description of the category’s purpose.manualDescription: string
OPTIONAL - A more detailed description of all of the manual audits in a plugin. Only use this if you’ve added manual audits.auditRefs: Array<{id: string, weight: number, group?: string}>
REQUIRED - The list of audits to include in the plugin category along with their overall weight in the score of the plugin category. Each audit ref may optionally reference a group ID from groups
.groups
Defines the audit groups used for display in the HTML report.
It is an object whose keys are the group IDs and whose values are objects with the following properties:
title: string
REQUIRED - The display name of the group in the report.description: string
OPTIONAL - A more detailed description of the group’s purpose.Example of Category with Groups
<img alt=”audit group with groups” src=”https://user-images.githubusercontent.com/2301202/56936017-86d3ce80-6aba-11e9-9a43-39bf3810b551.png” width=550>
Example of Category without Groups
<img alt=”audit group without groups” src=”https://user-images.githubusercontent.com/2301202/56936043-c0a4d500-6aba-11e9-9e37-0bc131010a37.png” width=550>
A plugin audit is a class that implements at least two properties: meta
and audit()
.
meta
The meta
property is a static getter for the metadata of an audit. It should return an object with the following properties:
id: string
REQUIRED - The string identifier of the audit, in kebab case, typically matching the file name.title: string
REQUIRED - Short, user-visible title for the audit when successful.failureTitle: string
OPTIONAL - Short, user-visible title for the audit when failing.description: string
REQUIRED - A more detailed description that describes why the audit is important and links to Lighthouse documentation on the audit; markdown links supported.requiredArtifacts: Array<string>
REQUIRED - A list of artifacts that must be present for the audit to execute. See Available Artifacts for what’s available to plugins.scoreDisplayMode: "numeric" | "binary" | "manual" | "informative"
OPTIONAL - A string identifying how the score should be interpreted for display.See Best Practices > Naming for best practices on the display strings.
audit(artifacts, context)
The audit()
property is a function the computes the audit results for the report. It accepts two arguments: artifacts
and context
. artifacts
is an object whose keys will be the values you passed to requiredArtifacts
in the meta
object. context
is an internal object whose primary use in plugins is to derive network request information (see Using Network Requests).
The primary objective of the audit function is to return a score
from 0
to 1
based on the data observed in artifacts
. There are several other properties that can be returned by an audit to control additional display features. For the complete list, see the audit results documentation and type information.
The following artifacts are available for use in the audits of Lighthouse plugins. For more detailed information on their usage and purpose, see the type information.
devtoolsLogs
fetchTime
settings
traces
BenchmarkIndex
ConsoleMessages
HostUserAgent
ImageElements
LinkElements
MetaElements
NetworkUserAgent
RuntimeExceptions
ScriptElements
Stacks
TestedAsMobileDevice
Timing
URL
ViewportDimensions
WebAppManifest
While Lighthouse has more artifacts with information about the page than are in this list, those artifacts are considered experimental and their structure or existence could change at any time. Only use artifacts not on the list above if you are comfortable living on the bleeding edge and can tolerate unannounced breaking changes.
If you’re interested in other page information not mentioned here, please file an issue. We’d love to help.
You might have noticed that a simple array of network requests is missing from the list above. The source information for network requests made by the page is actually contained in the devtoolsLogs
artifact, which contains all the of DevTools Protocol traffic recorded during page load. The network request objects are derived from this message log at audit time.
See below for an example of an audit that processes network requests.
const {Audit, NetworkRecords} = require('lighthouse');
class HeaderPoliceAudit {
static get meta() {
return {
id: 'header-police-audit-id',
title: 'All headers stripped of debug data',
failureTitle: 'Headers contained debug data',
description: 'Pages should mask debug data in production.',
requiredArtifacts: ['devtoolsLogs'],
};
}
static async audit(artifacts, context) {
// Lighthouse loads the page multiple times: while offline, without javascript, etc.
// Use the devtools log from the default pass of the page.
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
// Request the network records from the devtools log.
// The `context` argument is passed in to allow Lighthouse to cache the result and not re-compute the network requests for every audit that needs them.
const requests = await NetworkRecords.request(devtoolsLog, context);
// Do whatever you need to with the network requests.
const badRequests = requests.filter(request =>
request.responseHeaders.some(header => header.name.toLowerCase() === 'x-debug-data')
);
return {
score: badRequests.length === 0 ? 1 : 0,
};
}
}
module.exports = HeaderPoliceAudit;
There are only two hard things in Computer Science: cache invalidation and naming things. Phil Karlton
There are several display strings you will need to write in the course of plugin development. To ensure your plugin users have a consistent experience with the rest of the Lighthouse report, follow these guidelines.
Write category titles that are short (fewer than 20 characters), ideally a single word or acronym. Avoid unnecessary prefixes like “Lighthouse” or “Plugin” which will already be clear from the context of the report.
Write category descriptions that provide context for your plugin’s audits and link to where users can learn more or ask questions about their advice.
Write audit titles in the present tense that describe what the page is successfully or unsuccessfully doing.
DO
Document has a
<title>
element
Document does not have a
<title>
element
Uses HTTPS
Does not use HTTPS
Tap targets are sized appropriately
Tap targets are not sized appropriately
DON’T
Good job on
alt
attributes
Fix your headers
Write audit descriptions that provide brief context for why the audit is important and link to more detailed guides on how to follow its advice. Markdown links are supported, so use them!
DO
Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. Learn more.
All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. Learn more.
DON’T
Images need alt attributes.
4.8.4.4 Requirements for providing text to act as an alternative for images Except where otherwise specified, the alt attribute…. 10,000 words later… and that is everything you need to know about the
alt
attribute!
0
and 1
. Scores greater than 0.9
will be hidden in “Passed Audits” section by default.return {score: null, notApplicable: true}
.The web is a diverse place, and your plugin will be run on pages you never thought existed. Here are a few things to keep in mind when writing your audit to avoid common bugs. The Lighthouse team has made all of these mistakes below, so you’re in good company!
Most audits will have a specific use case in mind that will apply to most elements or requests, but there are corner cases that come up fairly frequently that are easy to forget.
Examples:
blob:
, data:
, file:
, etc)type="x-shader/x-vertex"
, type="application/ld+json"
, etc)Most artifacts will try to represent as truthfully as possible what was observed from the page. When possible, the values are normalized according to the spec as you would access them from the DOM, but typically no transformation beyond this is done. This means that some values will have leading or trailing whitespace, be mixed-case, potentially missing, relative URLs instead of absolute, etc.
Examples:
type
valuessrc
values