How to Use a Fetch Polyfill in Cypress Tests
Cypress no longer supports experimentalFetchPolyfill. Learn when your app needs a fetch polyfill, how to stub fetch requests with cy.intercept(), and how to troubleshoot both.
Direct answer: Current Cypress does not support experimentalFetchPolyfill. Remove that setting. If your goal is to control or verify a fetch-backed request in a test, use cy.intercept(). Add a fetch polyfill only if the application must run in a browser that lacks native window.fetch, and load it in the application runtime before code calls fetch.
A polyfill and an intercept solve different problems: a polyfill supplies an API to application code; cy.intercept() observes or controls HTTP traffic for a test. Cypress recommends intercepting fetch requests in place of its removed option. See the Cypress migration guide and cy.intercept() documentation.
1. Decide whether you need a polyfill
First check the browsers your application supports and whether those browsers provide window.fetch. If they do, you usually do not need a polyfill just because Cypress tests exercise fetch. Use an intercept to make the test’s network behavior deterministic.
If a supported browser lacks fetch, install a polyfill through the application’s normal dependency and build process. Its package, import syntax, and browser configuration depend on your project; Cypress’s current documentation does not prescribe a particular polyfill package or a universal method for injecting one into the application window.
| Need | Use | Where it runs |
|---|---|---|
Provide fetch to a browser that lacks it |
An application fetch polyfill | Application runtime, before code calls fetch |
| Stub a response or inspect a request during a test | cy.intercept() |
Cypress test setup |
| Make a direct request from Cypress rather than the app | cy.request() |
Cypress test process |
2. Intercept a fetch request with cy.intercept()
Register the route before visiting the app or performing the action that triggers the request. Cypress intercepts matching network traffic whether the application used native fetch or a polyfilled fetch implementation.
describe('user list', () => {
it('loads users from the API', () => {
cy.intercept('GET', '/api/users', {
statusCode: 200,
body: [{ id: 1, name: 'Ada' }],
}).as('getUsers')
cy.visit('/')
cy.get('[data-cy=load-users]').click()
cy.wait('@getUsers')
.its('response.statusCode')
.should('eq', 200)
cy.get('[data-cy=user-name]').should('contain', 'Ada')
})
})
Use a fixture when the response belongs in a checked-in data file. Cypress supports serving fixtures through cy.intercept(); this avoids relying on a live backend response.
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers')
cy.visit('/')
cy.get('[data-cy=load-users]').click()
cy.wait('@getUsers')
Example cypress/fixtures/users.json:
[
{ "id": 1, "name": "Ada" },
{ "id": 2, "name": "Grace" }
]
For request-dependent behavior, use a route handler and inspect the intercepted request before replying. This is useful when the method, query, or submitted data is part of what the test needs to verify.
cy.intercept('POST', '/api/users', (req) => {
expect(req.body).to.deep.include({ name: 'Ada' })
req.reply({ statusCode: 201, body: { id: 3, name: 'Ada' } })
}).as('createUser')
cy.visit('/')
cy.get('[data-cy=name]').type('Ada')
cy.get('[data-cy=save]').click()
cy.wait('@createUser').its('response.statusCode').should('eq', 201)
Choose a narrow URL matcher that matches the application request. Cypress supports route matching by URL and other request properties; consult the intercept API reference for the matcher forms supported by your Cypress version.
3. Add a polyfill only for application browser support
When the application genuinely supports a browser without native fetch, configure the polyfill as part of the application’s own startup and build. It must be available on the application window before any application module calls fetch. The exact code depends on the chosen package and bundler, so use that package’s installation and import instructions and verify the resulting browser bundle.
- Confirm the browser support requirement that needs fetch compatibility.
- Select a polyfill compatible with your browser targets and build setup.
- Install it as an application dependency and load it before modules that make fetch calls.
- Run the app in the target browser and verify
window.fetchexists before startup code uses it. - Keep Cypress network control in the test, using
cy.intercept()to stub or inspect requests.
Do not assume that importing a polyfill in Cypress’s support file modifies the application under test (AUT). The support bundle is Cypress test setup; the app runs in its own browser context. Cypress documents support-file loading, but the reviewed official guidance does not provide a universal third-party polyfill injection recipe. Treat any test-only injection as project-specific and confirm that it reaches the AUT window before application startup.
4. Keep shared Cypress setup predictable
Cypress clears intercepts before each test. Define a route in the test that needs it or in a relevant beforeEach hook:
describe('user list', () => {
beforeEach(() => {
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers')
})
it('shows the users', () => {
cy.visit('/')
cy.get('[data-cy=load-users]').click()
cy.wait('@getUsers')
cy.get('[data-cy=user-name]').should('exist')
})
})
For hooks shared across specs, Cypress support files run before each spec. Keep their imports and setup lean: anything imported there is bundled and loaded for every spec. Avoid putting a test-only polyfill there unless you have confirmed it modifies the AUT’s window at the right time.
5. Troubleshoot fetch polyfills and intercepts
| Symptom | Likely cause | Fix |
|---|---|---|
experimentalFetchPolyfill is rejected or has no effect |
The old Cypress setting is no longer supported. | Remove it. Use cy.intercept() to handle application fetch traffic. |
| The request is not matched by the intercept | The route was registered after the triggering action, or the matcher does not match the actual URL or method. | Register the intercept first, then check the request URL, method, and matcher. |
| The request succeeds but the intercept does not fire | The browser may serve the response from cache, so it never reaches the network layer. | Check caching behavior and ensure the request reaches the network during the test. Cypress documents that cached responses do not pass through the network layer for interception. |
The app still reports that fetch is undefined |
The polyfill is missing from the AUT window, loads too late, or is configured in the wrong build path. | Load it through the application startup before code that calls fetch; verify it in the target browser context. |
A fetch from Electron’s initial about:blank page fails |
Cypress documents that Electron no longer permits fetch or XMLHttpRequest from the initial blank page before navigation. | Navigate with cy.visit() first, or use cy.request() for a direct Cypress-side request. |
| A request reaches the real backend unexpectedly | The route matcher did not match, or the route was not registered for this test. | Match the actual method and URL, define the route in the test or its beforeEach, and wait on the alias to confirm the expected request occurred. |
Relevant Cypress references: migration guide, intercept API, fixture API, and request API.
6. Performance, reliability, and maintenance
- Keep test setup focused. Put only shared hooks and imports in support files because they load for every spec.
- Prefer controlled responses for deterministic tests. Fixtures and explicit stub responses avoid dependence on a live service’s availability or changing data.
- Register routes before use. A correctly matched route in place before the application request is the key setup condition.
- Account for caching. A cached browser response bypasses the network layer and therefore cannot be observed by an intercept.
- Keep polyfills tied to browser support. A polyfill adds application code and can conceal a mismatch between the intended browser matrix and the actual runtime. Add one for a real compatibility requirement.
- Recheck version-specific behavior. Cypress and browser behavior can change; consult current Cypress documentation when upgrading.
Cost note: Cypress interception and fixture stubbing are test techniques; they do not require a separate network service for the example above. A real backend or external service may have its own usage costs, so stubbing can keep tests independent of those requests.
Or skip the browser setup
If your task is to capture a page rather than test its fetch behavior, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API docs.
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}`)
const bytes = new Uint8Array(await res.arrayBuffer())
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', bytes))
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed. Response headers report the page verdict and billing status.
- An MCP server gives AI agents tools for screenshots, page information, and PDF capture.
- Get 1,000 screenshots a month free with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
Frequently asked questions
Does Cypress still need experimentalFetchPolyfill?
No. The setting was deprecated and removed. Remove it from Cypress configuration.
Can cy.intercept() catch requests made by a fetch polyfill?
Yes. It handles matching network traffic; the app’s use of native fetch or a polyfilled implementation does not change the purpose of the intercept.
Should I polyfill fetch just for Cypress?
Usually no. Add a polyfill when the application’s supported browser environment requires it. Use intercepts to control network behavior in tests.
Can I make an intercept available to every test?
Put shared route setup in a suitable beforeEach hook, while remembering Cypress clears intercepts between tests.


