Combine Navigation, Timespan, and Snapshot audits into a single multi-step report that covers the full page lifecycle.
Historically, Lighthouse analyzed the cold page load of a page. Starting with Lighthouse v10, it can analyze and report on the entire page lifecycle via user flows.You might want flows if:
You want to run Lighthouse on your whole web app, not just the landing page.
You want to verify that all parts of an experience are accessible (e.g. a checkout process).
You want to measure Cumulative Layout Shift across an SPA page transition.
Lighthouse runs in three modes. Each has distinct use cases and limitations. A flow is built by combining them.
Navigation
Timespan
Snapshot
Navigation mode analyzes a single page load. It is the default mode — all Lighthouse runs prior to v9.6.0 were essentially navigations.Use cases:
Obtain a Lighthouse Performance score and all performance metrics.
Assess Progressive Web App capabilities.
Analyze accessibility immediately after page load.
Limitations:
Cannot analyze form submissions or single-page app transitions.
Cannot analyze content that isn’t available immediately on page load.
// Navigate with a URLawait flow.navigate('https://example.com');// Interaction-initiated navigation via a callbackawait flow.navigate(async () => { await page.click('a.link');});// Navigate with explicit start/endawait flow.startNavigation();await page.click('a.link');await flow.endNavigation();
When you provide a callback instead of a URL, Lighthouse cannot clear Service Worker and Cache Storage (since it doesn’t know the URL in advance). This generally reflects the real user experience. If you need to clear storage manually, do so inside the callback.
Timespan mode records an arbitrary period of time, typically containing user interactions. There is no overall performance score.Use cases:
Measure layout shifts and JavaScript execution time over a time range that includes interactions.
Discover performance opportunities for long-lived pages and SPAs.
Limitations:
No overall performance score.
Cannot measure moment-based metrics like Largest Contentful Paint.
Chrome DevTools supports individual Navigation, Timespan, and Snapshot runs, but does not support combining multiple steps into a single multi-step user flow report. To create a multi-step flow, use the Node API as shown below.
A flow is created by calling startFlow(page) with a Puppeteer Page object. You then call flow methods (navigate, startTimespan/endTimespan, snapshot) in sequence to build up the steps.
import {startFlow} from 'lighthouse';import puppeteer from 'puppeteer';const browser = await puppeteer.launch();const page = await browser.newPage();const flow = await startFlow(page);
After all steps are recorded, generate the report:
The example below models a user flow for an e-commerce site: navigate to the homepage, search for a product, snapshot the results, and navigate to the detail page.
import puppeteer from 'puppeteer';import {startFlow} from 'lighthouse';import {writeFileSync} from 'fs';// Setup the browser and Lighthouse.const browser = await puppeteer.launch();const page = await browser.newPage();const flow = await startFlow(page);// Phase 1 - Navigate to the landing page.await flow.navigate('https://web.dev/');// Phase 2 - Interact with the page and submit the search form.await flow.startTimespan();await page.click('button[search-open]');const searchBox = await page.waitForSelector('devsite-search[search-active] input');await searchBox.type('CLS');await searchBox.press('Enter');// Ensure search results have rendered before moving on.const link = await page.waitForSelector('devsite-content a[href="https://web.dev/articles/cls"]');await flow.endTimespan();// Phase 3 - Analyze the new state.await flow.snapshot();// Phase 4 - Navigate to the article.await flow.navigate(async () => { await link.click();});// Get the comprehensive flow report.writeFileSync('report.html', await flow.generateReport());// Save results as JSON.writeFileSync('flow-result.json', JSON.stringify(await flow.createFlowResult(), null, 2));// Cleanup.await browser.close();
The resulting report summarizes all steps and lets you drill into each phase individually.
If Puppeteer is already configured with a specific viewport, disable Lighthouse’s own screen emulation to avoid overriding it:
import puppeteer from 'puppeteer';import {startFlow, desktopConfig} from 'lighthouse';const browser = await puppeteer.launch();const page = await browser.newPage();const flow = await startFlow(page, { // Puppeteer is emulating a desktop environment, // so use the desktop config to keep desktop scoring. // If Puppeteer is emulating a mobile device, remove this line. config: desktopConfig, // Prevent Lighthouse from changing screen dimensions. flags: {screenEmulation: {disabled: true}},});await page.setViewport({width: 1000, height: 500});await flow.navigate('https://example.com');
Record each timespan around a single interaction sequence or page transition. Shorter recordings are easier to debug and produce more actionable results.
Always use navigation mode for hard navigations
If the user action causes a full page navigation (URL change, form POST, etc.), use flow.navigate() or startNavigation/endNavigation rather than capturing it inside a timespan.
Use snapshot after significant DOM changes
Reach for snapshot mode when a substantial portion of the page content has changed — for example, after a multi-step wizard advances to the final confirmation screen.
Wait for interactions to finish before ending a timespan
Always wait for page transitions and async operations to settle before calling flow.endTimespan(). Puppeteer helpers like page.waitForSelector, page.waitForFunction, page.waitForResponse, and page.waitForTimeout are useful here.