# How Cypress Handles Viewport Resizing and Device Emulation: A Deep Dive into the Source Code

> Explore how Cypress handles viewport resizing and device emulation via the cy viewport command. Discover the source code and protocols that sync changes across Cypress layers.

- Repository: [Cypress.io/cypress](https://github.com/cypress-io/cypress)
- Tags: deep-dive
- Published: 2026-07-12

---

**Cypress resizes the browser viewport and emulates devices through the `cy.viewport()` command, which updates a global state object and synchronizes the change across the driver, server, and browser automation layers using CDP or Playwright protocols.**

The `cypress-io/cypress` repository implements viewport control through a sophisticated multi-stage pipeline that ensures consistent behavior across Chrome, Edge, Electron, and WebKit browsers. Understanding how Cypress viewport resizing and device emulation works under the hood helps you write more reliable responsive design tests and debug scaling issues in headless environments.

## How cy.viewport() Parses Device Presets and Dimensions

The `cy.viewport()` command accepts three distinct input formats, all processed in [`packages/driver/src/cy/commands/window.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/commands/window.ts).

### Supported Input Formats

- **Numeric pair**: `cy.viewport(1024, 768)` — width followed by height in pixels
- **Object syntax**: `cy.viewport({ width: 1024, height: 768 })` — useful for dynamic values
- **Named preset**: `cy.viewport('iphone-x')` — references internal device database

### The viewports Map and Preset Resolution

Inside [`window.ts`](https://github.com/cypress-io/cypress/blob/main/window.ts), Cypress maintains a **`viewports` map** containing common device dimensions (iPhone X, Samsung S10, iPad, MacBook, etc.). When you pass a preset string, the command looks up the default width × height string. An optional orientation parameter (`'landscape'` or `'portrait'`) swaps these dimensions before proceeding to the synchronization stage.

```javascript
// Preset with landscape orientation swaps width/height
cy.viewport('iphone-x', 'landscape')

```

## The Three-Stage Synchronization Pipeline

Cypress viewport resizing operates through three distinct stages that ensure the browser, driver, and UI remain synchronized.

### Stage 1: Command Parsing in window.ts

The implementation in [`packages/driver/src/cy/commands/window.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/commands/window.ts) validates inputs and resolves them to concrete pixel dimensions. The internal **`setViewportAndSynchronize`** function (lines 73-95) prepares the payload for the next stage.

### Stage 2: Global State and Event Emission

The resolved dimensions are stored in a **global `currentViewport`** object defined in [`packages/driver/src/cypress.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cypress.ts), ensuring the value persists across test runs. Cypress then emits the **`cy:viewport:changed`** action via `Cypress.action('cy:viewport:changed', viewport, ...)`.

The driver’s protocol layer listens for this action and forwards the payload to the server process through the **`protocol:viewport:changed`** socket message.

### Stage 3: Browser-Level Resize via Protocols

In [`packages/server/lib/socket-base.ts`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/socket-base.ts), the server receives the `viewportChanged` call and invokes browser-specific automation APIs:

- **Chrome / Edge**: Uses Chrome DevTools Protocol (CDP) commands `Emulation.setDeviceMetricsOverride` and `Browser.setWindowBounds`
- **Electron**: Uses the same CDP commands (Electron ships with Chrome DevTools)
- **WebKit**: Uses Playwright’s `setViewportSize` method

For headless runs, Cypress forces **`deviceScaleFactor = 1`** via the `--force-device-scale-factor=1` flag (implemented in [`packages/server/lib/browsers/chrome.ts`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/chrome.ts) and [`packages/server/lib/browsers/webkit-automation.ts`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/webkit-automation.ts)). This ensures logical pixel ratios match the declared viewport size, preventing "retina-scale" mismatches in screenshots.

## Code Examples for Common Use Cases

Use these patterns to control viewport dimensions in your test suites:

```javascript
// Set explicit dimensions
cy.viewport(1024, 768)

// Use a device preset (portrait default)
cy.viewport('iphone-x')

// Preset with landscape orientation
cy.viewport('iphone-x', 'landscape')

// Object syntax for dynamic values
const myWidth = 1200
const myHeight = 800
cy.viewport({ width: myWidth, height: myHeight })

```

After the browser confirms the resize, Cypress updates its internal `state(viewport)` and the test runner UI reflects the new dimensions. All subsequent commands—including screenshot capture—use these updated values.

## Device Emulation vs. Viewport Sizing

While Cypress can imitate real-world devices through the preset list, **viewport sizing differs from full device emulation**. When you call `cy.viewport('iphone-x')`, Cypress only manipulates the browser window size to match the iPhone X dimensions. It does **not** automatically modify the user-agent string, DPI settings, or touch event behaviors unless explicitly configured.

The forced `deviceScaleFactor` in headless mode guarantees that visual regression tests capture screenshots at a 1× device pixel ratio, eliminating cross-platform scaling discrepancies that would otherwise occur between retina and non-retina displays.

## Summary

- **`cy.viewport()`** accepts numeric dimensions, objects, or named presets with optional orientation parameters
- The **`viewports` map** in [`packages/driver/src/cy/commands/window.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/commands/window.ts) defines preset dimensions for common devices
- Changes flow through the **`cy:viewport:changed`** action to **`protocol:viewport:changed`** socket messages
- Browser automation uses **CDP** for Chromium-based browsers and **Playwright** for WebKit
- Headless mode forces **`deviceScaleFactor = 1`** to ensure consistent screenshot scaling
- Device presets resize the window but do **not** automatically emulate user agents or touch behaviors

## Frequently Asked Questions

### Does Cypress automatically change the user agent when using device presets?

No. Cypress only resizes the browser window to match the preset dimensions. According to the source code in [`packages/driver/src/cy/commands/window.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/commands/window.ts), the `cy.viewport()` command does not modify the user-agent string, DPI, or touch event capabilities. You must manually set these values if your application relies on user-agent detection for mobile rendering.

### How does Cypress handle viewport resizing in headless mode?

In headless mode, Cypress forces a **deviceScaleFactor of 1** using the `--force-device-scale-factor=1` flag. This implementation in [`packages/server/lib/browsers/chrome.ts`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/chrome.ts) and [`packages/server/lib/browsers/webkit-automation.ts`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/webkit-automation.ts) ensures that screenshots and visual diffs are captured at the logical pixel ratio matching your declared viewport size, preventing retina-scale mismatches between local and CI environments.

### What is the difference between cy.viewport() and a mobile device preset?

The **`cy.viewport()`** command is the API you call in tests, while **device presets** are pre-defined width/height pairs stored in the `viewports` map. When you pass a preset name like `'iphone-x'`, Cypress extracts the dimensions (375×812) and passes them to the same resize logic used for explicit numeric values. Presets simply provide convenient aliases for common device dimensions.

### Where does Cypress store the current viewport dimensions?

Cypress stores the current dimensions in a **global `currentViewport`** object within the driver state ([`packages/driver/src/cypress.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cypress.ts)). This object survives across test runs and is updated via the `setViewportAndSynchronize` function. The server process accesses these values through the `protocol:viewport:changed` socket message to coordinate browser-level resizing.