Screenshot API Best Practices | LaunchBrightly API Docs

Best practices

You will find on this page a collection of best practices that we discussed internally over time but lacks a centralized reference page.

  1. Difference between step type: change, write, and keyDown.
  2. The internal workings of the waitForNavigation attribute
  3. The difference between waitFor and waitForNavigation
  4. Why use waitForNavigation Instead of waitFor
  5. Why Isn’t There a Generic Way to Determine if a Page Has Successfully Loaded?

Difference between step type: change, write, and keyDown.

Summarizing below the difference between the three different steps:

Step Type Description Puppeteers underlying method name
change Fills out an input field, which could be a selector, input, or textarea field. Locator.fill(). See https://pptr.dev/api/puppeteer.locator.fill
write Sends a keydown, keypress/input, and keyup event for each character in the text. There's a 200ms pause between each character type to simulate human typing behaviour. Useful when simulating typing in an autosuggest input field. Keyboard.type(). See https://pptr.dev/api/puppeteer.keyboard.type
keyDown Dispatches a keydown event. Use it to sent ENTER, SHIFT, ALT, CTRL command Keyboard.down. See https://pptr.dev/api/puppeteer.keyboard.down

Main differences between change and write:

The internal workings of the waitForNavigation attribute

The waitForNavigation attribute ensures that a page fully loads before executing the next step whenever there is a URL change, such as when clicking a hyperlink or a login button.

In Puppeteer, using waitForNavigation: 5000 translates to the following:

// Wait for the page to load completely using the 'networkidle2' algorithm
await page.waitForNavigation({waitUntil: 'networkidle2'})
// Then, wait an additional 5 seconds
await new Promise((resolve) => setTimeout(resolve, 5000));

In most cases, using only page.waitForNavigation({ waitUntil: 'networkidle2' }) is sufficient to ensure that a page has loaded successfully.

However, in some situations, the page might appear to be loaded before all elements are fully rendered. To address this, the second line, new Promise((resolve) => setTimeout(resolve, 5000)), adds a precautionary 5-second delay. This additional wait time helps prevent issues where page.waitForNavigation({ waitUntil: 'networkidle2' }) completes too early.

This approach is based on observations documented here, where adding a small delay proved beneficial in specific scenarios.

Difference Between waitFor and waitForNavigation

waitFor introduces a fixed delay before continuing, while waitForNavigation waits based on page load completion using the networkidle2 algorithm, plus an additional delay.

The delay value in waitForNavigation should typically be small (e.g., 500 to 5000 ms) because the primary waiting time is already handled by await page.waitForNavigation({ waitUntil: 'networkidle2' }). In many cases, a short delay like waitForNavigation:500 is enough.

To clarify, consider the following values:

waitFor:5000
waitForNavigation:2000

This distinction helps optimize loading times by combining flexible navigation waiting with minimal additional delay.

Why Use waitForNavigation Instead of waitFor

Consider the following delay values for waitFor and waitForNavigation:

waitFor: 5000 ms
waitForNavigation: 2000 ms

When Using waitFor:

When Using waitForNavigation:

Using waitForNavigation provides greater accuracy, executes more efficiently, and therefore reduces AWS Lambda infrastructure costs over time by avoiding unnecessary delays and handling varying load times more gracefully.

Using waitFor should be discouraged. Here's a great article explaining why.

Why Isn’t There a Generic Way to Determine if a Page Has Successfully Loaded?

There’s no universal method to determine when a page has fully loaded. Puppeteer offers several options, each suited for different scenarios:

page.waitForNavigation({ waitUntil: 'domcontentloaded' })
page.waitForNavigation({ waitUntil: 'load' })
page.waitForNavigation({ waitUntil: 'networkidle0' })
page.waitForNavigation({ waitUntil: 'networkidle2' })
page.waitForNetworkIdle({ idleTime: 250 })
page.waitForSelector("form.register", { visible: true })

Each of these methods waits for a different condition:

At some point, we may consider introducing a similar feature, but this would mean that the application itself would need to select the appropriate waiting algorithm. This approach could potentially be confusing for users or customers, who may not be familiar with the nuances of each method.

For now, a combination of waitForNetworkIdle followed by a brief additional delay seems to be the most reliable and generic way to ensure pages load fully before moving to the next step.