Configuration
pulse.config.js sets the performance and load thresholds that Pulse enforces across your application. All fields are optional — the defaults match Google's Core Web Vitals "good" band. Configuration here is not about enabling features; it is about deciding where, if anywhere, you need to lower a guaranteed baseline.
Full schema
// pulse.config.js
export default {
port: 3000,
lighthouse: {
// Category scores — 0 to 100. Default: 100 for all four.
performance: 100,
accessibility: 100,
bestPractices: 100,
seo: 100,
// Core Web Vitals and timing metrics.
// Defaults are the Google "good" thresholds.
lcp: 2500, // Largest Contentful Paint (ms)
cls: 0.1, // Cumulative Layout Shift
tbt: 200, // Total Blocking Time (ms)
fcp: 1800, // First Contentful Paint (ms)
si: 3400, // Speed Index (ms)
inp: 200, // Interaction to Next Paint (ms)
},
load: {
duration: 10,
connections: 10,
thresholds: {
rps: undefined, // minimum req/s (optional)
p99: undefined, // maximum p99 latency ms (optional)
errors: 0,
},
},
environments: {
// Environment names are bespoke — choose whatever suits your project.
local: { url: 'http://localhost:3000', default: true },
staging: {
url: 'https://staging.myapp.com',
headers: { Authorization: `Bearer ${process.env.STAGING_TOKEN}` },
load: { duration: 30, connections: 50 },
lighthouse: { performance: 90 },
},
production: { url: 'https://myapp.com' },
},
routes: {
// Per-route overrides — merged on top of global lighthouse/load config.
// Only specify what differs from the global defaults.
'/dashboard': {
lighthouse: {
performance: 85,
lcp: 4000,
},
},
'/embed': {
lighthouse: {
bestPractices: 85,
},
},
},
}
port
| Field | Type | Default | Description |
|---|---|---|---|
port | number | 3000 | Port the dev and production servers listen on. |
lighthouse
Global Lighthouse thresholds that every page must meet. /pulse-report enforces these after every audit — any page that falls below a threshold is reported as a failure, not a warning.
| Field | Type | Default | Description |
|---|---|---|---|
performance | number | 100 | Lighthouse Performance category score (0–100). |
accessibility | number | 100 | Lighthouse Accessibility category score (0–100). |
bestPractices | number | 100 | Lighthouse Best Practices category score (0–100). |
seo | number | 100 | Lighthouse SEO category score (0–100). |
lcp | number | 2500 | Largest Contentful Paint budget (ms). |
cls | number | 0.1 | Cumulative Layout Shift budget. |
tbt | number | 200 | Total Blocking Time budget (ms). |
fcp | number | 1800 | First Contentful Paint budget (ms). |
si | number | 3400 | Speed Index budget (ms). |
inp | number | 200 | Interaction to Next Paint budget (ms). |
null removes that check for the project — use sparingly. Raising a threshold is always preferable to disabling it.routes
Route-specific overrides let you lower a threshold for a specific page without relaxing the global guarantee. Only specify the fields that differ — everything else inherits from the global config.
routes: {
// This route uses a third-party chart library — relax performance only.
'/dashboard': {
lighthouse: {
performance: 85,
lcp: 4000,
},
},
}
route field, including any leading slash. Dynamic segments are not supported — create a specific override for each route pattern.load
Load test thresholds enforced by /pulse-load. All fields are optional — omitting a threshold means that check is not enforced.
| Field | Type | Default | Description |
|---|---|---|---|
duration | number | 10 | Test duration in seconds. |
connections | number | 10 | Number of concurrent request chains. |
thresholds.rps | number | undefined | Minimum acceptable requests per second. Unset = no check. |
thresholds.p99 | number | undefined | Maximum acceptable p99 latency (ms). Unset = no check. |
thresholds.errors | number | 0 | Maximum acceptable error count. |
load: {
duration: 30,
connections: 20,
thresholds: {
rps: 100, // fail if below 100 req/s
p99: 500, // fail if p99 exceeds 500ms
errors: 0,
},
},
Per-route overrides follow the same pattern as lighthouse:
routes: {
'/feed': {
load: { connections: 5, thresholds: { rps: 20 } },
},
}
Results are saved to .pulse/load-reports/ and displayed in the Load Tests tab of the report dashboard, alongside the Lighthouse Performance tab for the same route.
environments
Named environments let you enforce thresholds against different targets — local, staging, production — from the same config. Thresholds are applied per-environment, so staging and production can have different performance floors.
| Field | Type | Description |
|---|---|---|
url | string | Base URL to test against. If it contains localhost or 127.0.0.1, a local production build is spun up automatically. Remote URLs are tested directly. |
default | boolean | The environment used when none is explicitly specified by pulse report and pulse load-test. |
headers | object | HTTP headers sent with every request to this environment. Useful for authorization tokens on protected staging deployments — read values from process.env rather than hardcoding them. |
load | object | Load test config overrides for this environment. Same fields as the global load block. Merged on top of global config. |
lighthouse | object | Lighthouse threshold overrides for this environment. Same fields as the global lighthouse block. Merged on top of global config. |
local, staging, production, preview, eu — whatever maps to your project's infrastructure. There are no reserved names.Threshold merge order: global config → environment override → per-route override.
environments: {
local: { url: 'http://localhost:3000', default: true },
staging: {
url: 'https://staging.myapp.com',
headers: { Authorization: `Bearer ${process.env.STAGING_TOKEN}` },
load: { duration: 30, connections: 50, thresholds: { rps: 500 } },
lighthouse: { performance: 90 },
},
production: { url: 'https://myapp.com' },
}
CWV default thresholds
The default metric thresholds match the Google "good" band from the Core Web Vitals specification:
| Metric | Default | Google "good" threshold |
|---|---|---|
| LCP | 2500 ms | ≤ 2500 ms |
| CLS | 0.1 | ≤ 0.1 |
| TBT | 200 ms | ≤ 200 ms |
| FCP | 1800 ms | ≤ 1800 ms |
| SI | 3400 ms | ≤ 3400 ms |
| INP | 200 ms | ≤ 200 ms |