Better Cross-Platform React Native Components

Introduction

React Native gives JavaScript developers amazing tools to build truly native mobile apps for both iOS and Android with a single code base. However it falls short of providing developers with the tools to create polished, platform-specific user interfaces, instead leaving that up to the individual developer or wider development community to provide the solutions.

A few solutions like React Native Elements and Native Base have popped up recently, but they fail to address the subtle (and often not-so-subtle) differences between iOS and Android, instead opting to provide generic components that look “good enough” on both.

This is really unfortunate considering the bar for quality in apps has never been higher, and evidence has shown that users are very sensitive to inconsistencies in the “look and feel” of the apps they use. Others have even argued that this mindset of “good enough” could be “The Death of React Native”.

In this post we’ll look at some tools and techniques we can use to make our components look right at home on both iOS and Android — without sacrificing code reuse. Here’s what we’ll cover:

• Typography
• Iconography
• Styling and Theming
• Touchable Interactions
• Putting it all together: A cross-platform Button Component


Typography

Typography is a subtle but important element of a mobile app’s user interface and design. iOS and Android use distinctly different typefaces that help distinguish the two platforms and give a cohesive look and feel to the apps on their platforms.

Thankfully React Native makes this easy for us and by default renders the correct typeface for each platform: “San Francisco” on iOS and Material Design’s “Roboto” on Android.

However, earlier versions of React Native hard-coded Helvetica as the default font for most elements, so if you are dealing with a legacy component or package one trick you can use is fontFamily: ‘System’ to force the correct font family on each platform.

Another noticeable difference between iOS and Android is that most buttons on Android use all caps. Unfortunately React Native doesn’t support a textTransform: ‘uppercase’ property instead requiring developers to use JavaScript’s toUpperCase string function.

One solution to this could be to wrap the built in <Text /> component:


Iconography

Iconography is key to communicating concepts in your app. On iOS, icons tend to utilize thin lines and white space, whereas Android implements Google’s Material Design language with thicker, bolder icons. React Native doesn’t give us anything out of the box for icons, but fortunately there are two great resources we can use to render icons that map 1:1 between Apple and Android’s Material Design.

icons8

Icons8 has a staggering number of free, downloadable icons that not only provide iOS and Material Design versions, but also include Windows 8, Windows 10 and legacy Android style icons. These can easily be downloaded and added to your app to be used with React Native’s <Image /> component.

Ionicons v3

The beta of Ionic v2 includes a great upgrade to the Ionicons icon library. In this new version, every icon contains both an iOS and Material Design version. Combined with the React Native Vector Icons package we can easily create a wrapper component that displays the appropriate icon based on the current platform:

Now with one line of code we can display a platform-specific icon in our app:

<CrossPlatformIcon name=”alarm” size={30} color=”#fff” />

Styling and Theming

The Facebook team recommends that “your code should re-use as much as possible”. So rather than building separate components for iOS and Android, attempt to utilize a single component, and if possible apply platform-specific styles.

Option 1: Platform.select

The “out-of-the-box” solution is to use Platform.select to selectively apply custom styles for each platform.

Option 2: Use a Package

The previous technique has been abstracted into it’s own package called react-native-platform-stylesheet which requires less code and looks a bit cleaner:

Option 3: react-with-styles

The engineering team at AirBNB recently released react-with-styles, an inline-styling package that supports both React and React Native. It also includes some nice theming capabilities that we could potentially use to build platform-specific styles. I’ve just started experimenting with this option, but I could see it working something like this:


Touchable Interactions

The most fundamental user interaction on a mobile app is the touch. Appropriately, React Native provides 3 separate components for handling touch interactions, but few people know which one to use or why. In my experience most people (including my previous self) will randomly grab a TouchableHighlight or TouchableOpacity and call it a day.

Let’s walk through each component and talk about when they should be used.

TouchableHighlight

What it does: Darkens or lightens the background of the element when pressed.
When to use it: On iOS for touchable elements or buttons that have a solid shape or background, and on ListView items.

TouchableOpacity

What it does: Lightens the opacity of the entire element when pressed.
When to use it: On iOS for touchable elements that are standalone text or icons with no background color.

TouchableNativeFeedback

What it does: Adds a ripple effect to the background when pressed.
When to use it: On Android for almost all touchable elements.


Putting it all together: A cross platform <Button /> Component

By combining the previous tools and techniques we’ve learned, we can create a component that abstracts away the differences between the platforms and allows us to write the same exact code on iOS and Android, but with the appropriate styles and interactions for each:

A cross-platform Button component with the correct touch interactions.

Obviously there’s a lot more we could do here (different sizes, colors, icons, etc), but hopefully this gives you an idea of what’s possible.


I’m planning to distill these lessons into a full UI framework in the near future, so if you’re interested in this type of thing be sure to follow me on Twitter!