Maintaining the "native" part of React Native applications can be time consuming. Major releases of React Native often take several days and introduce bugs due to incompatibilities with other native packages in use. Updating the latter is also a tedious process.
Expo is the solution that makes it easier to build and maintain React Native applications. Expo's prebuild system, called Continuous Native Generation (CNG), automates the generation of native code from configuration files. Config plugins integrate specific native modifications, while the Expo Modules API simplifies the creation of native modules. Expo Application Services (EAS) makes it easy to build and deploy mobile applications. In addition, Expo Updates offers an overthe-air (OTA) update solution for sending updates without having to go through the stores.
Meta officially recommends the use of Expo for new React Native applications, as stated on the React Native website: "To build a new app with React Native, we recommend a framework like Expo". What's more, with the scheduled closure of AppCenter by Microsoft on March 31, 2025, Expo Updates remains the only viable solution for OTA updates.
Many projects never left the ground with Expo because the solution wasn't always as complete and stable as it is today. Fortunately, it's possible to migrate to Expo gradually. First, by configuring a new Expo application with all the dependencies of the existing application so that the JavaScript code can be executed. Then, by updating the deployment processes to use EAS and Expo updates. Finally, by migrating to Expo modules to benefit from higher quality modules that are better maintained and automatically updated when the Expo SDK is upgraded.
At Theodo, we've seen significant gains in development and maintenance time by using Expo in our projects. The ease of updates, improved productivity, and robustness of the Expo ecosystem make it a must-have solution for React Native projects. Despite some migration challenges and support limitations on certain platforms, adopting Expo is a strategic decision that will pay off in the long run.
We strongly recommend planning to migrate your project to Expo to simplify upgrades, increase productivity, and improve maintenance of React Native projects.
Universal applications are gaining in popularity, allowing developers to target iOS, Android and the web with a common code base. This trend raises the question of the best technology to handle this multi-platform approach. At Theodo, we use two frameworks depending on the project: Expo Router and Solito.
As its documentation indicates, Solito is both:
The main advantage of Solito over Expo Router is the ability to use the advanced features of Next.js. These include enabling serverside rendering (SSR), using server components, improved font and image management, and better support for internationalization. For applications where performance and SEO are critical, this is an undeniable advantage. Monorepo also provides a clear separation between web- or mobile-specific code and shared code, which is especially interesting if certain features of your application are web- or mobile-only. Another interesting use case is if you already have an Expo application and a NextJS application, and you want to share code between them. In this case, the applications can be grouped under a single repository and the mobile components can be shared incrementally.
However, using a monorepo with an Expo application and a NextJS application presents some challenges compared to a single application under Expo Router. While this can introduce some complexity, such as the need to maintain two separate navigation systems, this approach also allows for greater flexibility. Page creation is still fast, but to ensure a consistent experience between the mobile app and the website, it's important to synchronize the page structure between the two routers.
Solito makes it much easier to navigate the shared code, although the lack of typing and autocompletion for URLs requires special attention to avoid errors.
Solito is an excellent option for universal application projects where SEO and web performance are critical. For applications that don't need these features, it's easier and less expensive to use Expo Router.
We talked about Suspense in the last Tech Radar and recommended its activation. The major advantage of using Suspense with a data fetching solution like React Query is the simplification of the components, whose loading states and error handling we no longer need to specify. This not only improves maintainability, but also UX, since it encourages the placement of loaders and error inserts.
There is, however, one aspect to be aware of when switching to Suspense. If several data fetching hooks using Suspense are placed end-to-end, the asynchronous tasks will execute one after the other, even if they are independent, a phenomenon commonly referred to as waterfall calls. The solution we recommend is to distribute hooks as far down the React tree as possible, so that the component being suspended is as small as possible. As the components are suspended in parallel, this ensures asynchronous parallel calls. To improve time-to-interactivity (TTI), it is also possible to prefetch requests.
Recently, the React team released a change that completely broke React Query with Suspense (all calls became waterfall, even when applying the above approach).
However, that change was cancelled and it delayed the release of React 19. This makes us particularly confident that this way of using Suspense is indeed a practice recognized and validated by the React team.
With all the UX and maintainability benefits offered by Suspense for data fetching (with React Query or other libraries supporting it), we strongly recommend switching to Suspense. We also recommend migrating existing projects.
Since an app is often available on both web and mobile, it's natural to want to share code between these platforms, which is possible for business logic, UI state, and API calls. However, UI components cannot be shared by default.
React Native Web is a React DOM-compliant implementation of React Native components and APIs.
We've been able to share between 75% and 95% of the code between React Web and React Native applications using React Native Web on several projects, and the feedback has been very positive. Variations in the amount of code shared depend on the amount of web and/or mobile-specific functionality, such as page layout.
In our previous Radar, we reported on performance and accessibility issues. A lot of work has been done since then, both on react-native and react-native-web, as well as with community libraries like Tamagui and Expo Router.
React Native Web is not a magic solution to all codesharing problems, and the remaining points have more to do with the way we work than the technology itself:
We are now using React Native Web on all of our projects that require code sharing between web and mobile. This adoption confirms our confidence in the technology and we now recommend it as a reliable solution for developing applications for these platforms.
Developing web applications requires taking into account constraints that are generally unknown to mobile developers: bundle size, direct access to any page via a URL, server-side rendering...
In order for a framework to offer the best performance with respect to these constraints, it must directly manage the bundling process, navigation and, if necessary, data fetching. This is what Next. js, Astro, and Remix do.
Expo-router is a universal navigation framework for React Native apps that, thanks to its integration with other Expo tools, promises an optimized experience on both iOS/Android and the web. Specifically, exporouter provides file system based routing (creating a file in the app
folder defines a new route) and is an overlay for React navigation. The web platform is officially supported, with URL management, bundle splitting to optimize performance, and the ability to include CSS. This is the main advantage of Expo Router.
In use, Expo Router's youth is still noticeable. For example, the navigation typing is experimental and incomplete (it doesn't cover search parameters), and the seemingly simple API complicates the implementation of certain scenarios that weren't a problem for developers familiar with React Navigation. The choice of an app
folder where all files become routes means that 2 parallel trees must be maintained in the codebase, whereas other frameworks go back to this constraint and even the very idea of "filesystem-based routing".
We have decided to put Expo Router for universal apps in Trial because it's the best choice for deploying a React Native app on the web. However, the performance is not yet "best- inclass" (no server- side rendering, no treeshaking yet), so in certain scenarios an overlay, for example with Next.js and Solito, is still necessary.
Almost all mobile applications allow the user to enter text and therefore need to manipulate the iOS and Android virtual keyboard. Even the most popular apps often have bugs, or a suboptimal user experience related to this keyboard management. In fact, it's a more complex problem than it seems. And in the case of React Native, platform differences don't make it any easier.
The React Native package itself provides limited APIs to deal with this problem, so one developer decided to tackle it and released the React Native Keyboard Controller.
This library provides a unified API between iOS and Android that includes all the necessary functionality: keyboard open/ close with UI animation (integrated with reanimated), text field management, placement of fixed buttons above the keyboard...
React Native Keyboard Controller is a recent library and may have some instabilities, but it's already the most complete solution in its field, hence its position on our trial dial.
We've started using this library in our projects and encourage other developers to do the same.
One of the biggest challenges in developing a TV application is managing the focus on the remote control. Solutions are not consistent across platforms. React Native tvOS provides an implementation, but it's not completely predictable between tvOS and AndroidTV, and it doesn't yet exist for the web. This makes it difficult to build universal applications with React Native.
At Theodo, we've developed React TV Space Navigation, a library that reimplements navigation in React without using the native focus, ensuring consistent behavior across platforms. It provides a React-friendly API with purely declarative components and includes optimizations like virtualization and CSS scrolling to improve performance, especially on less powerful devices.
However, bypassing native focus has limitations: accessibility support is incomplete, and some specific features, such as "ploc" on Android, are missing. Despite these drawbacks, we have successfully deployed a large cross-platform streaming application using this library. This application is very smooth to use.
We are watching the development of react-native-tvos
, which may one day make our library obsolete. With proper component isolation, it's easy to revert to the default react-native-tvos
management, but this would remove support for the web, which has no builtin focus management.
For now, we recommend trying out the library for your cross-platform TV projects, keeping in mind the limitations of this solution, which comes with certain compromises in terms of accessibility and native feel.
In server-driven UI, the server sends a precise description of what should appear on the screen, rather than raw data. Some applications use this approach to respond to constraints such as the need for rapid iteration, heavy UI customization, or application size limitations.
In March 2024, the React team announced an official server driven UI mechanism called "React Server Components" (RSC). These components can be "composed" with "client components". React and the framework it uses are responsible for managing the interactions between the two worlds (creating the right javascript bundles, reconciling the client-side component tree, etc.).
The only role of the mobile application is to display the transferred UI.
Because server components run on a server, they can directly access secrets, databases, and the file system. This promises to radically simplify the development of certain functionalities. It also avoids client-server round trips, which improves performance. So far, there is no integration of Server Components in a React Native framework, but Expo is working on it. A React Native implementation of Server Components will face several challenges:
We invite you to keep an eye on how the React Native Server Components ecosystem evolves, so that you can use them as soon as possible, where relevant.
Many style libraries have emerged in response to the growing popularity of universal applications. These include Tamagui, Nativewind, and Unistyles. With the Web came new performance and server-side rendering (SSR) issues, making solutions more complex and difficult to compare.
Unistyles provides an API close to that of StyleSheet, but with additional features: accessible theme in style, variants, dynamic styles, breakpoints, access to insets and screen size. The core of the library is written in C++ and offers similar performance to StyleSheet.
The advantage of Unistyles is the simplicity of its API. Defining a custom theme is easy because the structure is not imposed, unlike Tamagui. Its proximity to StyleSheet makes it easier for React Native developers to get started, while those used to Tailwind will be more familiar with Nativewind.
The library is compatible across many platforms: web, Windows, MacOS, visionOS, and TV. However, support for SSR is not yet complete, since responsive styles don't use media queries. Version 3 should include a compiler, such as Nativewind or Tamagui, to generate CSS at build time.
Unistyles is a new project, but it's very active. The project benefits from the commitment of its main contributor, Jacek Pudysz, which allows it to move forward quickly. However, this dependence on a single individual may raise questions about its sustainability.
Unistyles is a very promising solution for universal applications. It is easy to use and offers many practical features. However, it still lacks maturity, especially in terms of web support, which is not yet on par with Nativewind. These shortcomings should be fixed with the release of V3.
SVG is a vector-based image format in which the image is dynamically drawn. This format has some interesting properties: it is never blurred and can inherit color or shape variations. However, drawing dynamically comes at a performance cost, especially if the rendering engine is not tuned or if the image is complex.
The react-native-svg
library is often used to draw SVGs, recreating each SVG shape in the React view so that it can be dynamically modified or animated. However, this transformation can be slow, especially for icons, which are often numerous on a single screen and slow rendering.
There are better solutions for icons and static SVGs. For icons, icon fonts can be used. This means converting SVGs to characters in a font. The limitation of this solution is that the SVG must be monochrome, a typical feature of standard icons. A common method is to use IcoMoon in combination with @expo/vector-icons.
For other illustrations, expo-image
is a good alternative. This library handles SVGs and displays them better. But it's impossible to animate the SVG and adjust its colors (unless you apply a tint to the whole SVG container, but that changes the whole image).
These alternatives cover a large part of SVG needs, since it's rare to need to partially change the color of SVGs.
We recommend not using react-nativesvg by default for all SVGs. It is preferable to use solutions like icon fonts for icons or expo-image for static SVGs to improve performance. However, it is still important to use react-native-svg for dynamic SVGs.
Find out what our experts have to say about the techniques, platforms, tools, languages and frameworks associated with the main mobile technologies we use every day at BAM: React Native, Flutter and Native.