React Native Best Practices (Learned the Hard Way Building ReceiptGold)
I've been building ReceiptGold for a while now — it's a receipt management app with bank syncing, team collaboration, and subscription management. Here's what actually works when you're trying to ship a real app that handles real user data and real money.
I've been building ReceiptGold for a while now — it's a receipt management app with bank syncing, team collaboration, and subscription management. I've hit every wall React Native can throw at you. Here's what actually works when you're trying to ship a real app that handles real user data and real money.
1. Just Use Expo (I Learned This the Hard Way)
I started ReceiptGold bare workflow because I thought I needed "more control." Spent a week just getting push notifications working on iOS. Then another week on Android. Then the app store submission hell.
Switched to Expo EAS Build and suddenly:
- OTA updates fix bugs without app store delays
- Push notifications work out of the box
- Build for both platforms with one command
- No more Xcode version nightmares
Unless you're building something like a custom camera driver (and even then, try expo-camera first), just use Expo.
2. State Management: Don't Be Like Early ReceiptGold
ReceiptGold v1 had Redux for everything. User profile? Redux. Current receipt? Redux. Whether a modal was open? Redux. It was a mess.
Now it's:
- Local state:
useStatefor forms, UI toggles - React Query: All server data — receipts, bank transactions, team members
- Zustand: Only global UI state (current business context, auth token)
React Query handles caching, background updates, and optimistic updates. When a user edits a receipt, the UI updates instantly while it syncs in the background. If it fails, it rolls back. Magic.
3. TypeScript Catches the Dumb Stuff
I once spent 3 hours debugging why receipt uploads were failing. Turned out I was passing receiptId instead of id to the API. TypeScript would've caught it in the editor.
Now every API response is typed. Every navigation param is typed. Props are typed. The time spent writing types pays for itself tenfold in bugs prevented.
4. Images Will Crash Your App (They Crashed Mine)
ReceiptGold users scan multiple receipts. Early versions would crash after 5-6 scans because we were loading full 4MB images into memory.
Our solution:
- Resize images before upload using
expo-image-manipulator - Compress to ~500KB max
- Use
expo-imagefor efficient caching and rendering - FlashList (not FlatList) for receipt lists with hundreds of items
Now users can scan 50 receipts in a row and the app stays smooth.
5. Test on Real Devices or Regret It
Plaid integration worked perfectly in the simulator. First real device test? The OAuth redirect failed because deep linking wasn't set up right. The Plaid SDK behavior was slightly different too.
Now I test on:
- iPhone SE (old/slow device check)
- iPhone 15 Pro (modern iOS)
- Cheap Android (Pixel 4a — catches most Android issues)
If it works on those three, it usually works everywhere.
6. Navigation: Deep Linking Is Non-Negotiable
ReceiptGold has team invites. User gets an email, clicks a link, should open the app and show the invite screen. Sounds simple. Took me a day to get right.
React Navigation v6 + Expo Linking handles it, but you have to:
- Configure the URL scheme in app.json
- Handle the initial URL on app launch
- Handle URLs when the app is already open
- Test both iOS Universal Links and Android App Links
Don't skip this. Deep links are how users get back into your app from emails, texts, and notifications.
7. Crash Reporting Saved ReceiptGold's Launch
Day 1 of ReceiptGold being live in the App Store, Sentry showed me a crash affecting 10% of users. Turned out to be a null check I missed on older iOS versions.
Fixed it, pushed an OTA update via Expo, problem solved in an hour instead of weeks.
You need crash reporting. Users won't email you. They'll just delete your app and leave a 1-star review.
8. Environment Variables: Don't Leak Your Keys
Early ReceiptGold builds had my Firebase API key in the JavaScript bundle. Not catastrophic (Firebase rules should protect you), but not ideal either.
Now:
- Use EAS Build environment variables for secrets
- Never commit .env files
- Keep API keys server-side when possible
- Rotate keys immediately if one leaks
Also: EXPO_PUBLIC_ prefix for client-side env vars, no prefix for server-side. Know the difference.
9. Performance: Profile Before You Optimize
ReceiptGold's "Detailed Breakdown" screen was slow. Like, really slow. I assumed it was the chart library.
Profiler showed it was actually re-rendering the entire list every time a single category updated. Memoized the list items with React.memo and useMemo for the calculations. Problem solved.
Most performance issues aren't libraries — they're unnecessary re-renders. Check there first.
10. Subscriptions Are Harder Than You Think
RevenueCat makes it easier, but ReceiptGold's subscription logic still has edge cases:
- User buys on iPhone, restores on Android
- User cancels but has time remaining
- Family sharing complications
- Grace periods when payment fails
Test every scenario. RevenueCat has sandbox testing modes — use them. Nothing's worse than a user paying and not getting access to features.
11. Offline Mode: Users Expect It
ReceiptGold works offline. Scan a receipt with no signal, it queues up and syncs when you're back online. This wasn't v1 — I added it after user complaints.
React Query makes this manageable with networkMode: 'offlineFirst'. But you have to think about:
- Conflict resolution (what if the receipt changed server-side too?)
- Storage limits (AsyncStorage has size limits)
- Queue management (don't let it grow forever)
12. Just Ship It Already
I spent 6 months "perfecting" ReceiptGold before launch. Should've shipped in 3. The feedback from real users shaped the product more than my assumptions ever could.
Your app will have bugs. It won't have every feature. Ship it anyway. You can always iterate.
Want more ReceiptGold war stories? I'm currently looking for remote full-stack work — if you need someone who's shipped React Native apps to production and survived, let's talk.
I'm a senior full-stack developer building ReceiptGold and looking for my next challenge. I write code that (mostly) works on the first try.