ScreenshotNeo

BlogHow-to

How to Scroll a Page With the Puppeteer Mouse Wheel

Use `page.mouse.wheel()` to send wheel input in Puppeteer. Learn how to scroll the page or an element, choose the right method, and fix common issues.

By the ScreenshotNeo team4 October 20267 min read

To scroll a page with Puppeteer’s mouse wheel, call and await page.mouse.wheel() with a positive deltaY for downward movement:

await page.mouse.wheel({ deltaY: 300 });

Use a negative deltaY to request movement in the opposite direction. If you need to scroll a particular element through Puppeteer’s locator API, use page.locator(selector).scroll(). Choose wheel input when the page’s response to a wheel event matters; choose locator scrolling when you want to target an element.

Scroll the page with mouse wheel input

Mouse.wheel() dispatches a mousewheel event and returns a promise. Await it before depending on the resulting page state or issuing the next interaction. Puppeteer’s [Mouse.wheel() documentation](https://pptr.dev/api/puppeteer.mouse.wheel) shows the same API.

await page.mouse.wheel({ deltaY: 300 });

A positive value requests downward movement; a negative value requests movement in the other direction. The delta is an input to the browser, not a guarantee that the document will move by precisely that many CSS pixels. The actual result depends on the page and browser state.

Runnable example

This example opens a page, sends one downward wheel event, and closes the browser. It assumes Puppeteer is installed in the project and that its browser is available.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
    await page.mouse.wheel({ deltaY: 300 });
    await page.waitForTimeout(250);
    console.log('Wheel input sent.');
  } finally {
    await browser.close();
  }
})();

Replace the example URL with the page you need to scroll. If the page loads content in response to scrolling, wait for the relevant content or page condition after the wheel event instead of relying on a fixed delay.

Scroll in steps

Some pages reveal content progressively or respond to each wheel event. Send multiple awaited events and check for the result you need between steps:

for (let i = 0; i < 4; i++) {
  await page.mouse.wheel({ deltaY: 400 });
  // Wait for the page's expected update here if it loads content on scroll.
}

Use a condition tied to the page when possible, such as waiting for a known selector to appear. A fixed number of wheel events is not a reliable way to guarantee that you reached the bottom: page height can change, and lazy-loaded content can extend it.

Scroll a specific element with a locator

For an element-specific scroll, Puppeteer’s interactions guide provides locator scrolling:

await page.locator('.results-panel').scroll({
  scrollLeft: 0,
  scrollTop: 250,
});

The locator scroll method uses mouse wheel events. Its documented preconditions include bringing the target into the viewport, waiting for visibility, and waiting for its bounding box to remain stable over two consecutive animation frames. See the [Puppeteer page interactions guide](https://pptr.dev/guides/page-interactions).

Use a selector that identifies the scrollable element, and make sure the element actually has overflow content in the direction you want to scroll. If the target is inside nested scroll containers, verify which container receives the wheel event.

Choose the right scrolling method

Method Target Use it when Behavior to account for
page.mouse.wheel({ deltaY }) The page at the pointer position You need to dispatch wheel input, for example to exercise wheel-driven behavior. Mouse input is synthetic. The page and pointer position affect what responds.
page.locator(selector).scroll(...) A selected element You want locator-based scrolling of an element. The locator method has visibility and stable-bounds preconditions and uses wheel events.
page.evaluate(() => ...) Page state in the browser context You need to change or inspect scroll state directly, rather than simulate wheel input. This runs page-context JavaScript; it is a different mechanism from sending wheel input.

Puppeteer documents page.evaluate() as running a function in the page context. See [Page.evaluate()](https://pptr.dev/api/puppeteer.page.evaluate). For mouse input details and limitations, see the [Puppeteer Mouse class documentation](https://pptr.dev/api/puppeteer.mouse).

Important details and edge cases

  • Await the wheel call. It returns Promise<void>; awaiting it ensures the input dispatch has completed before your script continues.
  • Wheel input is not a fixed-distance scroll command. deltaY is the wheel input amount. The browser’s resulting scroll movement can depend on the page and its state.
  • The pointer position matters. Mouse coordinates and events are relative to the main-frame viewport. A wheel event over a nested scrollable area may scroll that area instead of the document.
  • Events are synthetic. Puppeteer’s mouse input does not fully reproduce a physical user’s mouse behavior. Validate pages whose logic depends on detailed input behavior in the browser and page you use.
  • Scroll may trigger asynchronous work. Infinite lists, lazy content, and animations can update after the wheel event. Wait for an observable condition before asserting or capturing a result.
  • The page may not be scrollable. A short document, a modal, a fixed layout, or a scrollable child can change what moves—or prevent visible movement.

Do-it-yourself screenshot after scrolling

If you need a screenshot of a page after wheel-driven content loads, send the wheel input, wait for the page’s expected state, then capture:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.mouse.wheel({ deltaY: 500 });
await page.waitForSelector('.loaded-results');
await page.screenshot({ path: 'after-scroll.png' });

Replace .loaded-results with a selector that appears when the content you need is ready. For a fixed layout, a short delay may be sufficient, but a page-specific condition is more reliable. Puppeteer’s [Page class documentation](https://pptr.dev/api/puppeteer.page) covers the page APIs.

Or skip the browser setup

If your goal is a clean screenshot rather than testing wheel interaction, [ScreenshotNeo](https://screenshotneo.com) can capture a URL with one API request. See the [ScreenshotNeo API docs](https://screenshotneo.com/docs/) for request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes known cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides screenshot tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

Troubleshooting

Symptom Likely cause What to do
The page does not visibly move. The page may not overflow, the pointer may be over a different scroll container, or the page may not yet be ready. Check the document and target element, confirm the intended area is under the pointer, and wait for the page to finish loading.
The wrong panel scrolls. A nested scrollable element is receiving wheel input. Move the pointer over the intended region before using mouse input, or target the element with page.locator(...).scroll().
The next step runs before new content appears. The wheel event completed, but the page’s asynchronous response has not. Wait for a selector or another observable page condition after scrolling.
Locator scrolling times out or cannot proceed. The locator may not match a visible element, or its bounding box may not stabilize. Check the selector and page state, make the target visible, and account for animations or layout changes.
The automation behaves differently from a physical mouse. Puppeteer sends synthetic mouse events and does not fully reproduce physical input. Validate the relevant behavior in the actual browser and page. Use page-context scrolling only if wheel input is not part of what you need to exercise.

Performance, reliability, and cost

A wheel call is a small browser interaction, but the total time usually depends on navigation and whatever the page does after scrolling. Avoid large fixed sleeps when a meaningful selector or state can signal readiness. For repeated scrolling, use only as many events as needed and check progress so the script does not keep sending input after the target content is reached.

For reliable automation, make the starting state explicit: navigate to the page, wait for the relevant content, scroll the intended region, then wait for the expected result. Synthetic input can behave differently across pages, so keep assertions tied to visible outcomes rather than assuming a specific pixel distance.

Puppeteer itself does not bill per wheel event. If the goal is simply obtaining screenshots, a screenshot API can avoid maintaining browser setup. ScreenshotNeo’s free and paid allowances and its billing headers make its capture charges visible; consult its docs for the current request configuration.

FAQ

Does deltaY: 100 scroll down?

Yes, a positive value requests downward wheel movement. The resulting page movement is controlled by the browser and page.

Can I use the mouse wheel to scroll horizontally?

The wheel options support horizontal and vertical deltas; use the documented wheel options for the installed Puppeteer version and verify the target container responds as expected.

Should I use page.evaluate() instead?

Use it when you need to change scroll state in page JavaScript. Use wheel input when the page behavior depends on receiving a wheel event.

Does locator scrolling scroll the whole page?

It targets the selected locator. The page interactions guide describes it as scrolling by mouse wheel and documents its visibility and stable-position preconditions.