Skip to main content

28 posts tagged with "engineering"

View All Tags

React Native Core Contributor Summit 2024 Recap

· 10 min read
Michał Pierzchała
Michał Pierzchała
Head of Technology at Callstack
Szymon Rybczak
Szymon Rybczak
Software Engineer @ Callstack
Mo Javad
Mo Javad
Head of Mobile (UK) @ Theodo
Steven Moyes
Steven Moyes
Senior Product Manager @ Microsoft

Every year, the core contributors in the React Native Community get together with the React Native team to collaboratively shape the direction of this project.

Last year was no different—with small exception. We usually meet a day before React Universe Conf (formerly React Native EU) at Callstack HQ in Wrocław. In 2024, learning from past experiences, we hosted the Summit for two consecutive days, so that we can have more unstructured time together.

all-participants

This annual tradition has become a valuable opportunity for contributors to share insights and voice their concerns, and for the core team to share their plans and gather feedback from key contributors to the React Native ecosystem—including partner companies, individual library authors and friends.

We divided the Summit into two tracks covering following topics:

In this blog post, we’d like to give you a sneak peek of the results of this gathering.

Releases

We had an extensive discussion about the release process for React Native. Core Team appreciates the value of having contributors from outside Meta involved in releases and emphasizes the importance of having nightly releases, which are particularly beneficial for Out-of-Tree platforms like React Native visionOS, library maintainers (Reanimated) and frameworks (Expo). We discussed the frequency of releases, with some folks asking for more frequent releases to ship fixes faster, while others expressed concerns about the impact on 3rd party libraries and upgrading efforts.

We also brainstormed ways to reduce unintentional breaking changes and improve communication about compatibility between React Native and 3rd party dependencies.

This session showed how complex it is to manage releases for React Native, and how delicate this topic is, given all the different parts of the ecosystem that need to be considered.

What's next after the New Architecture?

Now that the New Architecture has shipped as stable, we discussed what we should focus on next. What could be the next big thing? The topics revolved around:

  • Web compatibility – concluded in the discussion around direction of the React Strict DOM project, which should be treated as a temporary polyfill, while the Xplat team implements proper cross-platform functionality into the core of React Native.
  • Stabilizing the core API – turned out we need more consensus on what this means for app developers, library authors, Out-of-Tree platforms. E.g. it may be necessary to extract platform-native logic for iOS and Android from the shared C++ codebase. Part of which was covered by the LeanCore 2.0 discussion.
  • Old architecture support – as expected, the team confirmed that new React 19 features based on concurrent rendering, won’t work in old architecture. New features are primarily targeted for the new architecture. Due to blockers in React 19 release schedule it’s still not clear where to draw the line between functionality supported by both new and old architecture.
  • 3rd party libraries for React Native – today we library authors can use TurboModules, ExpoModules, recently NitroModules to achieve the same goal of bridging native platform functionality. We need better documentation on how to make it well.
  • Brownfield docs – at the time of the summit, the official documentation for integrating React Native into native apps was quite dated. Since then the team has followed through with up-to-date and simpler docs for Android and iOS.
  • Tree-shaking for Metro web – core Metro team is open to merge the work from the Expo team in this area.

Web APIs for Native Modules

This session was dedicated to Microsoft's RFC revolving around the idea of bringing a subset of Web APIs to React Native. It aims to enhance React Native’s scalability and attract more web developers by leveraging familiar APIs. Opening access to a wealth of existing open-source web libraries that don't have explicit React Native support.

web-apis

Standardizing on Web API specifications is not only beneficial but also essential for React Native growth, and aligns well with our Many Platforms vision and react-strict-dom project. The web offers a unified interface through its specifications, which React Native community modules currently lack. Microsoft has identified around 200 essential Web APIs that could be implemented first for platforms they support: iOS, Android, Windows and macOS.

We encourage library developers to align their APIs with web specifications whenever possible, as this standardization will improve code portability and developer experience across platforms.

While the proposal seems beneficial for the future of React Native, we're still brainstorming the next steps forward. One concern we noticed is governance of the APIs, and whether they’d need to live in a separate repository from the platform implementations. Another around diverging from the official specification in case a specific platform allows for behaviors not specified by the W3C. We would need to figure out how to avoid bundling unnecessary modules, e.g. with a Babel plugin. Not to mention the scope of such initiative is quite large.

The session conclusion reinforced two key points: First, there is strong alignment across the React Native community on adopting web-compatible specifications where possible. Second, we need to establish a clear technical strategy for how these Web API implementations can be maintained separately for different platforms. Microsoft together with Callstack could work on refining the original RFC and produce a proof of concept implementation for a smaller number of APIs as a community initiative. This incremental approach will help us validate the design and developer experience before expanding the scope.

LeanCore 2.0

In 2019, the React Native team started the Lean Core initiative. The goal was to tackle the surface area of React Native’s core and reduce APIs and components that were outdated and legacy. Since then, the React Native components and API surfaces have been long overdue another round of clean up.

Today, there are many components that are not actively being maintained with better community alternatives. Additionally, there are components that have duplicates that should eventually be consolidated for maintainability.

On the API side, a lot of the JS layer APIs are tied to native iOS & Android implementations, rather than being truly platform agnostic. For example, with Pressable, we have props like android_disableSound and android_ripple. Ideally, React Native components should have the smallest possible API surface that is not tied to any specific platform.

As Out-of-Tree platforms are growing and being adopted more by the ecosystem, there needs to be a path to reduce the component and API surface of React Native core, reducing the load on the React Native core team, and also making it significantly easier for Out-of-Tree platform & library maintainers to stay up-to-date.

As an added bonus, this would make it easier for beginner app developers to pick up React Native, as there are less duplicated components and "gotchas" for them to learn. Where there is a better community alternative, developers can be signposted and encouraged to use the community alternatives available.

During the session, we discussed:

  • The high level motivations of Lean Core and the benefits to the parties involved (developers, library maintainers, Meta)
  • An aggregated view of what components are being used in some real-world production React Native apps
  • The criteria of what is a candidate to be removed from core
  • A clear action plan for executing Lean Core 2.0 with:
    • The high-level process for deprecation
    • Handling cases where Meta is using components internally that have better community alternatives,

As a next step, a group of the core contributors will look at collecting more telemetry and data, assessing community alternatives, and putting together an RFC detailing the proposed changes.

Nitro Modules - Unblocking View Components by exposing props as jsi::Values

Recently, Marc Rousavy introduced Nitro Modules as an alternative approach to creating Native Modules. Nitro Modules utilize experimental C++ Swift Interop and incorporate a bunch of enhancements that can lead to improved performance in certain scenarios. However, during this session, we discussed the various trade-offs involved between Nitro Modules and existing TurboModules.

While Nitro Modules offer some performance benefits, they also have limitations and considerations that need to be addressed. For example, the use of experimental interop features might introduce complexity or compatibility issues that are not present in TurboModules. Our discussion focused on these trade-offs and the potential for upstreaming some of Nitro Modules' improvements into React Native Core, which could allow developers to benefit from more performant modules for everyone.

Out-of-Tree Platforms & CocoaPods

Out-of-Tree Platforms presents the full power of React Native, where we can share one JS codebase between different platforms running on our mobile devices, desktops or even on VR/XR devices. Creating such a platform currently isn’t the easiest process, actually there are no guidelines on how things should be created, developed and maintained. Also React Native Core in a way is tied to Android and iOS platforms. In the future we could aim for a scenario where all the platforms are treated equally and integrate with a C++/JS core through the same APIs.

oot-platforms

During this session maintainers of different platforms discussed what are the problems, what they struggle with and what should be the solution to unify the process of creating and maintaining new Out-of-Tree platforms.

Another aspect of this session was to discuss CocoaPods and future plans related to managing native dependencies. Recently the CocoaPods team announced that they’ve moved to maintenance mode and new major improvements or features won’t be shipped. There are various alternatives that could be used and during this session we discussed their pros and cons, and what migration would look like.

React Native on Desktop

Steven and Saad from Microsoft, maintainers of react-native-windows and react-native-macos, hosted a session to listen and gather feedback from contributors related to Desktop platforms. Topics discussed included exploring how to increase adoption of React Native for Desktop (such as having a dedicated workflow in Visual Studio, or exposing desktop as part of Nx), as well as how to support Expo, which is a continual pain point for more adoption.

There’s a big discrepancy in availability of community modules between macOS and Windows, largely due to the fact that iOS code is mostly compatible with macOS, while RNW needs bespoke implementations. While working on the New Architecture for React Native for Windows, the team sees potential in C++ modules allowing for even more code sharing across platforms which will hopefully ease the burden of targeting desktop platforms. It’s worth noting that on the community side Software Mansion is working on adding desktop support for their most popular modules, such as React Native Screens, Gesture Handler and Reanimated.


We’re still impressed by how spending several hours together for a couple of days resulted in so much knowledge-sharing and cross-pollination of ideas. During this summit, we planted the seeds for initiatives that will help us improve and re-shape the React Native ecosystem.

If you’re interested in joining the development of React Native, make sure you join our open initiatives and read the contribution guide we have on our website. We hope to meet you in person as well in the future!

React Native 0.77 - New Styling Features, Android’s 16KB page support, Swift Template

· 15 min read
Vojtech Novak
Vojtech Novak
Software Engineer @ Expo
Mazen Chami
Mazen Chami
Software Engineer @ InfiniteRed
Blake Friedman
Blake Friedman
Software Engineer at Meta
Rob Hogan
Rob Hogan
Software Engineer @ Meta

Today we are excited to release React Native 0.77!

This release ships several features: new styling capabilities such as support for display: contents, boxSizing, mixBlendMode, and outline-related properties to provide a more powerful layout options; Android 16KB page support to be compatible with the newer Android devices. We are also modernizing the community template by migrating it to Swift, while continuing to support and maintain compatibility with Objective-C for developers who prefer it.

React Native 0.75 - Support for Percentage Values in Layout, New Architecture Stabilization, Template & init Updates, and more

· 14 min read
Gabriel Donadel Dall'Agnol
Gabriel Donadel Dall'Agnol
Software Engineer at Expo
Siddharth Kulkarni
Siddharth Kulkarni
Software Engineer at Coinbase
Thibault Malbranche
Thibault Malbranche
Lead Mobile Engineer at Brigad
Blake Friedman
Blake Friedman
Software Engineer at Meta
Riccardo Cipolleschi
Riccardo Cipolleschi
Software Engineer at Meta
Nicola Corti
Nicola Corti
Software Engineer at Meta

Today we are excited to release React Native 0.75!

This release ships several features, such as Yoga 3.1 with support for % values, several stabilization fixes for the New Architecture, and the introduction of the recommendation for users to use a React Native Framework.

Highlights

Breaking Changes

React Native 0.71-RC0 Android outage postmortem

· 7 min read
Nicola Corti
Nicola Corti
Software Engineer at Meta
Lorenzo Sciandra
Lorenzo Sciandra
Senior Software Engineer at Microsoft

Now that 0.71 is available, we want to share some key information about the incident that broke Android builds for all React Native versions while releasing the first 0.71 release candidate for React Native & Expo Android builds on November 4th, 2022.

The contributors who helped tackle the incident recently attended a post-mortem meeting to discuss in detail what happened, what we all learned from it, and what actions we are going to take to avoid similar outages in the future.

First-class Support for TypeScript

· 5 min read
Luna Wei
Luna Wei
Software Engineer at Meta
Nick Gerleman
Nick Gerleman
Software Engineer at Meta

With the release of 0.71, React Native is investing in the TypeScript experience with the following changes:

In this post we’ll cover what these changes mean for you as a TypeScript or Flow user.

Preparing Your App for iOS 15 and Android 12

· 3 min read
Samuel Susla
Samuel Susla
Software Engineer at Meta

Hello everyone!

With new mobile OS versions releasing late this year, we recommend preparing your React Native apps beforehand to avoid regressions when the releases become generally available.

Introducing new iOS WebViews

· 2 min read
Software Engineer at Facebook

For a long time now, Apple has discouraged using UIWebViews in favor of WKWebView. In iOS 12, which will be released in the upcoming months, UIWebViews will be formally deprecated. React Native's iOS WebView implementation relies heavily on the UIWebView class. Therefore, in light of these developments, we've built a new native iOS backend to the WebView React Native component that uses WKWebView.

The tail end of these changes were landed in this commit, and will become available in the 0.57 release.

To opt into this new implementation, please use the useWebKit prop:

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

Improvements

UIWebView had no legitimate way to facilitate communication between the JavaScript running in the WebView, and React Native. When messages were sent from the WebView, we relied on a hack to deliver them to React Native. Succinctly, we encoded the message data into a url with a special scheme, and navigated the WebView to it. On the native side, we intercepted and cancelled this navigation, parsed the data from the url, and finally called into React Native. This implementation was error prone and insecure. I'm glad to announce that we've leveraged WKWebView features to completely replace it.

Other benefits of WKWebView over UIWebView include faster JavaScript execution, and a multi-process architecture. Please see this 2014 WWDC for more details.

Caveats

If your components use the following props, then you may experience problems when switching to WKWebView. For the time being, we suggest that you avoid using these props:

Inconsistent behavior:

automaticallyAdjustContentInsets and contentInsets (commit)

When you add contentInsets to a WKWebView, it doesn't change the WKWebView's viewport. The viewport remains the same size as the frame. With UIWebView, the viewport size actually changes (gets smaller, if the content insets are positive).

backgroundColor (commit)

With the new iOS implementation of WebView, there's a chance that your background color will flicker into view if you use this property. Furthermore, WKWebView renders transparent backgrounds differently from UIWebview. Please look at the commit description for more details.

Not supported:

scalesPageToFit (commit)

WKWebView didn't support the scalesPageToFit prop, so we couldn't implement this on the WebView React Native component.

Accessibility API Updates

· 7 min read
Ziqi Chen
Student at UC Berkeley

Motivation

As technology advances and mobile apps become increasingly important to everyday life, the necessity of creating accessible applications has likewise grown in importance.

React Native's limited Accessibility API has always been a huge pain point for developers, so we've made a few updates to the Accessibility API to make it easier to create inclusive mobile applications.

Problems With the Existing API

Problem One: Two Completely Different Yet Similar Props - accessibilityComponentType (Android) and accessibilityTraits (iOS)

accessibilityComponentType and accessibilityTraits are two properties that are used to tell TalkBack on Android and VoiceOver on iOS what kind of UI element the user is interacting with. The two biggest problems with these properties are that:

  1. They are two different properties with different usage methods, yet have the same purpose. In the previous API, these are two separate properties (one for each platform), which was not only inconvenient, but also confusing to many developers. accessibilityTraits on iOS allows 17 different values while accessibilityComponentType on Android allows only 4 values. Furthermore, the values for the most part had no overlap. Even the input types for these two properties are different. accessibilityTraits allows either an array of traits to be passed in or a single trait, while accessibilityComponentType allows only a single value.
  2. There is very limited functionality on Android. With the old property, the only UI elements that Talkback were able to recognize were “button,” “radiobutton_checked,” and “radiobutton_unchecked.”

Problem Two: Non-existent Accessibility Hints:

Accessibility Hints help users using TalkBack or VoiceOver understand what will happen when they perform an action on an accessibility element that is not apparent by only the accessibility label. These hints can be turned on and off in the settings panel. Previously, React Native's API did not support accessibility hints at all.

Problem Three: Ignoring Inverted Colors:

Some users with vision loss use inverted colors on their mobile phones to have greater screen contrast. Apple provided an API for iOS which allows developers to ignore certain views. This way, images and videos aren't distorted when a user has the inverted colors setting on. This API is currently unsupported by React Native.

Design of the New API

Solution One: Combining accessibilityComponentType (Android) and accessibilityTraits (iOS)

In order to solve the confusion between accessibilityComponentType and accessibilityTraits, we decided to merge them into a single property. This made sense because they technically had the same intended functionality and by merging them, developers no longer had to worry about platform specific intricacies when building accessibility features.

Background

On iOS, UIAccessibilityTraits is a property that can be set on any NSObject. Each of the 17 traits passed in through the javascript property to native is mapped to a UIAccessibilityTraits element in Objective-C. Traits are each represented by a long int, and every trait that is set is ORed together.

On Android however, AccessibilityComponentType is a concept that was made up by React Native, and doesn't directly map to any properties in Android. Accessibility is handled by an accessibility delegate. Each view has a default accessibility delegate. If you want to customize any accessibility actions, you have to create a new accessibility delegate, override specific methods you want to customize, and then set the accessibility delegate of the view you are handling to be associated with the new delegate. When a developer set AccessibilityComponentType, the native code created a new delegate based off of the component that was passed in, and set the view to have that accessibility delegate.

Changes Made

For our new property, we wanted to create a superset of the two properties. We decided to keep the new property modeled mostly after the existing property accessibilityTraits, since accessibilityTraits has significantly more values. The functionality of Android for these traits would be polyfilled in by modifying the Accessibility Delegate.

There are 17 values of UIAccessibilityTraits that accessibilityTraits on iOS can be set to. However, we didn't include all of them as possible values to our new property. This is because the effect of setting some of these traits is actually not very well known, and many of these values are virtually never used.

The values UIAccessibilityTraits were set to generally took on one of two purposes. They either described a role that UI element had, or they described the state a UI element was in. Most uses of the previous properties we observed usually used one value that represented a role and combined it with either “state selected,” “state disabled,” or both. Therefore, we decided to create two new accessibility properties: accessibilityRole and accessibilityState.

accessibilityRole

The new property, accessibilityRole, is used to tell Talkback or Voiceover the role of a UI Element. This new property can take on one of the following values:

  • none
  • button
  • link
  • search
  • image
  • keyboardkey
  • text
  • adjustable
  • header
  • summary
  • imagebutton

This property only allows one value to be passed in because UI elements generally don't logically take on more than one of these. The exception is image and button, so we've added a role imagebutton that is a combination of both.

accessibilityStates

The new property, accessibilityStates, is used to tell Talkback or Voiceover the state a UI Element is in. This property takes on an Array containing one or both of the following values:

  • selected
  • disabled

Solution Two: Adding Accessibility Hints

For this, we added a new property, accessibilityHint. Setting this property will allow Talkback or Voiceover to recite the hint to users.

accessibilityHint

This property takes in the accessibility hint to be read in the form of a String.

On iOS, setting this property will set the corresponding native property AccessibilityHint on the view. The hint will then be read by Voiceover if Accessibility Hints are turned on in the iPhone.

On Android, setting this property appends the value of the hint to the end of the accessibility label. The upside to this implementation is that it mimics the behavior of hints on iOS, but the downside to this implementation is that these hints cannot be turned off in the settings on Android the way they can be on iOS.

The reason we made this decision on Android is because normally, accessibility hints correspond with a specific action (e.g. click), and we wanted to keep behaviors consistent across platforms.

Solution to Problem Three

accessibilityIgnoresInvertColors

We exposed Apple's api AccessibilityIgnoresInvertColors to JavaScript, so now when you have a view where you don't want colors to be inverted (e.g image), you can set this property to true, and it won't be inverted.

New Usage

These new properties will become available in the React Native 0.57 release.

How to Upgrade

If you are currently using accessibilityComponentType and accessibilityTraits, here are the steps you can take to upgrade to the new properties.

1. Using jscodeshift

The most simple use cases can be replaced by running a jscodeshift script.

This script replaces the following instances:

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

With

accessibilityRole= “trait”

This script also removes instances of AccessibilityComponentType (assuming everywhere you set AccessibilityComponentType, you would also set AccessibilityTraits).

2. Using a manual codemod

For the cases that used AccessibilityTraits that don't have a corresponding value for AccessibilityRole, and the cases where multiple traits were passed into AccessibilityTraits, a manual codemod would have to be done.

In general,

accessibilityTraits= {[“button”, “selected”]}

would be manually replaced with

accessibilityRole=“button”
accessibilityStates={[“selected”]}

These properties are already being used in Facebook's codebase. The codemod for Facebook was surprisingly simple. The jscodeshift script fixed about half of our instances, and the other half was fixed manually. Overall, the entire process took less than a few hours.

Hopefully you will find the updated API useful! And please continue making apps accessible! #inclusion

State of React Native 2018

· 5 min read
Sophie Alpert
Engineering Manager on React at Facebook

It's been a while since we last published a status update about React Native.

At Facebook, we're using React Native more than ever and for many important projects. One of our most popular products is Marketplace, one of the top-level tabs in our app which is used by 800 million people each month. Since its creation in 2015, all of Marketplace has been built with React Native, including over a hundred full-screen views throughout different parts of the app.

We're also using React Native for many new parts of the app. If you watched the F8 keynote last month, you'll recognize Blood Donations, Crisis Response, Privacy Shortcuts, and Wellness Checks – all recent features built with React Native. And projects outside the main Facebook app are using React Native too. The new Oculus Go VR headset includes a companion mobile app that is fully built with React Native, not to mention React VR powering many experiences in the headset itself.

Naturally, we also use many other technologies to build our apps. Litho and ComponentKit are two libraries we use extensively in our apps; both provide a React-like component API for building native screens. It's never been a goal for React Native to replace all other technologies – we are focused on making React Native itself better, but we love seeing other teams borrow ideas from React Native, like bringing instant reload to non-JavaScript code too.

Architecture

When we started the React Native project in 2013, we designed it to have a single “bridge” between JavaScript and native that is asynchronous, serializable, and batched. Just as React DOM turns React state updates into imperative, mutative calls to DOM APIs like document.createElement(attrs) and .appendChild(), React Native was designed to return a single JSON message that lists mutations to perform, like [["createView", attrs], ["manageChildren", ...]]. We designed the entire system to never rely on getting a synchronous response back and to ensure everything in that list could be fully serialized to JSON and back. We did this for the flexibility it gave us: on top of this architecture, we were able to build tools like Chrome debugging, which runs all the JavaScript code asynchronously over a WebSocket connection.

Over the last 5 years, we found that these initial principles have made building some features harder. An asynchronous bridge means you can't integrate JavaScript logic directly with many native APIs expecting synchronous answers. A batched bridge that queues native calls means it's harder to have React Native apps call into functions that are implemented natively. And a serializable bridge means unnecessary copying instead of directly sharing memory between the two worlds. For apps that are entirely built in React Native, these restrictions are usually bearable. But for apps with complex integration between React Native and existing app code, they are frustrating.

We're working on a large-scale rearchitecture of React Native to make the framework more flexible and integrate better with native infrastructure in hybrid JavaScript/native apps. With this project, we'll apply what we've learned over the last 5 years and incrementally bring our architecture to a more modern one. We're rewriting many of React Native's internals, but most of the changes are under the hood: existing React Native apps will continue to work with few or no changes.

To make React Native more lightweight and fit better into existing native apps, this rearchitecture has three major internal changes. First, we are changing the threading model. Instead of each UI update needing to perform work on three different threads, it will be possible to call synchronously into JavaScript on any thread for high-priority updates while still keeping low-priority work off the main thread to maintain responsiveness. Second, we are incorporating async rendering capabilities into React Native to allow multiple rendering priorities and to simplify asynchronous data handling. Finally, we are simplifying our bridge to make it faster and more lightweight; direct calls between native and JavaScript are more efficient and will make it easier to build debugging tools like cross-language stack traces.

Once these changes are completed, closer integrations will be possible. Today, it's not possible to incorporate native navigation and gesture handling or native components like UICollectionView and RecyclerView without complex hacks. After our changes to the threading model, building features like this will be straightforward.

We'll release more details about this work later this year as it approaches completion.

Community

Alongside the community inside Facebook, we're happy to have a thriving population of React Native users and collaborators outside Facebook. We'd like to support the React Native community more, both by serving React Native users better and by making the project easier to contribute to.

Just as our architecture changes will help React Native interoperate more cleanly with other native infrastructure, React Native should be slimmer on the JavaScript side to fit better with the JavaScript ecosystem, which includes making the VM and bundler swappable. We know the pace of breaking changes can be hard to keep up with, so we'd like to find ways to have fewer major releases. Finally, we know that some teams are looking for more thorough documentation in topics like startup optimization, where our expertise hasn't yet been written down. Expect to see some of these changes over the coming year.

If you're using React Native, you're part of our community; keep letting us know how we can make React Native better for you.

React Native is just one tool in a mobile developer's toolbox, but it's one that we strongly believe in – and we're making it better every day, with over 2500 commits in the last year from 500+ contributors.

Using TypeScript with React Native

· 8 min read
Ash Furrow
Software Engineer at Artsy

JavaScript! We all love it. But some of us also love types. Luckily, options exist to add stronger types to JavaScript. My favourite is TypeScript, but React Native supports Flow out of the box. Which you prefer is a matter of preference, they each have their own approach on how to add the magic of types to JavaScript. Today, we're going to look at how to use TypeScript in React Native apps.

This post uses Microsoft's TypeScript-React-Native-Starter repo as a guide.

Update: Since this blog post was written, things have gotten even easier. You can replace all the set up described in this blog post by running just one command:

npx react-native init MyAwesomeProject --template react-native-template-typescript

However, there are some limitations to Babel's TypeScript support, which the blog post above goes into in detail. The steps outlined in this post still work, and Artsy is still using react-native-typescript-transformer in production, but the fastest way to get up and running with React Native and TypeScript is using the above command. You can always switch later if you have to.

In any case, have fun! The original blog post continues below.

Prerequisites

Because you might be developing on one of several different platforms, targeting several different types of devices, basic setup can be involved. You should first ensure that you can run a plain React Native app without TypeScript. Follow the instructions on the React Native website to get started. When you've managed to deploy to a device or emulator, you'll be ready to start a TypeScript React Native app.

You will also need Node.js, npm, and Yarn.

Initializing

Once you've tried scaffolding out an ordinary React Native project, you'll be ready to start adding TypeScript. Let's go ahead and do that.

react-native init MyAwesomeProject
cd MyAwesomeProject

Adding TypeScript

The next step is to add TypeScript to your project. The following commands will:

  • add TypeScript to your project
  • add React Native TypeScript Transformer to your project
  • initialize an empty TypeScript config file, which we'll configure next
  • add an empty React Native TypeScript Transformer config file, which we'll configure next
  • adds typings for React and React Native

Okay, let's go ahead and run these.

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

The tsconfig.json file contains all the settings for the TypeScript compiler. The defaults created by the command above are mostly fine, but open the file and uncomment the following line:

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

The rn-cli.config.js contains the settings for the React Native TypeScript Transformer. Open it and add the following:

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

Migrating to TypeScript

Rename the generated App.js and __tests_/App.js files to App.tsx. index.js needs to use the .js extension. All new files should use the .tsx extension (or .ts if the file doesn't contain any JSX).

If you tried to run the app now, you'd get an error like object prototype may only be an object or null. This is caused by a failure to import the default export from React as well as a named export on the same line. Open App.tsx and modify the import at the top of the file:

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

Some of this has to do with differences in how Babel and TypeScript interoperate with CommonJS modules. In the future, the two will stabilize on the same behaviour.

At this point, you should be able to run the React Native app.

Adding TypeScript Testing Infrastructure

React Native ships with Jest, so for testing a React Native app with TypeScript, we'll want to add ts-jest to our devDependencies.

yarn add --dev ts-jest

Then, we'll open up our package.json and replace the jest field with the following:

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

This will configure Jest to run .ts and .tsx files with ts-jest.

Installing Dependency Type Declarations

To get the best experience in TypeScript, we want the type-checker to understand the shape and API of our dependencies. Some libraries will publish their packages with .d.ts files (type declaration/type definition files), which can describe the shape of the underlying JavaScript. For other libraries, we'll need to explicitly install the appropriate package in the @types/ npm scope.

For example, here we'll need types for Jest, React, and React Native, and React Test Renderer.

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

We saved these declaration file packages to our dev dependencies because this is a React Native app that only uses these dependencies during development and not during runtime. If we were publishing a library to NPM, we might have to add some of these type dependencies as regular dependencies.

You can read more here about getting .d.ts files.

Ignoring More Files

For your source control, you'll want to start ignoring the .jest folder. If you're using git, we can just add entries to our .gitignore file.

# Jest
#
.jest/

As a checkpoint, consider committing your files into version control.

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

Adding a Component

Let's add a component to our app. Let's go ahead and create a Hello.tsx component. It's a pedagogical component, not something that you'd actually write in an app, but something nontrivial that shows off how to use TypeScript in React Native.

Create a components directory and add the following example.

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

Whoa! That's a lot, but let's break it down:

  • Instead of rendering HTML elements like div, span, h1, etc., we're rendering components like View and Button. These are native components that work across different platforms.
  • Styling is specified using the StyleSheet.create function that React Native gives us. React's stylesheets allow us to control our layout using Flexbox, and style using other constructs similar to those in CSS.

Adding a Component Test

Now that we've got a component, let's try testing it.

We already have Jest installed as a test runner. We're going to write snapshot tests for our components, let's add the required add-on for snapshot tests:

yarn add --dev react-addons-test-utils

Now let's create a __tests__ folder in the components directory and add a test for Hello.tsx:

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

The first time the test is run, it will create a snapshot of the rendered component and store it in the components/__tests__/__snapshots__/Hello.tsx.snap file. When you modify your component, you'll need to update the snapshots and review the update for inadvertent changes. You can read more about testing React Native components here.

Next Steps

Check out the official React tutorial and state-management library Redux. These resources can be helpful when writing React Native apps. Additionally, you may want to look at ReactXP, a component library written entirely in TypeScript that supports both React on the web as well as React Native.

Have fun in a more type-safe React Native development environment!