Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For most Playwright actions, you do not need to scroll first: actions such as click() automatically scroll their target into view. When you need to make visibility explicit—for example, to trigger an infinite list, position a page before a screenshot, or test that an element is already reachable—call scrollIntoViewIfNeeded() on a locator.

Scroll a locator into view

The locator method scrollIntoViewIfNeeded() is the usual choice when your intent is “make this element visible.” It waits for actionability checks, then scrolls if the element is not completely visible according to its intersection with the viewport. It has been available since Playwright v1.14.

Use a semantic locator when possible, so the target is identified by what it is rather than by a fragile page position:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { test, expect } from '@playwright/test';

test('scroll to the pricing heading', async ({ page }) => {
  await page.goto('https://example.com');

  const target = page.getByRole('heading', { name: 'Pricing' });
  await target.scrollIntoViewIfNeeded();

  await expect(target).toBeVisible();
});

The method scrolls the element; it does not click it, assert its text, or guarantee that every part of it is unobscured by an overlay. Add the action or assertion your test actually needs.

Python

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page()
        await page.goto("https://example.com")

        target = page.get_by_role("heading", name="Pricing")
        await target.scroll_into_view_if_needed()
        await target.wait_for(state="visible")

        await browser.close()

Java

Locator target = page.getByRole(
    AriaRole.HEADING,
    new Page.GetByRoleOptions().setName("Pricing")
);
target.scrollIntoViewIfNeeded();

.NET

var target = Page.GetByRole(AriaRole.Heading, new() { Name = "Pricing" });
await target.ScrollIntoViewIfNeededAsync();

The language bindings expose the same basic operation with their own naming conventions. In JavaScript and TypeScript the call is scrollIntoViewIfNeeded(); in Python it is scroll_into_view_if_needed(); in .NET it is ScrollIntoViewIfNeededAsync().

Do clicks scroll automatically?

Usually, yes. Playwright says that “Most of the time, Playwright will automatically scroll for you before doing any actions.” A typical click can therefore be written directly:

await page.getByRole('button', { name: 'Submit' }).click();

The click performs the necessary scrolling as part of the action when the target is outside the viewport. An explicit scroll immediately before the click is generally redundant unless the scroll itself is what the test is checking, or you need to trigger content loading or establish a particular position independently of the action.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some action APIs expose a scroll option. Where supported, setting scroll: 'none' disables automatic scrolling; the action then fails if the element is not already in the viewport. This is useful for a deliberate no-scroll test, not as the normal way to click an off-screen element.

Choose the scrolling method that matches the intent

Method Best for What it controls
locator.scrollIntoViewIfNeeded() Making a specific element visible, triggering a bottom sentinel, or preparing a target for a separate step Semantic target visibility; concise and locator-based
page.mouse.wheel(deltaX, deltaY) Simulating a user wheel gesture Physical wheel input; the pointer position and active scroll owner matter
locator.evaluate(...scrollTop...) Moving a known scrollable container by a specific amount Explicit scroll-position change on that element

Prefer locator scrolling for ordinary visibility. Use wheel input when the user-like gesture matters. Adjust scrollTop directly when you know the nested element that owns scrolling and want a defined pixel change.

Scroll a nested container or infinite list

A page can contain independently scrolling regions, such as a chat panel, modal, or product list. First identify the container that actually scrolls. A wheel event affects the region under the mouse pointer, so hover that container before sending wheel input:

const container = page.getByTestId('scrolling-container');
await container.hover();
await page.mouse.wheel(0, 10);

This models user input, but it is not an exact way to request a particular final position: the page and browser determine how the gesture is handled. For a known container and a controlled increment, use its scroll position instead:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.getByTestId('scrolling-container')
  .evaluate(element => element.scrollTop += 100);

Change the increment to suit the test. A fixed increment moves that container; it does not prove that an item loaded, so follow it with an assertion on the expected content.

Triggering an infinite list

For an infinite list, scroll a locator for a bottom sentinel, footer, or other element that appears at the end of the loaded content:

const footer = page.getByRole('contentinfo');
await footer.scrollIntoViewIfNeeded();
await expect(page.getByText('Next batch of results')).toBeVisible();

Playwright’s guidance describes finding an element to make visible at the bottom and scrolling it into view as the most reliable approach for this case. Scrolling to the bottom target can trigger the site’s load-more behavior; assert the new content rather than assuming that scrolling alone means the request completed. If the target disappears or the list reflows during loading, locate the current sentinel again before the next scroll.

Position a page before a screenshot

To capture a particular section, scroll the relevant locator into view immediately before taking the screenshot. This is helpful when the screenshot needs a deliberate viewport position rather than a full-page capture. The target may still be partly obscured by a sticky header or another overlay, so inspect the resulting composition and adjust the page or test setup if necessary.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const section = page.getByRole('heading', { name: 'Pricing' });
await section.scrollIntoViewIfNeeded();
await page.screenshot({ path: 'pricing.png' });

If the page can reflow—for example, as images or results load—perform the scroll close to the assertion or capture that depends on it. That reduces the chance that the target moves between positioning and use.

Reliability and locator guidance

  • Prefer semantic locators. Use getByRole(), getByText(), or getByTestId() when they identify the intended target clearly. Avoid relying on brittle CSS or XPath solely to describe where an element happens to sit.
  • Scroll the element you mean to reveal. For an infinite list, that is often a bottom sentinel or footer; for a nested panel, it may be an item within the panel.
  • Identify the scroll owner. If scrolling the page does not move a nested list, hover the intended container and use the wheel, or adjust that container’s scrollTop.
  • Keep scroll and action close together. A page that changes layout can move the target again. Reacquire or scroll the locator just before the dependent assertion or action.
  • Handle detachment as a changing-page condition. If the element is removed while the page is updating, a related action can fail on detachment. Locate the current element again after the update instead of assuming an earlier DOM instance remains attached.
  • Reserve scroll: 'none' for an explicit check. It disables automatic scrolling where the action supports that option, so an off-screen target is expected to fail.

Troubleshooting scrolling problems

The click works without an explicit scroll

That is normal: Playwright automatically scrolls for most actions. Keep an explicit scroll only if visibility or page position is part of the test, or if you need to trigger loading before another step.

The nested list does not move

The page viewport may not be the active scroll owner. Hover the scrollable panel before page.mouse.wheel(), or call evaluate() on the panel itself to adjust its scrollTop.

The sentinel scrolls into view, but no items appear

Scrolling can trigger a site’s loading behavior, but it does not establish that the load succeeded. Wait for and assert the expected new item or other completion signal. If the list replaces its sentinel during the update, find the current bottom target again.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The target is visible but covered

scrollIntoViewIfNeeded() addresses whether the element is in view; it does not promise a particular screenshot composition around sticky headers or overlays. Check the captured viewport and account for the page’s own fixed elements where needed.

The action fails after scrolling

Check whether the page reflowed or detached the target during the operation. Reacquire the locator after the update and perform the scroll immediately before the assertion or action. If you used scroll: 'none', remove it unless the test is specifically meant to fail when scrolling would be required.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a page rather than test browser scrolling behavior, ScreenshotNeo provides a screenshot API. It returns an image or PDF from one GET request; the API options also include full-page capture, element capture by CSS selector, and waiting for a selector, delay, or network idle. This is not a replacement for a Playwright test when you need to verify scroll behavior or application interactions.

For example, this cURL request captures the Stripe home page as WebP:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options and authentication details. The API can remove cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. Its MCP server offers screenshot tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for free and try ScreenshotNeo.

Frequently Asked Questions

Does scrollIntoViewIfNeeded() work with a locator inside a nested scrolling element?

Yes. It is locator-based; if you need to control a known container’s exact movement, adjust that container’s scrollTop instead.

Which Playwright version added scrollIntoViewIfNeeded()?

The Locator API lists the method as available since Playwright v1.14.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.