firebase.js is SO DAMN HUGE!

And what can we do about it.

Roman Dubinin
3 min readJul 2, 2017

It’s indeed huge. Just look at it:

Red Rectangle — firebase; outside of Red Rectangle — all other code for production ready online store.

This thing is 103kb minified and gzipped! It’s bigger than our application (58kb) and can be compared with all other vendor libraries combined (156kb) — and mind you, our vendor.js includes react, react-dom, react-router, moment.js, lodash, polyfills and tons of other stuff.

Worst thing — it doesn’t even need to be loaded all the time and almost never need to be loaded on initial page load.

So that can we do about this?

Not much as it turns out.

Requiring separate packages does not help (for some reason webpack wasn’t handling it right). But even if it does — not much help as we need both auth and database — (42kb + 40kb and 3kb for ‘app’ required module). And as you can see on the screenshot above — they are not modular at all. So best we can do anyway is 85kb. And it’s already minified beyond any comprehension.

Fun fact — it’s compressed by Closure Compiler or whatever Google come up with this time — good luck minifying it within vendor.js without breaking it on production.

But we need to do something!

Yes, we do. 105kb minified and gzipped code laying dead in vendor bundle is not acceptable in any way. Just think about it — people complaining about react size (5kb + 34kb with react-dom), considering moving to preact/inferno — and firebase just sitting here like “best I can do is 100”.

Well, since we cannot reduce the size of this boulder of code — we will delay loading it until it’s actually needed.

And we going to do it in a way no one will ever notice.

Webpack 2 and dynamic imports to the rescue

The first thing to remember — we need to do it in isomorphic (universal) fashion. And, since it’s 2017, we are going to use Promises.

For server it’s dead simple:

For browser it is, well, not so simple but thanks to webpack 2, its simple enough to make its worth:

webpack dynamic imports — https://webpack.js.org/guides/code-splitting/#dynamic-imports

Next, we need to make polymorphic export (we bundle our server app with webpack and babel, so may skip this step if you don’ use server-side rendering):

More on alias resolving — https://webpack.js.org/configuration/resolve/#resolve-alias

Now this import will return an instantly fulfilled Promise on the server and lazy-loading firebase bundle in the browser! Mind you, after loaded once client-side Promise also became near-instant fulfilled, so don’t worry about overhead.

Now you can initialize your firebase and return usable database and auth objects:

https://www.npmjs.com/package/firebase — more on initialization

That’s it. Of course, usage of firebase now became more verbose:

But since it’s lazy (read — loads than actually needed, not on initial page load) it is worthwhile.

It’s still damn huge, but at least it is in its own bundle
firebase bundle (called 0.js because, well, webpack) loaded when login function is called

Some thoughts

  • Don’t forget to handle promise failure/rejections along the way
  • Don’t forget to handle loading timeout. Promises cannot have timeouts, but they can be wrapped in Promise with timeout-based rejection:
  • You can get firebase.js in time you vendor bundle will be loaded. Just add link=”preload/prefetch” to <head/>.
  • Firebase app initialization may be somewhere complicated to get it work with hot-reloading. The key is to use default app, not named apps. You can read more here — https://firebase.google.com/docs/web/setup
  • Webpack Bundle Analyzer is awesome! Just run it from time to time — you would not believe how much space can be occupied by simple time formating library (yes, moment.js, I am looking at you and your locales)

--

--