If you wish to build a cross-platform native app today, there are a ton of frameworks and technologies to choose from. Two of the most popular ones include React Native and Flutter. Both have grown massively over the last few years. Backed by a strong open-source community and popular tech giants Facebook and Google, both have a promising future ahead.
In this roundup, we’ll compare React Native with Flutter on various factors. By the end of this post, you’ll be able to decide the right framework for you.
The next criterion is based on how developers build apps using both these tools. React Native, similar to React for the web, is quite minimal. This means that it provides some base components out of the box but leaves the rest to the community. Consequently, this also means that you’ll end up using a lot of external dependencies for your project. In case one or more of your dependencies is deprecated, you’ll have to find newer and well-supported alternatives.
Flutter, on the other hand, comes with loads of built-in plugins and widgets you can use out of the box. They’re more reliable in terms of future scope since the Flutter team itself maintains these packages. Additionally, you can also use community-built open-source packages in Flutter. Or even better, use a combination of both.
Since Flutter gives the best of both worlds, it wins over React Native in this aspect.
Architecture and Performance
Flutter, on the other hand, doesn’t use native UI components. It has its own rendering engine built with C++ and Skia. Your Dart code on Flutter is compiled via this engine to create custom pixels on the screen. This allows it to render pixel-perfect code pertaining to both Android and iOS platforms. You can also use this architecture to create your own custom pixels that translate into UI elements on the screen.
Only in situations where it needs to interact with the device’s native modules does Flutter use a platform channels module that acts as a bridge. It normally doesn’t require a serial bridge for compiling Dart to native code. Hence, it renders better performance than React Native.
Frameworks exist to make a developer’s life easier by doing the heavy lifting of all the complicated stuff. This ease of using a framework is measured by developer experience, or DX. A framework that offers a great DX allows developers to focus solely on the end-user experience of the app.
To get started with React Native, you can simply generate a blank project by running the following:
npx react-native init <name_of_your_project>
You’ll need Node.js and npm (Node Package Manager) installed and set up on your system. The npx command downloads all the required packages and dependencies to generate a fresh React Native project on the fly.
Additionally, you can also begin your project with a template using the –template flag. Also, you can directly use Expo instead of React Native CLI for your React Native project. Expo offers an even more convenient and beginner-friendly way to start coding apps in React Native and deploying them to the relevant app stores.
With Flutter, you have to install the Flutter SDK first. Then, you can run the following command to generate a new Flutter project:
flutter create <name_of_your_project>
The above command runs faster than its React Native equivalent. This is because, with React Native, you download and install all the packages on the fly. On the other hand, you already have some initial dependencies configured when you installed the Flutter SDK.
Generating a new Flutter project is marginally faster. However, the initial setup for a React Native project is quite flexible, and the fact that it has Expo simplifies app development to another level. This puts React Native ahead of Flutter in this aspect.
Development Time Tools
With native app development, the most important tools at development time include emulators and physical devices. You need to see your code live at developing time and see how it appears and behaves on a real device before shipping it to your users.
Once you have an emulator running, you’ll have to run the adb command to connect to it.
adb devices //This lists out the devices
adb -s <device_name> //This connects you to the emulator device
Metro watches for changes in the background and supports hot reloading for you to see your changes live as you code them.
Finally, you’ll need to run the following:
npm run android
This will create your Android app bundle and install it on the emulator.
The same process is much easier with Flutter. Once you’ve configured the Android emulator, you can simply click the device button available in the VS code. All you then need to do is run the following command:
Now your project creates an Android bundle and also supports hot reloading in the same terminal.
When I first learned React Native, there were days when I couldn’t get beyond the point of running the project on a physical device. Brownie points to Flutter since it’s a lot easier in this respect.
On the other hand, Dart makes it really hard to compile code that contains type errors and will not let this happen in the first place. It tells you beforehand that you’ve written some bad code with respect to the types you’ve used for your variables. This helps you prevent type errors early in your program.
You can achieve a similar result with React Native if you use TypeScript instead. However, Dart also offers features like null safety to make your code more robust. Clearly, Flutter stands tall in this segment.
React Native uses the underlying principles and coding patterns used by React Web. You break your app into components. These components can be either classes or simple functions that return your component’s UI.
To use dynamic data, you add a state to your components and share it across other components using props or data stores like Redux or Context API. You can also abstract away your codebase into utility functions, custom hooks, or higher-order components.
Flutter also follows a component-based architecture. However, it restricts you to only use classes for components. Each class has a build method that returns a tree of widgets. Each widget in itself is a class instance and could have a ton of other properties that take other widgets as arguments. This manifests your codebase into deeply nested widgets that are hard to follow. You can abstract these into custom widgets, but the process isn’t as intuitive as it is in React.
Building declarative UI and refactoring code is easier in React than Flutter. Hence, more points to React Native!
Overall, React Native offers a far better developer experience than Flutter.
Testing and Deployment
What good is your app if it doesn’t reach your users bug-free in time? Testing and deployment are a crucial part of native app development.
React Native doesn’t provide support for testing and CI/CD out of the box. You’ll have to hook up a third-party library for this. While there are some really great testing libraries out there, it could lead to chaos and decision fatigue for your team.
Flutter, on the other hand, comes with default support for widget testing. This is really helpful if you wish to quickly perform some unit tests to make your UI foolproof. Flutter also has dedicated documentation on how you can set up CI/CD for your app.
Thus, Flutter has an edge over React Native here. However, modern testing tools like Waldo allow you to create reliable automated tests without writing a single line of code. You can even run them directly in the browser, detect issues in your code, and compare them to other versions of your app.
Therefore, if you’re using something like Waldo for testing your apps, React Native’s disadvantages are nullified.
Let’s summarize the scores for each based on the criteria we discussed:
Flutter outperforms React Native due to better architecture. However, in most of the use cases, the performance difference is almost indistinguishable. All in all, React Native provides a better developer experience than Flutter, which eventually gives you a better time to market.
For me, React Native is the winner of this battle! What do you think?