How to Test a Website’s Tablet Layout with Screenshot Comparisons
Use repeatable tablet viewports, reviewed Playwright baselines, and controlled captures to catch layout changes. Learn what emulation can—and cannot—verify.
Test a tablet layout by capturing the page at an explicit tablet viewport, comparing it with a reviewed reference screenshot, and inspecting every meaningful difference before updating the reference. Playwright Test can automate this with expect(page).toHaveScreenshot(); Chrome DevTools is useful for interactive breakpoint checks. Keep browser and capture conditions consistent, and use a physical tablet when a behavior may depend on real hardware.
A 768-pixel-wide viewport is a useful starting point because Chrome DevTools includes a 768 px Tablet preset, but it is not a universal definition of tablet. Choose widths from your product’s supported devices and CSS breakpoints. [Chrome DevTools device mode]
1. Choose tablet viewports that exercise your breakpoints
Start by listing the CSS media-query breakpoints that affect the page: navigation changes, grid column counts, sidebars, typography, spacing, and controls are common examples. Test widths just below and above important breakpoints as well as representative tablet widths. Include portrait and landscape dimensions if both orientations are supported.
| Capture | What it helps check |
|---|---|
| Representative portrait viewport | Tablet composition at a commonly supported narrow width. |
| Representative landscape viewport | Whether wider layouts, navigation, and columns fit as intended. |
| Just below and above a CSS breakpoint | Unexpected jumps, overflow, or a layout that changes at the wrong width. |
| Full-page capture | Below-the-fold sections, sticky elements, long grids, and page-height issues. |
Document the viewport width and height, orientation, device scale factor (DPR), touch setting, browser engine, and browser version for each test. Playwright device presets can set properties such as viewport, screen size, user agent, and touch behavior; override preset dimensions when your project needs a different size. [Playwright emulation]
2. Set up a repeatable Playwright screenshot test
Install Playwright Test and its browser, then create a test with an explicit viewport. The example below uses Chromium and tests both representative portrait and landscape sizes. It waits for the page to load, disables CSS animations during the screenshot assertion, and records a baseline on the first run. Review that baseline before treating it as the expected result.
npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium
Create playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
use: {
browserName: 'chromium',
baseURL: 'http://127.0.0.1:3000',
viewport: { width: 768, height: 1024 },
deviceScaleFactor: 1,
isMobile: false,
hasTouch: true,
reducedMotion: 'reduce',
},
webServer: {
command: 'npm run dev -- --host 127.0.0.1',
url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI,
},
});
Adjust the web server command and URL to your application. For a static site or an already-running test server, remove the webServer block and set baseURL accordingly. deviceScaleFactor controls pixel density, while hasTouch controls touch-event support; neither makes a desktop browser equivalent to physical tablet hardware.
Create tests/tablet-layout.spec.ts:
import { test, expect } from '@playwright/test';
const tabletViewports = [
{ name: 'portrait', width: 768, height: 1024 },
{ name: 'landscape', width: 1024, height: 768 },
];
for (const viewport of tabletViewports) {
test(`tablet layout: ${viewport.name}`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await page.goto('/products', { waitUntil: 'networkidle' });
await expect(page).toHaveScreenshot(`products-tablet-${viewport.name}.png`, {
fullPage: true,
animations: 'disabled',
maxDiffPixelRatio: 0.005,
});
});
}
Run the test with npx playwright test. On its first run, Playwright creates reference screenshots. Inspect them and commit the approved files with the test. On later runs, Playwright captures again and compares the result to the stored reference. The screenshot assertion waits for two consecutive screenshots to match before saving, which helps avoid capturing a page that is still settling. [Playwright visual comparisons]
The example uses networkidle as a simple readiness condition. Some applications keep analytics, polling, or streaming requests open, so that condition can stall. In those cases, wait for a meaningful page element instead, for example await page.getByRole('main').waitFor(), or wait for the specific content that determines the layout. A page load event alone does not guarantee that client-rendered content or images have finished changing.
Use a device preset when device properties matter
If your layout branches on touch capability, user agent, or other device properties, use a Playwright device descriptor and then override the viewport to match your target. Device descriptors are browser emulation settings, not a substitute for testing the actual device.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'tablet-emulation',
use: {
...devices['iPad (gen 7)'],
viewport: { width: 768, height: 1024 },
},
},
],
});
Check the available descriptor names in the installed Playwright version. For ordinary responsive layout tests, explicit viewport dimensions and the device properties your code actually depends on are easier to reason about than relying on an unexplained preset. [Playwright emulation options]
3. Review screenshot differences without hiding regressions
When a comparison fails, open the actual image and the diff artifact. Decide whether the change is an intended design update or a regression. Update the baseline only after that review; otherwise, a broken layout can become the new expected image.
- Look for clipped content, horizontal scrolling, unexpected wrapping, overlapping elements, missing images, and changed spacing.
- Check both the viewport composition and, when relevant, the full page. A full-page image can expose lower-page layout changes, while a viewport-only image focuses on what appears on screen at initial load.
- Use a small pixel-difference tolerance only for unavoidable rendering variation. A generous threshold can conceal real changes.
- Use masks or injected styles only for content that is truly irrelevant to the test. Masking a banner, navigation area, or changing-height component can hide the very layout shift the test should catch.
Playwright supports screenshot controls such as disabling or fast-forwarding animations, masking selected locators, and applying styles to the page during capture. Use those controls to reduce known noise, not to erase the region under test. [Screenshot assertion options]
4. Use Chrome DevTools for interactive breakpoint checks
- Open the page in Chrome and launch DevTools.
- Turn on Device Mode and select the Tablet preset or enter your target width and height.
- Switch between portrait and landscape and inspect the page at the CSS breakpoints that affect its layout.
- Use DevTools’ breakpoint controls to reveal and trigger the page’s responsive breakpoints.
- Capture a screenshot from the DevTools command menu when you need a visual record, then compare it with the approved reference.
Device Mode is convenient for exploring a layout and finding breakpoint problems. Its tablet preset is a starting point; record the dimensions you actually used so another developer can repeat the capture. [Chrome DevTools device mode]
5. Keep captures stable across runs
Screenshot comparisons are most useful when the capture environment remains consistent. Rendering can differ across operating systems, browser versions, hardware, browser settings, and headless modes. Pin the browser version in CI where practical, and keep the operating system and capture settings stable. [Playwright snapshot guidance]
- Animations: Disable them for layout checks when animation timing is not under test. Keep them enabled for tests specifically about animated behavior.
- Dynamic content: Use deterministic test data, freeze timestamps where the app permits it, and avoid random content. Mask a volatile value only if its dimensions and position are not part of the test.
- Fonts and images: Wait for the fonts and important images used in the layout. A late font swap can change line wrapping and page height.
- Network conditions: Use stable test fixtures and avoid relying on third-party content that can change or fail independently.
- Baseline ownership: Review visual diffs in code review and update references alongside the intentional UI change.
For a focused component check, capture a locator instead of the whole page: await expect(page.locator('[data-testid="product-grid"]')).toHaveScreenshot('product-grid-tablet.png'). This can make failures easier to diagnose, but keep a page-level check too when overall spacing, navigation, or overflow matters.
6. Decide when emulation is enough
Emulated viewport screenshots are well suited to checking responsive CSS, composition, text wrapping, and overflow at repeatable dimensions. They do not run the page on a tablet and cannot reproduce every hardware characteristic or browser interaction. If the question depends on touch behavior, device-specific browser behavior, performance, or hardware, inspect the page on a physical tablet as well. Chrome’s documentation advises using a mobile device when in doubt. [Chrome DevTools device mode]
Choose browser coverage based on the browsers your site supports. Playwright can run projects with Chromium, WebKit, and Firefox, as well as selected branded Chrome and Edge channels. Keep the browser version consistent when comparing a baseline over time, and use separate references when intentionally validating different engines. [Playwright browsers]
7. Or skip the browser setup
For a one-off capture or a repeatable screenshot outside your local browser harness, ScreenshotNeo is a website screenshot API and MCP server. A GET request returns an image or PDF. Its API documentation describes the request options, including viewport and device presets, full-page capture, and caching.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
These examples capture the supplied URL; set the target URL to your page and add the documented viewport options to match the tablet size you are checking. Save the returned image as the reference or actual capture in your comparison workflow, and review changes before replacing a baseline. ScreenshotNeo removes known cookie and consent banners, newsletter popups, and chat widgets before capture; each 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 gives AI agents tools for screenshots, page information, and PDF capture.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; all features are available on every plan. Sign up for ScreenshotNeo’s free plan and get 1,000 screenshots a month with no card.
8. Troubleshooting screenshot comparisons
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot differs on every run | Animation, dynamic data, late fonts, changing images, or a different capture environment. | Disable irrelevant animation, make test data deterministic, wait for layout-critical content, and keep browser and operating system settings stable. |
The test hangs at networkidle |
The page maintains ongoing requests such as analytics or polling. | Wait for a meaningful selector or application-ready condition rather than network idleness. |
| The baseline looks wrong after it was generated | The first-run reference was accepted without review, or the test captured before the page settled. | Inspect the page at the exact viewport, fix the readiness condition, and regenerate only after the result is correct. |
| Text wraps differently in CI | Fonts are missing or load differently, or the OS/browser differs from the baseline environment. | Install the required fonts, wait for them to load, and run comparisons in a consistent environment. |
| The tablet layout is not activated | The chosen width is on the wrong side of a site breakpoint, or code branches on device properties not configured in the test. | Inspect the site’s media queries; test widths on both sides and set relevant touch or user-agent properties explicitly. |
| Full-page screenshots show awkward seams or unexpected height | Sticky elements, lazy-loaded sections, or content that changes during scrolling. | Use a viewport screenshot for the initial composition, wait for content to load, and validate full-page capture behavior against the page’s sticky and lazy-loaded elements. |
| Many pixels differ after a browser update | Rendering changed with the browser or platform. | Review the diff as a possible rendering change, keep versions pinned for stable comparisons, and approve a new baseline only when the rendered change is acceptable. |
9. Performance, reliability, and cost
Keep the matrix small enough to run on every relevant change: cover supported tablet orientations and the breakpoints most likely to change composition. Add browser engines or more viewport sizes when they cover a stated support requirement or a known layout risk. Full-page captures and extra browser projects increase capture work, so use them where below-the-fold content or cross-browser behavior matters.
Reliable comparisons depend more on controlled inputs and reviewed references than on a large number of screenshots. Store the baseline with the code, inspect diffs before updating it, and rerun a failed capture in the same environment to distinguish a real change from noise. Local Playwright and DevTools do not require a screenshot API; ScreenshotNeo’s optional API has a free monthly tier and published paid tiers if a hosted capture path suits the workflow.
10. Frequently asked questions
Should I compare screenshots at one tablet width or several?
Use several when your supported tablet range crosses responsive breakpoints or supports both orientations. A single width cannot reveal a layout failure just across a breakpoint.
Should tablet tests use touch emulation?
Set touch capability when the application responds differently to touch input or device settings. For a CSS-only responsive check, a deliberate viewport is the essential starting point.
Should every visual change fail the build?
A visual diff should trigger review. The team can then decide whether the change is an intended update or a regression; automatic baseline updates remove that review step.
Can a screenshot prove that a page works on a real tablet?
No. It records a rendering under the capture environment. Use a physical tablet for device-dependent interaction or hardware behavior.


