A lot has been written on the web about this, but I’m still going to write this down so I don’t forget.
I managed to get from a ~400kb bundle (the file was not served as gzipped) to a ~50kb bundle (gzipped), which is pretty amazing, in three steps (and some researching).
I mixed up the steps when writing this post, that’s why the file sizes I am reporting are not consistent. I actually went with Step 2, Step 3, and finally, Step 1.
Step 1: Enable gzip in Nginx / Apache (if you are hosting on your own VPS).
https://www.digitalocean.com/community/tutorials/how-to-add-the-gzip-module-to-nginx-on-ubuntu-14-04
Result: bundle file size served by Nginx reduced from ~175kb to ~48kb 😱
Step 2: Avoid importing whole libraries when you just need modules.
Instead of doing
import { times } from 'lodash' import { format } from 'date-fns'
Which doesn’t import only the times
and format
modules (when using create-react-app
), do
const times = require('lodash/times') const format = require('date-fns/format')
(I think it has something to do with Webpack’s Tree Shaking feature not being used or I don’t know, I didn’t research this any further.)
Result: bundle size reduced from ~260kb (~75kb compressed) to ~175kb (~48kb compressed) 🎉
Step 3: If possible, switch to Preact.
https://medium.com/@rajaraodv/using-preact-instead-of-react-70f40f53107c
My experience when switching was the same as mentioned in the article above, but my app was also kind of small when I switched, and I did have a little issue.
Since this.setState()
is async, I had to change when an event fired after updating the state of my app (using setState
‘s callback). This is actually how I should have written it in the first place, so it’s my bad it didn’t work right away.
// inside a component // somewhere at the top private _messages: HTMLDivElement | null async componentWillMount() { const self = this // ... // I had to rewrite this self.setState({ messages: messagesData.messages }) if (this._messages) { this._messages.scrollTop = this._messages.scrollHeight } // Into this self.setState({ messages: messagesData.messages }, () => { if (this._messages) { this._messages.scrollTop = this._messages.scrollHeight } }) }
The issue was basically that the _messages <div>
scrolled to the bottom before being populated with messages, which obviously resulted with the div not being scrolled at all.
Bonus: use Preact with create-react-app
without eject
ing:
Check out https://github.com/timarney/react-app-rewired/. It includes a package for Preact, so you won’t need to eject
your app to use it.
Bonus 2: use Preact with create-react-app-typescript
without eject
ing:
You will have to manually add the alias
part mentioned in the article above inside
- /your_project/node_modules/react-scripts-ts/config/webpack.config.prod.js
- /your_project/node_modules/react-scripts-ts/config/webpack.config.dev.js
Result: with Preact, the bundle size dropped from ~300kb to ~175kb (~86kb to ~38kb compressed) 🙈
I just noticed one thing: using yarn add/remove pkg
will rewrite the files above, and remove the aliases you added. Using npm i --save pkg
will not have this effect.
Hope this helps anybody.