---
title: Rapid cross-platform mobile development with React Native
teaser: 'How React Native helped us ship a cross-platform mobile app in 8 weeks.

  '
tags: react native,mobile,ios,android
author: Giles Van Gruisen
published_on: 2016-12-01
---

In this article, we examine a few features of React Native that helped us
ship a cross-platform iOS and Android app in 8 weeks, without sacrificing user
experience on either platform.

First, we'll see how code reuse and a fast feedback cycle let us move really
quickly and help us rapidly iterate on our designs. Then, we'll learn about the
framework's flexbox-based layout system and evaluate a few approaches to
platform-targeting, for situations where it isn't practical to reuse code due
to varying design patterns or platform constraints.

## Shared data layer

It's important not to overlook the data layer of our application as we consider
different implementation approaches. One of the biggest benefits of React Native
is the unofficial bundling of Redux as a framework for managing and tracking
changes in application state. This lets us write one shared data layer that can
be used within both our iOS and Android apps, saving us a lot of time and
helping us reduce the potential points of failure by only having to maintain
our data fetching and state management in one place. This data layer is probably
the most critical part of an application to be robust and tested, and having to
build that just once helps us ensure its stability across platforms.

Beyond that, Redux is also just a sensible approach to managing application
state. Planning and implementing a new feature is easy because Redux clearly
defines where many of the necessary pieces of functionality should live. Thus,
we can avoid the need for lots of upfront planning and naturally fall into
certain conventions and patterns of data fetching and state management. These
patterns also serve to ensure the code base is intuitive as we onboard or
hand off the project to other developers.

## Snappy feedback cycle

We've been quite impressed by how quickly the framework allows us to iterate
on mobile UI. This is due in large part to features like hot- and live-
reloading, where the application UI is essentially "refreshed" (like a web page)
whenever we change files. No more waiting to re-build and deploy our app in
order to preview a one-line UI change: we get the immediate feedback loop that
we previously only saw when working with web or hybrid apps.

![React Native feedback cycle](https://images.thoughtbot.com/blog-vellum-image-uploads/ptMNRDLFRk6wmH25efO2_feedback-cycle.gif)

At the same time, these mobile experiences feel truly native, because they are.

## Native styling

React Native's generic, flexbox-based layout system allows us to write a single
component that can be run on both iOS and Android, using plain-old JavaScript
objects to define styles. That means we're able to avoid the need to implement
UI separately using two vastly different languages and layout systems. I've
found this method of styling and layout to be more intuitive than Auto Layout,
and it should feel familiar to folks with a background building for the web.
That means it's easy for designers to hop into the codebase and implement their
own designs, helping ensure they come out as intended.

This is great because it saves us time, but we should be careful not to strive
for complete design parity across iOS and Android. After all, these are two
different platforms with varying design patterns and visual direction.

Users of each platform simply have different expectations when it comes to
mobile interfaces and experiences. We want to ensure that every user is served
the most familiar, intuitive experience for their platform, and that means
implementing platform-specific design and styling in our UI components.

## Platform targeting

There are two primary approaches to writing platform-specific code, and we can
take advantage of each in interesting ways. Let's take a look at them now.

### The Platform module

React Native exposes the `Platform` module, which detects the platform on which
the app is currently running. It exposes a property called `OS` which allows us
to control the flow of our application based on the current device platform. As
an example, let's suppose we want to tell the user which company made the OS
they're running, just in case they're not sure. We can do something like this:

```javascript
import { Platform } from 'react-native'

if (Platfom.OS === 'android') {
  alert('Your OS is made by Google')
} else {
  alert('Your OS is made by Apple')
}
```

This works pretty well but there's still a bit of duplication we'd like to
avoid. Sometimes we may need to diverge our application flow by platform in
order to perform the appropriate action, but often it's as simple as swapping
some values around based on the current platform. In that case, we can simplify
the code above using a nifty `Platform` method called `select`. It takes an
object whose keys correspond to the available platforms and returns the
appropriate value for the current platform. Let's see how that looks:

```javascript
import { Platform } from 'react-native'

const company = Platform.select({
  ios: 'Apple',
  android: 'Google',
})

alert(`Your OS is made by ${company}`)
```

This method can accept any value, including functions, so we can do some pretty
powerful stuff with it. It's easy to see how this approach can be valuable for
supplying different appearance values based on the platform or in any situation
_where the difference in implementation between the two platforms is rather
small_.

I want to emphasize that last point. We should strive for as much code-reuse as
is realistically possible, but not at the expense of clarity to the developer
or to the user. The goal here is to write programs that are easy to maintain and
work well. Littering our components with too many `Platform` expressions and
conditionals is antithetical to that goal because those divergences will
inevitably make the program more difficult to understand (and thus maintain).

So what do we do when our implementations are very different for each platform?
Fortunately, React Native's packager has a clever solution to that, which comes
in the form of platform-specific file extensions.

### Platform-specific extensions

When we write a statement like `import Button from './button'`, the packager
looks in the current directory for either a file called `button.js` (or a
directory called `button/` containing an `index.js`) and bundles it
appropriately. Platform-specific extensions allow us to write a separate file
for each platform, e.g. `button.ios.js` and `button.android.js`. The packager
will then bundle the appropriate file that corresponds to the packager's current
target platform.

```tree
├── index.js // cross-platform
├── button.ios.js // iOS-specific
└── button.android.js // Android-specific
```

Let's see how we can take advantage of this to build a generic component that
requires different, platform-specific implementations. Let's suppose no cross-
platform `<Switch>` component exists, and we want to write our own:

```javascript
// switch.ios.js
import React, { Component } from 'react'
import { SwitchIOS } from 'react-native'

const Switch = ({ disabled, onChange, style, value }) => (
  <SwitchIOS
    disabled={disabled}
    onValueChange={onChange}
    style={style}
    value={value}
  />
)

export default Switch
```

```javascript
// switch.android.js
import React, { Component } from 'react'
import { SwitchAndroid } from 'react-native'

const Switch = ({ disabled, onChange, style, value }) => (
  <SwitchAndroid
    disabled={disabled}
    onValueChange={onChange}
    style={style}
    value={value}
  />
)

export default Switch
```

This is a contrived example, but we may encounter situations which require
vastly different implementations, especially when working with native modules.
How about another example. Let's suppose now that we want to write a
`<SwitchField>` which will contain a switch and a label. Now that we have a
generic `<Switch>`, we can probably stick to one generic implementation, but we
still want platform-specific styles. We'll ultimately have three files:

```tree
├── switch-field.js // cross-platform component
├── switch-field-style.ios.js // iOS-specific styles
└── switch-field-style.android.js // Android-specific styles
```

```javascript
// switch-field.js
import React, { Component } from 'react'
import { Text, View } from 'react-native'
import Switch from './switch'
import styles from './switch-field-style' // Imports platform-specific style

const SwitchField = ({ style, label, onChange, value }) => (
  <View style={[styles.container, style]}>
    <Text style={styles.label}>{label}</Text>
    <Switch style={styles.switch} onChange={onChange} value={value} />
  </View>
)

export default SwitchField
```

Now we have our generic `<SwitchField>`, we'd like to specialize the style with
two independent `switch-field-style` files. Let's see how that might look:

```javascript
// switch-field-style.ios.js
import { StyleSheet } from 'react-native'

export default StyleSheet.create({
  container: {
    alignItems: 'center',
    alignSelf: 'stretch',
    flex: 0,
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  label: {
    alignSelf: 'flex-start',
    fontSize: 14,
  },
  switch: {
    alignSelf: 'flex-end',
  },
})
```

```javascript
// switch-field-style.android.js
import { StyleSheet } from 'react-native'

export default StyleSheet.create({
  container: {
    alignItems: 'flex-start',
    flex: 0,
    flexDirection: 'column',
  },
  label: {
    fontSize: 14,
    marginBottom: 10,
  },
  switch: {
    marginBottom: 10,
  },
})
```

As we have it, our version of `<SwitchField>` on Android will show a stacked
label and switch control, but on iOS we see a label on the left and a switch
control on the right.

![](https://images.thoughtbot.com/blog-vellum-image-uploads/Z2B4DpzRPqsgKfdZHzCb_preview.gif)

This is great, because we can keep our component logic in one place but maintain
our platform styles independently. If we decide to make a change to the way our
`<SwitchField>` looks on Android, we can be sure it won't affect its appearance
on iOS.

It's also worth noting that, while these examples pertain to Components, both
approaches are by no means component-specific! You can use either of these
solutions (the `Platform` module or platform-specific file extensions) in any
module within your React Native application.

---

In the end, React Native has been great for building cross-platform apps but we
still must be careful about how we approach cross-platform functionality and
styling. Hopefully the examples help you understand the differences between
these approaches so you can make the right decision when you find yourself
needing to target a particular platform in your React Native apps.

Thanks for reading!
