← Back to all posts

React Native Production Checklist: Don't Ship Without These

EhsanBy Ehsan
6 min read
React NativeProductionPerformanceSecurityBest PracticesOptimizationMobile DevelopmentHermesTurboModules

Introduction

You've built your React Native app. Features work. UI looks great. Time to ship, right?

Not yet.

Production apps need more than working features. They need security, performance optimization, monitoring, and protection against reverse engineering. Skip these steps, and you'll deal with crashes, slow performance, security issues, and bad reviews.

I've shipped multiple React Native apps to production. Here's my checklist of things you absolutely need before going live.

Not every point applies to every project—some might be overkill for your app. But it's always good to go through this list at least once for every new project and decide what makes sense for your use case.

Security & Code Protection

Enable Hermes Engine

Hermes is React Native's JavaScript engine optimized for mobile. It reduces JS bundle size, improves startup time, and compiles to bytecode making your code harder to reverse engineer. Enable it on both iOS and Android—it's the default in React Native 0.70+, but verify it's active. For most apps, Hermes bytecode provides sufficient code protection without additional obfuscation overhead.

Store Sensitive Logic in Native Modules

Critical business logic like payment processing or encryption shouldn't live in JavaScript. Move sensitive code to native modules written in Java/Kotlin or Swift/Objective-C—they're much harder to reverse engineer than JS.

Never Embed Secrets in App Bundle

API keys, tokens, and secrets should never be hardcoded in your app. Use a secure backend with proper authentication instead of client-side checks. For environment-specific config, use react-native-config to manage .env files safely. Remember: even with these tools, determined attackers can extract values from your bundle—always authenticate requests server-side.

Use Certificate Pinning

Prevent man-in-the-middle attacks by implementing certificate pinning with libraries like react-native-ssl-pinning. This ensures your app only trusts your specific SSL certificates, not just any valid certificate.

Enable ProGuard/R8 on Android

Android's ProGuard (or R8 in newer versions) obfuscates and shrinks your native code. Configure it with optimized rules to make reverse engineering harder and reduce your APK size. Don't use default settings—customize them for your app's needs.

Enable iOS Bitcode & Dead Code Stripping

For iOS, enable bitcode and dead code stripping to reduce binary size and make it harder to reverse. These settings remove unused code and optimize the compiled binary, making it smaller and more difficult to analyze.

Security Monitoring

Monitor Dependency Vulnerabilities

Use npm audit, GitHub's Dependabot, or Snyk to continuously monitor your dependencies for known vulnerabilities. New security issues are discovered regularly—you need automated alerts to stay on top of them.

Implement Jailbreak & Root Detection

If your app handles sensitive data, implement runtime jailbreak and root detection. Use jail-monkey to detect compromised devices. It checks for jailbreak/root, debugger attachment, and hook frameworks, letting you restrict features or show warnings on compromised phones.

Performance Optimization

Enable TurboModules & Fabric Renderer

React Native's new architecture (TurboModules + Fabric) is the default in 2025. Make sure you're using it—it significantly improves performance with better native-JS communication and concurrent rendering. Check the React Native New Architecture docs if you're migrating from older versions.

Memoize Expensive Components

Use React.memo, useCallback, and useMemo to prevent unnecessary re-renders. Wrap expensive components with React.memo and memoize callback functions passed as props. This is especially important for lists and complex components that render frequently.

Avoid Unnecessary Re-renders

Proper keying, virtualization with FlatList, and careful state management prevent performance issues. Don't render everything at once—use virtualized lists, lazy load screens, and split your app into smaller chunks that load on demand.

Lazy-Load Screens

Use React.lazy or dynamic imports to split your app into smaller bundles that load on demand. Don't load all screens at startup—load them when users navigate to them. This improves initial load time significantly.

Use MMKV for Storage

Replace AsyncStorage with MMKV. MMKV is 500-800x faster, synchronous, and perfect for storing user preferences, cached data, or app state. I covered this extensively in my Jotai state management post.

Preload Critical Assets

Preload images, fonts, and other assets during your splash screen. Use Image.prefetch() for images and load fonts before the main app renders. Users shouldn't see loading spinners after your splash screen disappears.

Monitoring & Analytics

Set Up Crash Reporting

Implement crash reporting with Sentry, Firebase Crashlytics, or BugSnag. You can't fix crashes you don't know about. Production crashes will happen—make sure you catch them, get stack traces, and can reproduce them.

Set Up Analytics

Add analytics with Firebase Analytics, Mixpanel, or Amplitude. Track user flows, feature usage, and conversion rates. Data-driven decisions beat guesses every time. Know how users actually use your app, not how you think they use it.

Final Checks

Before hitting that submit button:

  • Test on real devices, not just simulators
  • Verify deep linking works correctly
  • Check app permissions are properly requested
  • Test offline mode and poor network conditions
  • Review app store metadata and screenshots
  • Run your app through TestFlight or internal testing
  • Test payment flows thoroughly in sandbox mode
  • Verify push notifications work correctly

This checklist isn't exhaustive, but it covers the critical items that separate amateur apps from professional ones. Security, performance, and monitoring aren't optional—they're what keep your users safe and your app running smoothly.

Frequently Asked Questions

Do I really need Hermes if my app runs fine without it?

Yes. Hermes improves startup time, reduces memory usage, and makes your code harder to reverse engineer. It's enabled by default in modern React Native—there's no reason to disable it unless you have specific incompatibilities.

How do I know if my app has performance issues?

Use React Native's built-in Performance Monitor (shake device → "Show Perf Monitor") to watch FPS in development. Test on older devices—if it runs smoothly on an iPhone 15, try it on an iPhone 11.

Should I implement all security measures for every app?

Depends on your app. Banking apps need certificate pinning and root detection. Simple content apps might not. Assess your security needs based on the data you handle. At minimum, enable Hermes, configure ProGuard/R8 properly, and never hardcode secrets.

What's the difference between TurboModules and the old architecture?

TurboModules enable synchronous native-JS communication and lazy loading of native modules. The old bridge was asynchronous and loaded everything at startup. TurboModules are faster, more efficient, and the future of React Native.

How often should I check for dependency vulnerabilities?

Continuously. Set up automated tools like Dependabot or Snyk to alert you when vulnerabilities are discovered. Check manually at least monthly, and always before major releases. Security vulnerabilities are discovered constantly—staying current is critical.

Resources