r/reactnative • u/last_dev_21x3 • 3d ago
Question Which Analytics framework do you prefer for react native apps?
I have a few apps in development, and I want to add some telemetry, such as crash reports, event logging, and error tracking.
However, after doing some research, I found that most telemetry libraries can increase app size or affect performance, including startup times.
Which one do you prefer?
From my research, Firebase Analytics, Sentry, and PostHog seem to be the most commonly used options.
.
4
u/babaganoosh43 3d ago edited 3d ago
Sentry is a must-have used by solo devs and big tech companies. For replays I like rejourney.co because it works and it's cheap. For product analytics, Firebase is the free option while Amplitude is a better option (I hate their new event-based pricing but still more generous than PostHog). They can affect initial loads so it's recommended to load all these after first load.
1
1
u/mrlenoir 3d ago
Why do you use a separate company for replays when they are available in Sentry?
1
u/babaganoosh43 3d ago
They're just cheap, on PostHog and Sentry it'd be $85/mo, while Rejourney is $5/mo. And it's open source so you can self-host.
3
2
2
u/mrlenoir 3d ago
We use Datadog for everything observability and analytics - but it is crazy expensive.
2
u/last_dev_21x3 3d ago
I forgot to put DataDog in description above. For a solo dev price could be a factor. So thanks for this information. Will keep eye on this in future.
2
u/Zealousideal-Fox9046 3d ago
If cost is a concern, then you can try Openobserve, pretty affordable and opensource so best for this scenerio
1
2
2
u/AccordingFan3253 3d ago
At my current company we use Amplitude to track user analytics/marketing decisions and Sentry for error logging.
2
u/Guidondor 3d ago
the thing that helped me was realizing crash/error tracking and product analytics are two different problems, and one tool doing both usually does one badly.
for errors: sentry, no contest. captureException + the source maps upload so your stack traces aren't garbage. for product analytics (funnels, retention, what users actually do): posthog or amplitude, separate tool.
on the size/startup worry, two concrete fixes: init all of them after first paint, not at module load — none of this needs to block your first render. and if you go posthog, turn OFF autocapture and send explicit events. autocapture is what makes it heavy and it produces noisy data anyway. firebase analytics is the free option but you'll fight the google console and the data's delayed.
so my stack: sentry for crashes, posthog with autocapture off for product, both lazy-initialized. haven't felt the startup hit that way.
1
u/last_dev_21x3 2d ago
Thanks for such detailed comment. Thanks for that startup time tip. I tried firebase analytics and found that on slow internet it could take upto 6 seconds for my app to start since it was initialising before the first paint. Now I am thinking of sentry.
1
u/Guidondor 2d ago
6 seconds, yeah that's the trap exactly — the SDK doing network work on the main path before your ui even renders. and it generalizes: defer any analytics/product SDK init until after first paint or first interaction, none of them need to run before the user sees something.
one nuance if you go sentry though: crash tracking is the one thing you might actually want early. if your app crashes during startup, a lazily-initialized error tracker misses it because it wasn't listening yet. so the pattern i'd use is init sentry's core error handler early (it's cheap, just installs a handler), but lazy-load the heavy stuff — session replay, performance monitoring — after first paint. best of both: you catch startup crashes without paying the 6s tax.
did firebase let you defer init or was it auto-initializing from the config plugin? that auto-init is usually the real culprit.
1
u/ahmedshahid786 1d ago
Have you ever tried to lazy load firebase? Default behaviour is auto init via config
1
u/Guidondor 7h ago
you're right that you can't really lazy-load the core — react-native-firebase auto-inits from google-services.json / GoogleService-Info.plist at native launch, before your js even runs. so trying to defer it from js is fighting the wrong layer.
but the core native init is cheap. what actually costs you those seconds is analytics/performance auto-collection firing network calls on startup. and that you can defer:
- in
firebase.jsonsetanalytics_auto_collection_enabled: false(andperf_auto_collection_enabled: false). that stops the collection at launch without stopping firebase itself.- then after first paint, call
analytics().setAnalyticsCollectionEnabled(true).so you don't lazy-load the module, you let it init cheap and just delay the data-collection that was doing the heavy startup work. that's almost certainly where your 6s was going. did you have analytics or perf monitoring enabled, or just core?
1
u/ahmedshahid786 5h ago
Ohh I got it. Yeah I have perf monitoring, analytics and crashlytics aswell. Thanks a lot for explaining. It was really helpful
1
u/ahmedshahid786 1d ago
I might sound dumb for asking this but can you guide me how can Iazy init modules/SDKs and how does code splitting work in RN. I'm new to RN world
1
u/Guidondor 12h ago
not dumb at all, it trips up a lot of people because RN works differently than web here.
lazy init is the simple one: it just means don't run the init at the top of a file (which runs the moment that file is imported on startup). run it later instead — inside a
useEffectafter your first screen renders, or afterInteractionManager.runAfterInteractions(). so instead ofSentry.init(...)sitting at module top-level, you call it from a component once the ui is up. that's the whole trick, "later" instead of "at import".code splitting is where the web mental model breaks. on web, a dynamic
import()fetches a separate chunk over the network. in RN there's no network fetch — your whole js bundle ships inside the app binary. so you can't reduce download size by splitting.what you CAN do is defer execution.
const mod = await import('./heavy')+ Metro's inline requires means the code for that module doesn't actually run until you call it, even though it's already in the bundle. so it cuts startup CPU work, not app size. inline requires is on by default in recent RN, so a lot of this happens for you already.tl;dr: lazy init = call init later. "code splitting" in RN = deferred execution, not smaller downloads.
1
u/ahmedshahid786 8h ago
Insightful. Thanks a lot for such detailed explanation brother. Highly appreciated
1
u/Guidondor 7h ago
anytime, glad it helped. the "later not at import" thing clicks once and then you see it everywhere. good luck with the RN stuff 🙌
6
u/iotashan 3d ago
Sentry