Testing

requestIdleCallback: Browser Support, Use Cases, Polyfill

requestIdleCallback works in Chrome 47+, Edge 79+, Firefox 55+, Opera 34+, and Samsung 5+. Safari needs a polyfill. Learn browser support and use cases.

Author

Prince Dewani

May 6, 2026

requestIdleCallback is a W3C JavaScript API that schedules a callback during the browser's idle time, off the main thread. It works in Chrome 47+, Edge 79+, Firefox 55+, Opera 34+, and Samsung Internet 5+, while Safari keeps it behind a feature flag on macOS and iOS and Internet Explorer has no support.

This guide covers what requestIdleCallback is, the browsers that support it, how it compares to setTimeout, the use cases, a polyfill pattern, and known issues.

What is requestIdleCallback?

requestIdleCallback is a W3C method on the Window object that queues a function to run when the browser has spare time at the end of a frame. The callback receives an IdleDeadline object whose timeRemaining method tells you how many milliseconds you can safely use before yielding back to rendering and input.

Which browsers does requestIdleCallback support?

requestIdleCallback runs by default in every Chromium-based browser, in Firefox, and in Samsung Internet, while Safari on macOS and iOS hide it behind a feature flag and Internet Explorer never added support.

Loading browser compatibility data...

requestIdleCallback compatibility in Chrome

Chrome supports requestIdleCallback by default from Chrome 47 on Windows, macOS, Linux, ChromeOS, and Android. Chrome 1 to 46 did not support it. The same Chromium engine ships in Chrome for Android, so desktop and mobile builds gained the API together.

requestIdleCallback compatibility in Edge

Microsoft Edge supports requestIdleCallback from Edge 79, the first Chromium-based release, on Windows, macOS, and Linux. Legacy Edge 12 to 18 on the EdgeHTML engine never added the Background Tasks API. Anyone still on legacy Edge needs the setTimeout polyfill from the section below.

requestIdleCallback compatibility in Firefox

Firefox supports requestIdleCallback by default from Firefox 55 on Windows, macOS, Linux, and Android. Firefox 53 and 54 had it disabled by default behind the dom.requestIdleCallback.enabled preference. Firefox 1 to 52 did not support requestIdleCallback at all.

requestIdleCallback compatibility in Safari

Safari does not support requestIdleCallback by default in any stable version. WebKit ships the implementation behind a feature flag, so Safari on macOS exposes it under Develop, Feature Flags, and Safari on iOS exposes it under Settings, Safari, Advanced, Feature Flags. Most production users will not flip these flags, so a setTimeout polyfill is required to cover Safari desktop and iPhone visitors.

requestIdleCallback compatibility in Opera

Opera supports requestIdleCallback from Opera 34 on Windows, macOS, Linux, and Android. Opera 9 to 33 did not support it. Opera Mobile follows the same Chromium baseline as Opera desktop, so the API is available on the modern mobile builds.

requestIdleCallback compatibility in Samsung Internet

Samsung Internet supports requestIdleCallback from version 5.0 on Galaxy phones and tablets. The browser is built on Chromium, so it follows the same scheduling rules as Chrome for Android. Samsung Internet 4 and earlier did not support the API.

requestIdleCallback compatibility in Android Browser

Chrome for Android supports requestIdleCallback from Chrome 47, the same milestone as desktop Chrome. Firefox for Android picked it up in Firefox 55. The legacy stock Android Browser, last shipped with Android 4.4 KitKat, never added the API and is fully retired on shipping devices.

requestIdleCallback compatibility in Internet Explorer

Internet Explorer does not support requestIdleCallback in any version. Microsoft never added the Background Tasks API to IE 9, 10, or 11, and the browser is now end of life. Anyone still on IE needs the setTimeout polyfill, since the API does not exist on the Window object.

Note

Note: requestIdleCallback breaks in Safari and on iOS without a polyfill. Test it on real browsers and OS with TestMu AI. Try TestMu AI free!

What is the difference between requestIdleCallback and setTimeout?

Both APIs schedule a function to run later, but only requestIdleCallback waits for the browser to be idle. setTimeout fires after a fixed delay no matter what the main thread is doing, so heavy work inside a setTimeout callback can still block input and rendering.

DimensionrequestIdleCallbacksetTimeout
TriggerFires when the browser is idle at frame endFires after the supplied delay in milliseconds
Time guaranteeBest effort, up to 50 ms slice per callYes, the callback runs after the delay elapses
Callback parameterIdleDeadline with timeRemaining and didTimeoutNone, the callback receives only the arguments you passed
Effect on input and animationYields to user input, scroll, and animation framesCan block input and animation when the work is heavy
Browser reachChrome 47+, Edge 79+, Firefox 55+, Opera 34+, Samsung 5+; Safari behind a flagUniversal, every browser since the IE 4 era
Best fitBackground work, analytics, prefetch, log batchingTimed delays, debouncing, animations, single-frame deferrals

What are the use cases of requestIdleCallback?

requestIdleCallback fits any task you can defer until the user pauses scrolling or typing. Production teams use it to keep the main thread free during page load and user interactions.

  • Analytics and telemetry batching: Queue page-view, error, and product events and flush them in one beacon call when the browser is idle, so the network tab does not compete with first paint.
  • Prefetching non-critical resources: Warm the cache for the next route, the next image set, or a heavy JSON payload during idle frames, so the navigation feels instant when the user clicks.
  • Lazy hydration of off-screen components: Hydrate non-visible widgets, like a footer carousel or a chat launcher, only when the browser has spare time, which trims the time-to-interactive metric.
  • Background indexing and cache warming: Build a client-side search index or warm an IndexedDB store across many idle slices, instead of one long task that locks the main thread.
  • Splitting heavy data parsing: Parse a large JSON or CSV payload in chunks of fewer than 50 ms each, using deadline.timeRemaining to decide when to yield.
  • Cleaning up DOM after navigation: Remove detached event listeners, observers, and orphan DOM nodes after a route change, so memory does not climb on long sessions.

How do you polyfill requestIdleCallback for Safari?

The standard polyfill wraps setTimeout in a function that mirrors the requestIdleCallback signature and returns a fake IdleDeadline. Add the shim once at app startup so every later call works on Safari, iOS, and any browser that lacks the native method.

  • Pick a bootstrap file: Choose the earliest script in your app, such as the entry point of your bundler or a small inline script in the document head, so the shim runs before any other code calls requestIdleCallback.
  • Feature-detect first: Read window.requestIdleCallback once and only assign the shim when it is undefined, so native Chrome, Edge, Firefox, Opera, and Samsung Internet calls go to the real implementation.
  • Wrap setTimeout with an IdleDeadline shape: Inside the shim, return setTimeout and pass the callback an object with didTimeout set to false and a timeRemaining method that subtracts elapsed time from a 50 ms cap.
  • Mirror cancelIdleCallback: Define a matching shim for window.cancelIdleCallback that calls clearTimeout, so existing code that cancels by ID still works after the polyfill loads.
  • Verify in DevTools: Open the DevTools console in Safari or any unsupported browser and run the snippet below, then confirm the log prints true.
// Polyfill requestIdleCallback for Safari and any browser without native support.
// Paste into your app bootstrap or a <script> tag in the document <head>.
window.requestIdleCallback =
  window.requestIdleCallback ||
  function (callback, options) {
    const start = Date.now();
    const timeout = (options && options.timeout) || 0;
    return setTimeout(function () {
      callback({
        didTimeout: false,
        timeRemaining: function () {
          return Math.max(0, 50 - (Date.now() - start));
        }
      });
    }, 1);
  };

window.cancelIdleCallback =
  window.cancelIdleCallback ||
  function (id) {
    clearTimeout(id);
  };

// Verify the shim is in place.
console.log("requestIdleCallback in window:", "requestIdleCallback" in window);

If the console prints false, the script ran in a worker scope or before window existed; move the snippet into the document head and reload.

...

What are the known issues with requestIdleCallback?

requestIdleCallback ships in most engines, but a few real edge cases still bite production teams. The biggest hits are Safari support, the 50 ms slice cap, iframe budget sharing, and the React 18 scheduler change.

  • Safari and iOS need a polyfill: Safari on macOS and iOS hides the API behind a feature flag, so any code path that depends on requestIdleCallback silently never runs for the majority of Apple users without a setTimeout shim.
  • The 50 ms slice is a soft cap: IdleDeadline.timeRemaining returns at most 50 milliseconds, even if the browser is idle for longer, which forces long workloads to chunk across many callbacks instead of one.
  • Idle budget is shared with iframes: The same idle budget covers the top frame and every same-origin iframe, so a noisy ad or analytics iframe can starve your callbacks of time.
  • No timeout means no run on busy pages: A heavy first-paint or a long-running animation can keep the page busy for many seconds, and a callback without a timeout option may never fire on slow devices.
  • React 18 dropped it from the scheduler: The React 18 scheduler switched from requestIdleCallback to a MessageChannel-based loop because requestIdleCallback fires at most once per frame, which was too slow for concurrent rendering.
  • DOM writes still cause jank: requestIdleCallback runs after layout but before the next frame, so a DOM mutation inside the callback triggers a synchronous reflow; pair it with requestAnimationFrame for the actual write.

In my experience, the most surprising failure happens on iPhone Safari. A team ships a clever idle-time prefetch on Chrome, ignores the Safari gap, and learns six months later that a third of their iOS users have been hitting a cold cache on every navigation. Always pair the API with a feature check and the setTimeout shim above, and verify the path on a real iPhone before you ship.

...

Citations

All requestIdleCallback version numbers and platform notes in this guide come from these primary sources:

Author

Prince Dewani is a Community Contributor at TestMu AI, where he manages content strategies around software testing, QA, and test automation. He is certified in Selenium, Cypress, Playwright, Appium, Automation Testing, and KaneAI. Prince has also presented academic research at the international conference PBCON-01. He further specializes in on-page SEO, bridging marketing with core testing technologies. On LinkedIn, he is followed by 4,300+ QA engineers, developers, DevOps experts, tech leaders, and AI-focused practitioners in the global testing community.

Open in ChatGPT Icon

Open in ChatGPT

Open in Claude Icon

Open in Claude

Open in Perplexity Icon

Open in Perplexity

Open in Grok Icon

Open in Grok

Open in Gemini AI Icon

Open in Gemini AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it free
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for free

Frequently asked questions

Did you find this page helpful?

More Related Hubs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests