Playwright’s mobile emulation is surprisingly powerful, letting you test your web apps on anything from a tiny feature phone to a large tablet, all from your desktop.

Let’s see it in action. Imagine you’re testing a responsive e-commerce site. You want to make sure the product carousel looks good on a Pixel 5 and that the checkout process is usable on an iPad Mini.

Here’s how you’d launch Playwright in headed mode, targeting a Pixel 5, and navigate to your site:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,
        channel="chrome", # Or "msedge"
        args=[
            "--user-agent=Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36",
            "--window-size=393,879",
            "--force-device-scale-factor=2.25"
        ]
    )
    context = browser.new_context(
        device_scale_factor=2.25,
        screen={"width": 393, "height": 879},
        viewport={"width": 393, "height": 879},
        user_agent="Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36"
    )
    page = context.new_page()
    page.goto("https://your-ecommerce-site.com")
    # ... perform your tests ...
    browser.close()

The browser.new_context method is where the magic happens. You’re not just setting a viewport size; you’re telling Playwright to mimic a specific device’s characteristics.

  • device_scale_factor: This is crucial. It tells the browser how many device pixels correspond to one CSS pixel. A Pixel 5 has a 2.25x scale factor, meaning its physical pixels are much denser than CSS pixels. Getting this wrong means your layout might appear blurry or too small/large.
  • screen and viewport: These define the physical screen dimensions and the area the browser can actually render content within, respectively. For mobile emulation, they are often the same.
  • user_agent: This string identifies the browser and device to the web server. Websites often serve different content or styles based on the user agent. Playwright provides predefined device configurations that bundle these settings for you, which is often more convenient than manually setting each one.

You can also use Playwright’s built-in device descriptors, which simplifies this considerably. Instead of manually crafting the args, user_agent, screen, viewport, and device_scale_factor, you can use p.devices['Pixel 5'].

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    # Using a predefined device descriptor
    pixel_5 = p.devices['Pixel 5']

    browser = p.chromium.launch(headless=False)
    context = browser.new_context(**pixel_5) # Unpack the dictionary
    page = context.new_page()
    page.goto("https://your-ecommerce-site.com")
    # ... perform your tests ...
    browser.close()

This pixel_5 object is a dictionary containing all the necessary configurations:

{'user_agent': 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36',
 'viewport': {'width': 393, 'height': 879},
 'screen': {'width': 393, 'height': 879},
 'device_scale_factor': 2.25,
 'is_mobile': True,
 'has_touch': True}

Notice is_mobile and has_touch. These aren’t just for display; they influence how JavaScript events are handled. For instance, has_touch tells the browser to fire touch events, not just mouse events, which is critical for testing swipe gestures and tap interactions.

The actual rendering engine within the browser (Chromium, Firefox, WebKit) is still running on your desktop. Playwright intercepts the browser’s rendering commands and scales them to match the emulated device’s resolution and pixel density. This means you get an accurate representation of how your site will appear and behave on the target device, without needing to run a separate VM or use a physical device.

What trips most people up is not understanding the interplay between viewport and device_scale_factor. A common mistake is setting a large viewport width, expecting it to fill a large tablet screen, but then forgetting to adjust the device_scale_factor. This results in elements that are physically tiny on the emulated screen, even if they fit within the viewport dimensions. The device_scale_factor dictates the "effective DPI" of the emulated device, ensuring that elements render at the correct physical size.

After you’ve mastered device emulation, you’ll want to explore how Playwright handles touch actions and gestures programmatically.

Want structured learning?

Take the full Playwright course →