
Understanding Phantom window.resize Events in iOS

Something that I've been dealing with a lot recently whilst working on a web application that relies on displaying data on small screens is seemingly random or 'phantom' window.resize events from iOS devices. Unlike desktop browsers, where resize events are typically triggered by actual changes in window size, iOS Safari appears to exhibit a different behaviour, triggering resize events at random.
As web developers, we will often encounter unusual challenges like this in our line of work. It is fair to say that there must be some method to this madness: Apple isn't just triggering window.resize for fun!
Causes of phantom resize events
So, I did some experimenting, and here is what I think is causing these random events:
Dynamic Browser UI
I'm putting the main one first, because it's actually quite obvious when you think about it. In iOS Safari, the Browser interface is dynamic; the address bar and toolbars both react when a user scrolls the page, showing and hiding again to allow the user more screen real estate for the website itself.
When this happens, window.resize is triggered.
You'll have to excuse a little time‑hop here: I've returned to this article nearly seven years after it was first published to drop a video in to demonstrate this better. The original diagram I had here was not very good. So, please excuse the fact that this is showing an iOS 17 device, with iOS 10 having only just been released at the time of originally publishing this article.
The issue remains the same: watch the address bar at the bottom of the screen as the page is scrolled up and down. This is the source of our phantom resize events.
Viewport Adjustments
I've found that Safari will often also trigger a resize event when the viewport is adjusted by ‑ for example ‑ the virtual keyboard being shown or hidden. Again though: that makes sense. What makes less sense is that it also triggers when you switch between tabs...
Orientation Changes
This one should be self‑explanatory: the screen size does change between landscape and portrait. You can listen for this specifically by adding a listener to window:
window.addEventListener('orientationchange', (event) => { console.warn(`The screen orientation is now ${event.target.screen.orientation.angle}`);});User Interactions
window.resize triggers when user gestures like pinch‑to‑zoom are used.
Mitigating Phantom resize events
For the most part, these random additional events fly under the radar: it doesn't matter to the application or your user experience. However, there are edge cases where ‑ for example ‑ resize events might trigger additional functionality, whilst you really only intend for it to happen when the physical size of the screen has changed.
There is ‑ of course ‑ a simple solution: during a resize event, check the width of the window and if it has not changed, then disregard the event. Something like this:
const [windowWidth, setWindowWidth] = useState(window.innerWidth);useEffect(() => { const handleResize = () => { const { innerWidth } = window; if (innerWidth !== windowWidth) { setWindowWidth(innerWidth); // The window has resized. Do your resize stuff here } // This is a phantom resize event. Do nothing. }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize);}, [windowWidth])Here, all we are doing is:
- Initialising the
windowWidthstate with the currentwindow.innerWidth. - Creating a
useEffecthook, which sets an event listener for theresizeevent. - This triggers our
handleResizefunction, which checks the currentwindow.innerWidthagainst the width stored in state (windowWidth). - If they are different, we then reset our state item to the new window width and continue with whatever functionality we intend to trigger on a genuine resize event.
Wrapping up
And that's it! Some might find it annoying that iOS opts to trigger non‑event resize events so frequently, although I would argue that they do make sense ‑ even if the physical size of the screen doesn't change. Fortunately, it is pretty straightforward to detect these phantom events and deal with them accordingly.
Related Articles

For...in vs. for...of in JavaScript. Static Site Generators. Static Site Generators

Optimising gatsby‑image Even Further. Optimising
gatsby‑imageEven Further
Single or Double Colons in CSS Pseudo‑Elements (:before vs. ::before). Single or Double Colons in CSS Pseudo‑Elements (
:beforevs.::before)
Hiding Empty Elements with CSS. Hiding Empty Elements with CSS

Preventing and Debugging Memory Leaks in React. Preventing and Debugging Memory Leaks in React

Throttling vs. Debouncing in JavaScript: Managing Event Frequency. Throttling vs. Debouncing in JavaScript: Managing Event Frequency

Template Literals in JavaScript: Writing Multi‑Line Strings. Template Literals in JavaScript: Writing Multi‑Line Strings

Spread Syntax in JavaScript (...). Spread Syntax in JavaScript (
...)
Appending and Prepending Items to an Array. Appending and Prepending Items to an Array

How Inheritance Works in the JavaScript Prototype Chain. How Inheritance Works in the JavaScript Prototype Chain

Using next/link for Client‑Side Navigation. Using
next/linkfor Client‑Side Navigation