Using React Spring with older browsers
In one of my recent projects I realized, just before launching, that the website was not rendered at all in some non-modern browsers. Specifically, I tested on an old iPad running Safari on iOS 9.3.5 (which I keep exactly for testing purposes) and on an emulation of IE11. The final result was the same: a blank page. This surprised me because in the early stages of the project, the page was rendered properly even on these devices. I checked the mobile Safari console and found this error:
SyntaxError: Unexpected keyword ‘const’. Const declarations are not supported in strict mode.
This was rather unexpected, since in theory Babel was supposed to compile all my code to ES5 compatible code, and const
should be automatically transpiled to var
. Therefore I realized that this must be coming from one of the external dependencies that I’m using.
After some debugging I figured out that the culprit code was introduced by React Spring, the excellent library that is taking care of all my animations.
It seems in fact that React Spring is compiled in order to support only relatively modern browsers. This is confirmed by the fact that even their website in not rendered at all with the above mentioned browsers. I did a little bit of research and found some info about it in the docs:
The library comes as an es-module compiled for evergreen browsers with the following browserlist config:
>1%, not dead, not ie 11, not op_mini all
. If you need to support legacy targets or deal with targets that don't support modules, you can use the commonJS export by simply appending.cjs
to your imports.
However the suggested solution didn’t work at all for me: by inspecting the compiled bundle I was still able to find const
and other ES6 syntax that will not work in the browsers that I’m trying to target.
How I managed to solve the issue
My Webpack configuration was excluding node_modules
on purpose, since including all the modules can be an expensive process and I was starting from the assumption that my modules should be runnable without transpiling. My config looked like this:
I didn’t want to remove the exclusion, since the issue is caused specifically by one module. Therefore I decided to keep this exclusion but add an exception for react-spring, something that I could achieve with this regex:
exclude: /node_modules\/(?!react-spring)/
After the update, a new error was introduced on the build:
Module not found: Error: Can’t resolve ‘source-map-loader’
Parsed request is a module
This was an easy one to fix: source-map-loader
was missing in my package.json, and I fixed it by just running: yarn add source-map-loader
Now the build works, but something is still not right: the page doesn’t render in my target browsers yet and I can still find the ES6 syntax in the build file.
Here comes the tricky part: my Babel configuration was in a .babelrc
file and it turns out that with this setup, transforms are not applied to linked modules. This seems to be a known issue.
The solution was surprisingly easy: I just had to move the content of .babelrc to a babel.config.js file.
But still, it was not enough: even if I couldn’t see any const
anymore in the build code, I now had a different console error on iOS 9.3.5:
TypeError: Object.entries is not a function. (In ‘Object.entries(o)’, ‘Object.entries’ is undefined)
Object.entries
was introduced in iOS 10.3 and it’s definitely not supported in IE11. So, we need to apply a polyfill to make this work. In my case I decided to use core-js: first I ran: yarn add core-js
and then, at the top level of my application (which in my case is App.tsx), I added:
import 'core-js';
After this last change, I executed again the production build and the whole page was finally able to load properly in both target browsers.