React Native Stack: Our tools for building in React Native
We developed our React Native stack because we’re constantly trying to find tools that will make our products better. Any time we hear about a new tool or technique, our mobile team has a process for testing them out. If it proves out – delivering value to our clients — we’re quick to work it into our stack.
That’s how we ended up using React Native in the first place. One of our clients needed a cross-platform solution, and we had tested the other frameworks out there and found them lacking. So we tried React Native for Android, and it worked surprisingly well. Over the next year or so, we continued to build React Native apps, testing its limits and potential.
Today, React Native is our primary framework. We still do native Android and native iOS development in Java and Swift respectively, but most of the time in the battle of React Native vs. Swift, React Native just provides a better development workflow. Here are all the tools we’re using for apps built with React Native — and all the tools we tested but didn’t choose. Want the quick version?
Want all the best React Native tools in one stack? Download your free copy of our own mobile development stack.
React Native vs. Competitors
We tested so many frameworks for building cross-platform apps, because it always seemed like the perfect solution for clients who needed both iOS and Android apps. Facebook’s React Native has proven by far the best. Getting started with React Native was much simpler than the others, because it’s based on the React web framework, which is popular among frontend web developers. That’s made it possible for some of our frontend devs to learn React Native on the job, without needing a bunch of training and tutorials ahead of time.
Since it came out a few years ago, the framework has only gotten better, maturing rapidly and developing a thriving open-source community to support it. It’s incredibly flexible and quick to develop with, and if it doesn’t support a native feature, it’s relatively easy to incorporate native iOS or Android code into the project. Switching to a mostly React-based stack happened naturally, because it was so much easier and better to develop with than other options.
The Runners-up
Flutter: Google’s take on a cross-platform framework is an intriguing newcomer, and we’ve spent some time testing it out. While it sports a comfortable development environment and a good deal of high-quality, built-in UI components (something React Native lacks), we don’t see a compelling use case for it just yet. It’s built in Dart, which is an esoteric language that few developers use, and it doesn’t have anywhere near the wealth of third-party tools contributed by React Native’s sizeable community. Still, we’re keeping an eye on it as a potential alternative.
Ionic: Just because we found React Native to be the hands-down winner doesn’t mean everyone did. Ionic was another cross-platform language that came out around the same time. It’s based on Angular, a Javascript framework similar to React. We’ve tested it, and heard good things about it from the people who use it, but at our level, it wasn’t a free and open source tool. Using it would have increased costs for our clients. React Native, on the other hand, is free and open source, despite being created by Facebook. It also has a large community of people improving the code and building tools around it, making it a far better option for us (and far more likely to stick around).
A big part of what makes React Native so useful is the full ecosystem of tools and extensions. Some of our mainstays:
The tools in our React Native stack
Storybook: A wonderful tool for browsing all the discrete view components within an app — and their various states. This is fantastically useful when we’re building new views. Instead of tinkering with the entire app ecosystem, we can just focus on the view in isolation and feed in whatever data we need to test the UI. Even better, we can combine this tool with storyshots to automatically generate UI snapshot tests, so we’re building the view and writing its tests at the same time.
Jest: This is the standard automated test framework that ships with React Native, and we’ve fallen in love with it. It has the rspec-inspired, easy-to-read syntax shared by most testing frameworks, of course, but it also makes mocking an absolute breeze, and it contains an incredible code coverage tool that even builds a neatly-formatted website developers can use to review gaps in their tests.
Redux and redux-saga: Together, these development patterns allow us to elegantly manage a one-way flow of data through the entire app, including during asynchronous operations. This fits neatly with the reactive pattern that React Native is built upon. When data updates, the relevant views update. It’s as simple as that. Thanks to these tools, we have far fewer UI bugs than we ever did with earlier methods of updating an app’s UI state.
On Deck
React Context and Hooks: React ships with its own tools for managing state, and with the recent introduction of hooks, those tools are becoming mature enough that we’re considering them as proper alternatives to redux — especially for smaller projects. We’ll have more to say about hooks soon as we put them into practice.
Redux Persist: This library makes it easy for us to save the state of the app and restore it, even when the phone is restarted. That gives users the interruption-free experience they’ve come to expect.
Apisauce: With rare exceptions, every app we make communicates with a backend server. Apisauce makes this communication as simple as possible. It’s easy to integrate with our state management but deeply customizable as well, making the process of bootstrapping a new app much faster.
React Native Config and Envsub: We always test our apps extensively with our internal QA team and with clients before we release them to end users. Switching back and forth between a staging and production environment can easily cause issues if we’re not careful, so we use these tools to make it easy for us to organize environment-specific data and ensure that our test releases and production releases are always set up correctly.
react-native-I18n: At Table XI, our clients have users all over the world, which makes localization a necessity in our software This library provides extensive tools for storing and displaying text in different languages and makes switching locales a breeze.
A mobile stack optimized for React Native components
We also use plenty of tools that aren’t specific to React Native. Some of them are carryovers from iOS and Android development, others are framework agnostic.
User Testing
Zoom: We love to do user interviews in person, but it’s just not always possible, especially when we need an audience with specific experiences and needs. Zoom is a reliable tool we can use for video calls and screensharing, so we can watch users as they engage with our apps, even if they’re nowhere near.
Invision: Otherwise known as a UX designer’s best friend. Not only does it allow us to upload screens for testing mobile and desktop views, it has an intuitive commenting system that tightens the feedback loop for changes and improvements. Invision also comes with a mobile app — way better for approximating the native experience than browser-based simulators. To top it all off, Invision syncs with Sketch, our design tool of choice, and works with Lookback.
Code Review
Codecov: This has been useful to us in the past on Swift projects, and React Native projects have been no different. Codecov is a code coverage tool that provides us with a “sniff test” to go alongside our peer review process. If there’s code being merged in that isn’t being tested and might cause problems, Codecov gives us a much better chance of catching it.
Snapshot testing: We’re also using Jest’s snapshot testing feature to simplify the process of writing tests. Using snapshots allows us to set up a particular condition and save a snapshot of the state of the app, rather than having to exhaustively write out what we expect to occur. Since our larger apps have hundreds of individual tests, this saves a lot of time in the long run.
Now, all we do is write an automated test for our code, and Jest runs the code with the data we provide. We expect to get a certain result, like a function that takes a date and gives it back to us in a certain format. Traditionally, we’d have to run data into the app to test it, and then test that the result matches what we expect — a whole separate set of code to parse the data and make sure it’s correct. A Snapshot test takes an image of the result, matches it to the image it expects to see, and determines if the two match. If it doesn’t, the test will fail. It takes a lot of the thinking out of writing tests, cutting the time in half.
Detox: We use detox to run end-to-end tests during continuous integration. It’s been a useful tool to make sure the app continues to launch without problems, and we even take a screenshot to verify the app is opening to the expected screen.
Linting: It’s easy to skip the proper code formatting and styling when you’re in a time crunch. Over the lifetime of a project, however, sticking to the same structure is invaluable. Code that’s easier to read is easier to understand, alter, fix and maintain. That’s why we’ve incorporated linting tools into our workflow—we simply define our style guidelines at the beginning of a project and tools like eslint and prettier automatically keep us in line. Thanks to easy integration with VS Code, our preferred code editor, these style fixes are usually applied automatically every time we save a file.
Typescript: This is a typed superset of Javascript created by Microsoft. It allows you to add types to your code, and still compile into plain javascript. This is useful for tests and during development with smarter autocomplete and documentation. We’ve used typescript and flow a handful of times on projects, and there are pros and cons to both.
Flow: This tool is a static type checker that aims to prevent JavaScript’s natural ambiguity from causing bugs — i.e. it’s built to warn us when we attempt to put the wrong kind of data into a function, among other features. It does this by adding flow-specific decorators to your code, which are stripped out and results in plain JavaScript.
Deployment
Appcenter: A newcomer to the field of mobile CI, Appcenter is easily the cleanest and most user-friendly solution that we’ve found yet. Microsoft’s tool offers everything that we need to build, test, distribute and track our apps: simple Github integration, reliable certificate and provisioning profile management, robust logging and error reporting, and tons of customizability. We’ve only recently migrated, but it’s already making our build pipeline smoother and saving us a wealth of development time.
The Runners-Up
Bitrise: Our previous solution for CI was the most full-featured and mobile-specific option we could find a few years ago, but despite its impressive toolset and responsive support team, we simply had too many problems with it. Its bulky interface and slow build times made it difficult to troubleshoot. Their aggressive updating of their build stacks also caused us a number of issues.
BuildKite: This is a very simple tool that allows us to manage our CI, but does none of the actual hosting — we take care of that on our own. The result is much faster than Bitrise — a build might take two minutes instead of twelve — and much more customizable. It’s an awesome tool, but it has to run on macOS machines. Right now we’re trying to see if we can set up inexpensive macOS machines to run it. If we can, we’ll be swapping this into our React Native stack.
Testflight: We used to use this, but when Apple bought the app, they locked down the number of internal testers to 25. You also have to pass Apple’s full review process to send the app to external testers, meaning you basically need to have a finished app before you can test. Fabric lets us test with a greater number of people while we’re still building, and it works for both iOS and Android.
Analytics
Firebase: Google recently purchased Fabric, our previous platform for distribution and analytics, and is in the process of integrating it fully into Firebase. That’s made it easier for us to transition into this impressive analytics suite.
App Annie: This is another analytics tool we love, and it makes a great supplement to Firebase. It offers a lot of the same functionality but also compares your app data to that of your colleagues and competitors in the App Store — offering much more granular information than Apple itself does.
Mixpanel: Mixpanel provides our team with real-time data on the user experience in a simple and easy to understand way. It also allows us to track trends to ensure user adoption rates remain steady.
Crash Reporting
Appcenter: Appcenter offers its own integrated crash reporting tools that easily rival the functionality of our former choice, Crashlytics (part of the Fabric suite now owned by Google). So far we’ve found it to be responsive and accurate, so we can track issues in our beta and production builds with a high degree of transparency.
Bugsnag: We use Bugsnag as one of our error-monitoring tools. It helps our software engineers identify, prioritize and replicate bugs efficiently. It also alerts us to see how our code is working so we can identify issues before they become a problem.
Sentry: We use Sentry for our React Native projects because of its ability to provide a great stack trace from crashes caused in the Javascript. Sentry also has a feature that lets users actually report what they were doing when the crash occurred. This takes a lot of the guesswork out of production issues and makes fixing problems much more efficient. It also has an extensive set of issue tracking tools that rival the other solutions we’ve used.
We’ll keep updating this list as we continue testing and improving our React Native app stack. Want a cheat-sheet? See below.
Want all the best React Native tools in one stack? Download your free copy of our own mobile development stack.