Back to Blog

Mobile Web Emulation with Playwright: Testing Responsive Design and Mobile UX

Master mobile web testing with Playwright's powerful device emulation capabilities. Learn to test responsive layouts, touch interactions, geolocation, network conditions, and mobile-specific features across iPhone, Android, and tablet viewports�all without physical devices.

Scanly App

Published

9 min read

Reading time

Related articles: Also see testing touch gestures and device-specific interactions, a cross-browser strategy to pair with mobile emulation, and which framework handles mobile emulation best in 2026.

Mobile Web Emulation with Playwright: Testing Responsive Design and Mobile UX

As of 2026, mobile devices account for over 60% of global web traffic. On e-commerce sites, that number climbs to 75%. Yet many development teams still test primarily on desktop and only check mobile as an afterthought�often discovering critical bugs in production.

The good news? You don't need a drawer full of iPhones and Android devices to test mobile experiences. Playwright's device emulation provides a powerful, cost-effective way to test responsive design, touch interactions, and mobile-specific features�all from your local development environment or CI/CD pipeline.

In this comprehensive guide, we'll cover:

  • Why mobile web testing matters and common mobile-specific issues
  • Playwright's device emulation capabilities and configuration
  • Testing responsive layouts and breakpoints
  • Simulating touch gestures, geolocation, and network conditions
  • Best practices for mobile web testing
  • When to use real devices vs. emulation

Whether you're a QA engineer, frontend developer, or no-code tester, this article will help you ensure your web app delivers an exceptional experience on every device.

Why Mobile Web Testing Matters

The Mobile-First Reality

  • 60% of web traffic is mobile (Statista, 2026)
  • 53% of users abandon a site if it takes longer than 3 seconds to load on mobile
  • Google's mobile-first indexing means your mobile site affects your SEO ranking

Common Mobile-Specific Bugs

Issue Example Impact
Layout Breaks Text overflows on small screens Content unreadable
Touch Targets Too Small Buttons < 44x44px Usability issues, accidental clicks
Slow Performance Images not optimized for mobile High bounce rates
Unresponsive Nav Hamburger menu doesn't work on touch Users can't navigate
Form Input Issues Wrong keyboard opens (e.g., text instead of number) Friction, abandonment
Fixed Positioning Bugs Fixed header covers content Broken UX

Playwright's Device Emulation: How It Works

Playwright allows you to run tests in a virtual mobile browser by emulating:

  • Viewport size (e.g., 375x812 for iPhone 13)
  • User agent string (identifies the browser as mobile)
  • Device pixel ratio (for high-DPI displays)
  • Touch support (enables touch events)
  • Geolocation (simulates GPS coordinates)
  • Network conditions (throttles speed to 3G/4G)

Example: Basic Device Emulation

import { test, devices } from '@playwright/test';

test.use({ ...devices['iPhone 13'] });

test('should display mobile menu', async ({ page }) => {
  await page.goto('https://example.com');
  const menuButton = page.locator('button[aria-label="Open menu"]');
  await menuButton.click();
  await page.locator('nav.mobile-menu').waitFor();
});

This test runs in an emulated iPhone 13 with a 390x844 viewport, mobile user agent, and touch events enabled.

Testing Responsive Layouts

Viewport-Based Testing

Test your site at common breakpoints:

import { test, expect } from '@playwright/test';

const viewports = [
  { name: 'Mobile', width: 375, height: 667 }, // iPhone SE
  { name: 'Tablet', width: 768, height: 1024 }, // iPad
  { name: 'Desktop', width: 1920, height: 1080 }, // Full HD
];

for (const { name, width, height } of viewports) {
  test(`should render correctly on ${name}`, async ({ page }) => {
    await page.setViewportSize({ width, height });
    await page.goto('https://example.com');

    // Take a screenshot for visual regression testing
    await expect(page).toHaveScreenshot(`homepage-${name}.png`);
  });
}

Testing Hide/Show Elements at Breakpoints

test('should show hamburger menu on mobile, not on desktop', async ({ page }) => {
  // Mobile viewport
  await page.setViewportSize({ width: 375, height: 667 });
  await page.goto('https://example.com');
  await expect(page.locator('button.hamburger-menu')).toBeVisible();
  await expect(page.locator('nav.desktop-nav')).toBeHidden();

  // Desktop viewport
  await page.setViewportSize({ width: 1920, height: 1080 });
  await expect(page.locator('button.hamburger-menu')).toBeHidden();
  await expect(page.locator('nav.desktop-nav')).toBeVisible();
});

Testing Touch Interactions

Mobile users interact via touch, not mouse clicks. Playwright can simulate touch gestures:

Tap

test('should open product details on tap', async ({ page }) => {
  await page.goto('https://example.com/products');
  await page.locator('.product-card').first().tap();
  await expect(page).toHaveURL(/product\/\d+/);
});

Swipe (for carousels, sliders)

test('should swipe through image carousel', async ({ page }) => {
  await page.goto('https://example.com/product/123');

  const carousel = page.locator('.image-carousel');
  const box = await carousel.boundingBox();

  // Swipe left (from right to left)
  await page.mouse.move(box.x + box.width * 0.8, box.y + box.height / 2);
  await page.mouse.down();
  await page.mouse.move(box.x + box.width * 0.2, box.y + box.height / 2);
  await page.mouse.up();

  // Verify the second image is now visible
  await expect(page.locator('.carousel-item').nth(1)).toBeVisible();
});

Long Press

test('should show context menu on long press', async ({ page }) => {
  await page.goto('https://example.com');

  const element = page.locator('.item');
  await element.tap({ delay: 1000 }); // Long press (1 second)

  await expect(page.locator('.context-menu')).toBeVisible();
});

Emulating Device-Specific Features

Geolocation

test('should show nearby stores based on location', async ({ page, context }) => {
  // Grant geolocation permission
  await context.grantPermissions(['geolocation']);

  // Set location to New York
  await context.setGeolocation({ latitude: 40.7128, longitude: -74.006 });

  await page.goto('https://example.com/store-locator');

  await expect(page.locator('.store-list .store').first()).toContainText('New York');
});

Network Throttling (Slow 3G, Fast 3G, 4G)

test('should load gracefully on slow network', async ({ page, context }) => {
  // Emulate slow 3G
  await context.route('**/*', (route) =>
    route.continue({
      delay: 1000, // Add 1s delay to all requests
    }),
  );

  await page.goto('https://example.com');

  // Ensure loading spinner appears
  await expect(page.locator('.loading-spinner')).toBeVisible();

  // Wait for content to load
  await expect(page.locator('h1')).toBeVisible();
});

Playwright doesn't have built-in network throttling, but you can use Chrome DevTools Protocol (CDP):

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext();
const client = await context.newCDPSession(await context.newPage());

await client.send('Network.emulateNetworkConditions', {
  offline: false,
  downloadThroughput: 50 * 1024, // 50KB/s
  uploadThroughput: 20 * 1024, // 20KB/s
  latency: 100, // 100ms
});

Device Orientation (Portrait vs. Landscape)

test('should adapt layout to landscape orientation', async ({ page, context }) => {
  await page.goto('https://example.com');

  // Switch to landscape
  await page.setViewportSize({ width: 844, height: 390 }); // iPhone 13 landscape

  await expect(page.locator('.landscape-layout')).toBeVisible();
});

Testing Mobile Forms

Mobile forms have unique challenges: autocomplete, keyboard types, and input validation.

Ensure Correct Keyboard Opens

<!-- Email keyboard (@ symbol) -->
<input type="email" name="email" />

<!-- Numeric keyboard -->
<input type="tel" name="phone" />

<!-- Number keyboard with decimals -->
<input type="number" name="quantity" />

Test:

test('should open numeric keyboard for phone input', async ({ page }) => {
  await page.goto('https://example.com/checkout');
  const phoneInput = page.locator('input[type="tel"]');
  await phoneInput.focus();
  // Verify inputmode attribute or type
  await expect(phoneInput).toHaveAttribute('type', 'tel');
});

Test Autofill

test('should autofill address form', async ({ page, context }) => {
  await page.goto('https://example.com/checkout');

  // Simulate autofill by filling multiple fields at once
  await page.fill('input[name="address"]', '123 Main St');
  await page.fill('input[name="city"]', 'New York');
  await page.fill('input[name="zip"]', '10001');

  await page.click('button[type="submit"]');
  await expect(page).toHaveURL(/order-confirmation/);
});

Common Device Presets in Playwright

Playwright includes 40+ device presets:

Device Viewport User Agent Touch DPR
iPhone 13 390x844 Safari iOS 15 ? 3
iPhone 13 Pro Max 428x926 Safari iOS 15 ? 3
Pixel 5 393x851 Chrome Android ? 2.75
Galaxy S9+ 320x658 Samsung Internet ? 4.5
iPad Pro 1024x1366 Safari iPadOS ? 2

Usage:

import { devices } from '@playwright/test';

test.use({ ...devices['Pixel 5'] });

Full list: Playwright Device Descriptors

Best Practices for Mobile Web Testing

1. Test on Real Breakpoints Used in CSS

Don't just test arbitrary viewports. Match your CSS media query breakpoints:

/* Tailwind CSS defaults */
@media (min-width: 640px) {
  /* sm */
}
@media (min-width: 768px) {
  /* md */
}
@media (min-width: 1024px) {
  /* lg */
}

Test at 375px (mobile), 768px (tablet), 1024px (desktop).

2. Test Touch Interactions, Not Just Clicks

Use .tap() instead of .click() for mobile tests:

await page.locator('button').tap(); // Better for mobile

3. Check Performance on Slow Networks

Use network throttling to simulate real-world conditions (3G/4G).

4. Validate Touch Target Sizes

WCAG recommends touch targets be at least 44x44px. Test this:

test('buttons should be large enough for touch', async ({ page }) => {
  await page.goto('https://example.com');
  const button = page.locator('button.submit');
  const box = await button.boundingBox();
  expect(box.width).toBeGreaterThanOrEqual(44);
  expect(box.height).toBeGreaterThanOrEqual(44);
});

5. Use Visual Regression Testing

Take screenshots at multiple viewports and compare against baselines:

await expect(page).toHaveScreenshot('homepage-mobile.png');

When to Use Real Devices vs. Emulation

Use Emulation For:

  • Responsive layout testing: Quick feedback on breakpoints.
  • CI/CD pipelines: Fast, automated tests.
  • Early development: Iterative testing during feature development.

Use Real Devices For:

  • Touch gestures: Emulation doesn't perfectly replicate swipe mechanics.
  • Browser-specific bugs: Safari on iOS has quirks that WebKit emulation may miss.
  • Performance testing: Real device hardware affects performance.
  • Hardware features: Camera access, accelerometer, NFC.

Recommended Approach: 80% emulation (Playwright), 20% real device testing (BrowserStack, physical devices).

Mobile Testing in CI/CD

name: Mobile Web Tests

on: [pull_request, push]

jobs:
  mobile-tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        device: ['iPhone 13', 'Pixel 5', 'iPad Pro']
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '22'
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --project="${{ matrix.device }}"
      - uses: actions/upload-artifact@v3
        if: failure()
        with:
          name: playwright-report-${{ matrix.device }}
          path: playwright-report/

This workflow runs your tests on 3 different devices in parallel.

Conclusion

Mobile web testing is no longer optional�it's essential. With Playwright's powerful device emulation, you can test responsive design, touch interactions, geolocation, and mobile-specific features without needing a fleet of physical devices.

Start by emulating the most common devices (iPhone 13, Pixel 5, iPad), test at your CSS breakpoints, simulate touch gestures, and validate performance on slow networks. For critical flows, supplement with real device testing via BrowserStack or physical devices.

Ready to master mobile web testing? Sign up for ScanlyApp and integrate comprehensive mobile testing into your QA workflow.

Related Posts