How do you choose what goes in your package.json?
Is it based on what the team’s used before? What has the most GitHub stars? Or because some article on dev.to told you to use it? 😶
As a React (Native) Developer, this was something I never really thought about until TV development forced me to. Think about the performance gap between an iPhone and a Fire Stick - devices with 1GB of RAM don’t give you room for extra library 'costs'.
Why? Your library choices directly affect how fast your app feels to users because your JavaScript bundle size impacts Time to Interactive (TTI - how long it takes for the app to become fully interactive after the initial load), memory usage, and CPU usage during run time.
So while an extra 100KB might feel negligible on mobile, we’re rarely just building for mobile. Every library decision, gets amplified and can carry hidden ‘costs’ across the hardware spectrum our apps now have to support.
Let's take an example, a commonly suggested swap - moment.js for date-fns:
We can use bundlephobia.com to quickly check the ‘cost’ of adding a npm library to your bundle. Upon checking, it tells us moment.js
clocks in at around 300KB, while date-fns
is a much leaner 77KB:
Okay so we should definitely use date-fns
right? 🧐
Well since I no longer trust anything on the internet 👀, I did some more digging. I added each package to a clean app and then analysed the actual JS bundle using Expo Atlas(you could also use react-native-bundle-analyzer) and something surprising happened. date-fns
showed up as the larger library:
Wait — waat?
date-fns
is architected as 300+ small utility files, each doing one thing. Now that’s great if your bundler is properly configured for tree shaking — it can strip out unused code, and only include what you call. But for React Native developers, like me, the metro bundler isn’t configured for tree shaking by default. Now while it is currently experimental with Expo, if you aren’t tree shaking, or your import patterns are off —
import * from 'library'
— you end up bundling all 300 files, whether you use them or not.
At this point your probably still like:
Okay, So?
Its just a few extra KBs, how bad can it be? Well I decided to measure what those extra KB’s did to my app’s perceived speed. I created a simple Android app using React Native & Expo and rendered the date using each library. I then used our Fire-OS perf testing tool to see how each library would perform on my Amazon Fire Stick. The results were 😲
Those 200KB increased our time to interaction (latency) from cold start by 3%:
Now this is just an example but this could be any library.
So here’s the thing, a library might look super helpful but it always comes at a cost. And with a broad spectrum of devices, those costs hit harder. So how can we weigh up the costs? Heres a my -
Checklist for adding a library
- First, as we saw, check the actual bundle size on your target platforms
- Then measure the performance cost
- And finally consider platform quirks e.g. FlashList might work on mobile but on TV platforms it might not have the functionality to handle the the dynamic, focusable content we have to deal with.
So don’t wait for the hidden costs to sneak up on your app performance.
Be deliberate. Be Critical. Every library in your package.json
should earn its place 🏅
Top comments (9)
I agree and disagree.
Yes, we should be using better optimised dependencies - but the bathwater is already on the pavement as far as Node is concerned. A hello-world React app takes a quarter of a gigabyte.
"The ecosystem" is already way beyond redeemable.
However...
But the pictures you show indicate a difference of 18.171ms - 17.554ms, which is about 600 micro seconds, no? I mean, I know these differences add up, but that's imperceptible on its own.
Hey! Thank-you for sharing your thoughts and great call out, that was definitely a typo.
Yes agree its imperceptible on its own but you also wouldn't think 200kb would make a difference of 3% and while 3% may be imperceptible on mobile, this can have compound effects on 'slower' devices.
In terms of the ecosystem being beyond redeemable I do think improvements are being made - react.dev/blog/2025/02/14/sunsetti...
Surprising to see that from date-fns but makes sense. A good argument for tree shaking in metro honestly 😅. Though rnx kit has metro-serializer-esbuild for tree shaking with esbuild i never experimented with it. Any experience with it? Curious if you would see a significant difference.
Haha yes definitely see the value in tree-shaking! Noo I don't but i'd assume if it does do tree-shaking the results would look more similar to what came out of bundlephobia
Wow, really rethinking my library choices now 😐!
This is extremely impressive. I’ve made the exact same mistake, and it’s a pain to clean up later
Thank-you!
Great research! I've been telling everyone to switch from Moment to Date-fns because I knew Moment bundled everything, very interesting to see that's not always the case.
It's like a pre-check for installing the suitable npm libraries for your project's optimisation. The slightest difference in package size can make a big impact.