Skip to main content

Figma Dev Comparison

Figma Dev Comparison lets your Applitools Eyes tests use a Figma design directly as a visual baseline. Instead of manually re-approving a baseline every time a design changes, you link a test to a Figma frame, component, or component set. Eyes automatically detects when that design changes and compares your implementation against it, otherwise falling back to your last accepted baseline.

Why Use Figma Dev Comparison​

Keeping an implementation visually aligned with its design is normally a manual process. When a designer updates a Figma frame, someone has to notice the change, re-run the affected tests, and manually accept a new baseline before those tests are trustworthy again. That delay means visual differences between what was designed and what shipped can go unnoticed, and teams end up spending time on manual re-baselining instead of building.

Figma Dev Comparison closes that gap by making the design itself part of your test's baseline logic. Point a test at a live Figma URL, and Eyes resolves the design, sizes the test viewport to match it, and decides when a comparison against the design is actually needed, so differences between design and implementation surface automatically.

How It Works​

Figma Dev Comparison works by mapping each Eyes test's name to a Figma design URL, either by declaring a figmaBaselines map (test name → URL, or an array of URLs for multi-step tests) in your project configuration, or by calling the setup function directly in the test with a single URL. Before your test runs, Eyes looks up the test's name in that map and resolves the linked Figma node, using it to size the viewport and establish the baseline for comparison.

A test name that isn't found in the map is not an error. Eyes logs a note that no Figma baseline entry was found for that test, and the test simply runs as an ordinary regression test against its own accepted baseline — nothing about the test fails or is skipped because of it. If Figma "seems to do nothing," this is the first thing to check: the key in your map has to match the test name (or story name) exactly as Eyes resolves it, which varies slightly by SDK — see each code example below.

Mapping a test to an empty string ("") opts it out explicitly in the same way, and leaves any baseline environment name you've set yourself untouched. The one shape that is an error: a multi-step array made up of nothing but empty strings. A single "" is read as an intentional opt-out; an array of nothing else is read as a mistake, since there's no design anywhere to establish a baseline from. A non-figma.com URL also throws immediately as a validation error.

To make Figma API calls on your behalf, the integration requires a Figma personal access token with file_content:read scope. You can provide this by setting the FIGMA_ACCESS_TOKEN environment variable globally, in a .env file in your project, or by passing an accessToken value directly in figmaOptions when linking a test. If neither is set and offline/cache-only mode isn't enabled, the call fails with an error. Precedence is evaluated per setting, not per object — setting only mode in figmaOptions, for example, does not turn off the FIGMA_ACCESS_TOKEN environment-variable fallback for the token, or for anything else you didn't explicitly set.

By default, this reconciliation uses auto-baseline mode: a test compares against the linked Figma design only until an implementation is accepted for that test. Once accepted, that implementation becomes the baseline, and subsequent runs compare against it instead, skipping the design entirely, until the linked Figma design itself changes and a new comparison is accepted, at which point the test compares against the updated design again. This way, you get design-fidelity checks while a screen is being built, and fast, stable regression testing once it's done, without switching modes or editing your test code in between.

Every run of a Figma-linked test also triggers a short, throwaway Eyes test behind the scenes that renders the design and establishes it as the reference image; it's deleted immediately afterward, so you may briefly see an extra test appear and disappear in your dashboard. This is expected and not a sign of anything wrong.

Key Benefits​

  • Automatic design-drift detection: your tests compare against the Figma design only when it has actually changed, so you're not relying on someone to notice and manually re-baseline.
  • Minimal setup: a one-time setup call links a test to a design; the rest of your test stays the same.
  • Flexible comparison modes: choose whether a test should always follow the live design, always follow its last accepted baseline, or automatically switch between the two.
  • Multi-step test support: for tests with multiple checkpoints, map an ordered list of Figma URLs to steps, each cached and compared independently. Steps without a finished design yet can use a placeholder that compares against a blank canvas tagged (no design) so later steps keep their positions.
  • Accurate viewports, automatically: the browser/device viewport used for each test is sized to match the Figma design, so you don't have to configure it by hand.
  • Design context in your dashboard: every run captures the design's name, type, file revision, last-modified time, and the mode it ran under as searchable, filterable properties, plus a link on each step straight back to the Figma frame it was compared against.
  • Efficient by design: Figma API calls are batched per file (not per URL you link) and cached on disk, and rate-limit handling is built in, so the integration stays lightweight even across large test suites.

What's Supported Today​

  • Design-to-implementation comparison: Figma Dev Comparison compares a Figma design against your running implementation. It isn't a general design-QA tool, and it doesn't validate or edit Figma files.
  • Web-based SDKs and frameworks: support currently covers:
    • JavaScript/TypeScript: Playwright (Fixtures and Standard), Cypress, Storybook, Selenium, WebdriverIO
    • Java: Selenium, Playwright
    • Python: Selenium, Playwright
    • .NET: Selenium, Playwright
info

Coming later: native mobile support (iOS and Android) in Appium, XCUI, and Espresso is planned for a future release.

Configuration Options​

Use these options to control how Figma Dev Comparison authenticates, resolves designs, sizes viewports, and caches results.

OptionTypeDefaultDescription
accessTokenstring | undefinedFalls back to the FIGMA_ACCESS_TOKEN environment variableYour Figma personal access token, used to authenticate calls to the Figma API.
mode"figma-baseline" | "test-baseline" | "auto-baseline" | "disabled" | undefined"auto-baseline"Controls how comparisons are reconciled: auto-baseline compares against the design only when it has changed since the last accepted baseline; figma-baseline always compares against the current design, even if a newer non-Figma baseline was accepted since; test-baseline always compares against your accepted test baseline (the viewport and dashboard metadata are still taken from the design, but no design image is fetched); disabled turns the integration off entirely, and returns before even validating figmaBaselines. Equivalent to setting the APPLITOOLS_FIGMA_MODE environment variable — this option takes precedence if both are set. An unrecognized value throws.
maxViewportHeightnumber | undefined800The cap applied to the combined test viewport height, not each step individually. Eyes first sizes each step to its own design image (after any minViewportSize padding), then takes the bounding box across every real step — the widest width and the tallest height — and caps only that final height at this value. Width is never capped, since width is the form factor the design and the implementation have to share exactly, while a design's content can be arbitrarily tall.
minViewportSizenumber | null | undefinednull (no minimum)A single dimension applied independently to each axis of a step's viewport, not a width×height pair. Setting it to 320 means "at least 320 wide and at least 320 tall," so a 100×900 design becomes 320×900 and a 100×100 design becomes 320×320. A design already above the floor on both axes is untouched. This exists because the Ultrafast Grid rejects viewports below its own supported minimum, which a small, component-sized design (a single button or icon) commonly falls under; a design centered under this floor renders on a white canvas of the floored size.
maxRateLimitWaitSecondsnumber | undefined60The maximum total time, in seconds, to wait across all retries after Figma returns a rate-limit (429) response. Each retry honors the Retry-After value Figma returns; once the cumulative wait would exceed this budget (or a single Retry-After already does), the call fails rather than sleeping through it.
nodesCacheTtlSecondsnumber | undefined120How long, in seconds, design metadata (/nodes) responses are cached before being refreshed.
tempPathstring | undefinedos.tmpdir() (your system's default temp directory)Where cached design images and metadata are stored on disk. Rendered images are cached keyed by file + node + the Figma file's last-modified timestamp, so an edit in Figma invalidates only the affected entries automatically — there's nothing to clear by hand. Because last-modified is a property of the whole file, editing any part of a file re-renders every frame taken from it. All nodes from one Figma file are fetched in a single metadata call, and all cache misses render in a single image call, so API usage scales with the number of distinct Figma files you link, not the number of URLs.

Environment Variables​

These environment variables provide defaults and alternate ways to configure Figma Dev Comparison without changing code. Each is the fallback for the matching configuration option above, except APPLITOOLS_FIGMA_SKIP_API, which has no code-level equivalent — it's an offline/debug switch, not a per-test option.

VariableDescription
FIGMA_ACCESS_TOKENYour Figma personal access token. Used as a fallback when accessToken isn't passed in figmaOptions. Not required when APPLITOOLS_FIGMA_SKIP_API is set. This is the only variable without the APPLITOOLS_ prefix.
APPLITOOLS_FIGMA_MODESets the reconciliation mode: auto-baseline (default), figma-baseline, test-baseline, or disabled. Equivalent to the mode option; the option takes precedence if both are set. An invalid value throws an error.
APPLITOOLS_FIGMA_SKIP_APISet to 1 or true to skip all Figma API calls and serve results from the disk cache only; no access token is required when this is set. A cache miss under this flag throws an error rather than falling back to a live call. Useful for avoiding API quota usage after an initial warm-up run.
APPLITOOLS_FIGMA_TEMP_PATHFallback for the tempPath option, where the design-image and metadata cache lives on disk.
APPLITOOLS_FIGMA_NODES_CACHE_TTLFallback for the nodesCacheTtlSeconds option, in seconds.

Design Metadata & Dashboard​

Every run of a Figma-linked test (in every mode except disabled) attaches these properties, so you can search and filter for them in your dashboard:

PropertyValue
figma:nameNode name, e.g. "Login Screen"
figma:typeNode type, e.g. FRAME, COMPONENT
figma:file-revisionFigma file version id at the time of the fetch
figma:file-last-modifiedISO 8601 timestamp of the file's last edit
figma:modeThe reconciliation mode the run resolved to

figma:file-revision and figma:file-last-modified describe the Figma file, not the individual frame, so every frame taken from the same file reports identical values.

Each step of a Figma baseline is also tagged with the design's URL, so a reviewer can go straight from a test step in the dashboard to the exact frame it was compared against. Figma's t= share-tracking parameter is stripped from that tag, so re-copying a link doesn't rename the step and orphan its accepted baseline. A placeholder step — one mapped to an empty string in a multi-step array — is tagged (no design) rather than a URL.

Good to Know​

  • The design's size wins over anything you configure. Setting your own viewport size has no effect in any mode except disabled; see the Configuration Options table above for exactly how the size is derived. This holds true across all SDKs, including Storybook.
  • While you're iterating on an unfinished implementation, your test will have real differences from the design by definition, and most SDKs throw on a diff by default. Use your SDK's non-throwing close (for example, eyes.close(false), eyes.Close(false), eyes.close(raise_ex=False), or runner.getAllTestResults(false)), so those expected differences don't fail your build while a screen is still being built.
  • Figma rate-limits aggressively. If you're linking many designs from the same file, the batching described above keeps you well under typical limits; if you do hit one, raising maxRateLimitWaitSeconds gives retries more leeway before failing.

Getting Started​

To get started, ensure you have the latest Eyes SDK version and a Figma access token with the file_content:read scope.

  1. Add your Figma token as a FIGMA_ACCESS_TOKEN environment variable globally, in a .env file in your project, or set it via the accessToken configuration option.
  2. In Figma, right-click a design and select Copy/Paste as → Copy link to selection. Or, highlight/focus the design you want and copy the URL from the browser's address bar if using Figma web.
  3. Map your Figma design URLs to your Eyes test names (e.g., eyes.open(appName, testName), config.setTestName('testName'), or test('Playwright Fixtures testName', async (Page, Eyes)).
    • See the code examples below.
  4. Run your tests and view the results. Eyes will display the Figma design as the baseline on the left and compare it to your implementation on the right.
  5. Once you're happy with your design-to-implementation comparison, approve and save the implementation. This becomes your active baseline, and all subsequent executions will be compared against it.
    • However, if any of your designs change after you accept the implementation, this workflow will repeat and start comparing the design to the implementation again. You can disable this behavior by setting figmaOptions mode to either "test-baseline" or "disabled".
warning

You may not want this behavior when running your tests in CI. You can wrap a conditional around figmaOptions, such as figmaOptions: { mode: process.env.CI ? 'disabled' : 'auto-baseline' } (adjust to your language), or set this environment variable in your CI platform: APPLITOOLS_FIGMA_MODE='disabled'

Code Examples​

Verified code examples for each SDK are added here. In the meantime, reach out to your Applitools contact or support if you don't see example code for your language and framework.

// Implement globally in applitools.config.js

figmaBaselines: {
'Dashboard Screen': 'https://www.figma.com/design/...',
'Login Screen': [
'https://www.figma.com/design/...',
'',
'https://www.figma.com/design/...',
],
about: '',
},
figmaOptions: { mode: 'auto-baseline' }
// Implement in a specific spec

cy.eyesOpen({
appName: 'Hello World!',
testName: 'Login Screen',
figmaBaselines: {
'Login Screen': [
'https://www.figma.com/design/...',
'',
'https://www.figma.com/design/...',
],
},
figmaOptions: { mode: 'auto-baseline' },
});
note

figmaBaselines/figmaOptions passed to cy.eyesOpen replace applitools.config.js's values outright for that test, rather than merging field by field — if you set figmaOptions at the per-test level, include every field you need there, not just the one you're changing. The test-name key Eyes matches against for Cypress is the Mocha it(...) title (the enclosing describe isn't part of it); pass testName to cy.eyesOpen to pin it explicitly instead.