Tips for your first React Native project

React Native makes some big promises, but requires a few tradeoffs.

Minutes to read → 8
The React logo, from Facebook

What does the web look like in a world where most people are engaging through their phones? Where do you draw the line between responsive web applications and native applications? As Javascript gets faster, and WASM starts becoming a real thing, what is the role of the traditional client-side engineer?

These are some big questions that are going to be answered within the next few years, but for now we're stuck in this hybrid world, where it's important for us to have both high-performance responsive design pure-web applications, and native applications for apps with more demanding needs. I treat the web as a platform; we have iOS, we have Android, and we have the web. Ultimately I believe the web will be the dominant platform for most non-game applications, but for now there's the business demand for apps that live within app stores.

Rolling out applications to these three platforms (web, iOS, Android) is a pretty daunting ask, and traditionally you'd see three scrum teams deploy to tackle projects for these platforms. Today, however, there's another choice - using React Native to roll out to all three platforms.

I'm not going to go into the background behind React Native, that's more reading that you can do elsewhere. What I'd like to do is give you some pointers before you get into your first React Native project, that will hopefully save you some time and effort.

Plan your shared code as a collection of service libraries.

In an application that's heavy with business logic, you should break out that logic as a collection of service libraries, published to a private npm repository. These service libraries should allow for injected code based upon your platform. (Use fetch in your code, and a polyfill within your webpack script for deploying to the web)

export function MyComponent(props) {
  const [name, setName] = useState("");
  const handleSubmit = (evt) => {
    evt.preventDefault();
    MyService.submitForm(name);
  }
  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={name} onChange={e => setName(e.target.value)}/>
      <input type="submit"/>
    </form>
  )
}

This shared code (here in MyService) can also be where most of your unit testing takes place. Unit testing your business logic is the most important, and if you can get good coverage in a library that gets used by both your web React application and your React Native application, that will get you a long ways toward having a strong core library to build from.

My general rule of thumb is that any platform-specific DOM manipulation can go within that platform's codebase, eg "regular" React code for the web, and React Native DOM manipulation for your native apps.

CI/CD for React Native can be difficult; spend some time up front on this.

I'm going to profess my love for Gitlab here - the ability to have your own custom build pipeline runners, with configuration, running on a platform of your choosing, is huge. Having a custom runner sitting on a Mac mini, with Fastlane and React Native side by side, can give you that automatic deployment. Rather than running build after build manually within Xcode, get Fastlane running automatically when changes get pushed to either shared repos or the native-specific repo.

Mac mini
This can be your automated build runner for Gitlab.

Fastlane takes a while to get running the way you need it to run, so spend some time early on in your project to get it set up correctly. You'll get back that time tenfold when your builds are running and shipping smoothly later on. If you're shipping multiple times per day, you may want to have your CI/CD pipeline for native only run a maximum time of once per day, so that your beta users aren't overwhelmed.

Keep your designs simple - you can't reuse your SASS or CSS code.

This is a big one, and one that I would push for over and over. React Native styling can be difficult.

Of all the promises of code reuse that React Native makes, this one point is the one that can make your React Native project run into time crunches the most. You will need to do your styling in such a way that you can translate it to React Native's styling properties, which are slightly different than CSS. This can become a serious pain when you have a complex layout.

Here's a sample React Native "stylesheet":

import {StyleSheet} from 'react-native';


const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },

  inputbox: {
    height: 35,
    borderWidth:1
  },

  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

I didn't realize how big of an issue this would be, and it cost quite a bit of time translating relatively complex designs to SASS/CSS and then again to React Native styling. Had I known about the issues of styling with React Native, I would have pushed for simpler designs earlier to save time. You should too.

Plan for incompatibilities.

With NPM the way it is, you're going to run into this anyways with any sufficiently large project, but React Native can compound the pain of incompatible versions of libraries. Expect to run almost a full point version behind when dealing with React Native, though the community has gotten better at keeping pace with React lately.

The overall gameplan should be "reduce your dependencies" but this will only get you so far - there will need to be a maintenance cycle every so often when you're upgrading libraries and running through a full suite of tests for your React Native apps. I would plan for going through this cycle at least once per quarter.

You may still need some iOS or Android expertise.

The moment you start using third-party tools within your native app, you'll likely need to link those into your React Native application. You can then use React Native's "Native Modules" to call those libraries, but this isn't the most straightforward setup. Having an engineer on your team who has some iOS or Android experience will help here, since they should have some understanding of ObjC/Swift/Java and how those modules live together.

XCode fury
The one IDE you aren't happy to open.

Thankfully, you won't need an entire team of iOS/Android engineers, and so your team should have the ability to jump back and forth between native and web pretty readily. (And if you're using JS on the backend a la Node or in Lambda, your engineers can work on that side as well!) Effective resourcing is going to be the topic for a later article but for now the ability for people to keep their skills sharp on all sides of the application, along with being able to take a break on one module and jump to another, will give you a much healthier, faster team.

Unintended benefits of using React Native.

First, the tooling around creating web applications has blossomed recently, and there are quite a few extremely mature environments to create Javascript-based apps. There has been a lot of energy and thought into the web ecosphere, and that energy and thought isn't going to dissapate anytime soon. Contrast that with the world of native applications, where Apple and some mishmash of tools dominate the iOS environment, or a few more options given for Android development.

Next, the talent pool for primarily-web engineers is broader, and also has more depth. Javascript isn't going away, and Apple has already done one major language change for iOS development, going from ObjC to Swift. The web may or may not reach native application speeds, but when it comes time to launch a new scrum team for an application, using React Native will give you options that you wouldn't normally have.

Last, testing frameworks in Javascript -- I'm talking Jest here -- are far, far easier to use and develop against. Doing TDD in React Native is far, far easier than doing TDD for a native app. Jest is famously easy to use, and lets you set up mocks extremely quickly. The feedback and support for Jest are unparalleled, and Jest itself has a huge backing with a huge ecosystem of support. You could do far, far worse than using Jest.

What's the future of React Native?

Personally, I feel like React Native is in a bit of an odd place. Apple is trying to get in on the game with SwiftUI, giving an easier layout experience for native applications, however reimplementing business logic within Swift is just this tedious exercise for a developer to read, copy, and then rewrite. React Native should always be faster to bring a new application online based on a given Javascript core repository.

You can tell, though, that React Native is definitely still hacky. There is still a lot of work around getting code deployed (though most of the blame here can be placed on Apple and Google) and the entire build cycle in React Native still relies upon tools that you'd prefer weren't cobbled together. At some point, there may be a valid competitor here who brings better build tooling.

React Native is still primarily operated by Facebook; though there's a very robust community, there's a danger that Facebook gives up on RN and the community stalls out. (This isn't any different than any large framework primarily supported by one organization; look to Bootstrap for an example here) If Facebook decides to continue developing React, but abandons React Native, that could be extremely bad for the community. Definitely keep an eye on RN's development lifecycle.

Possibly, at some point, WASM might make React Native obsolete-- you'd be writing in an LLVM language like C++ or Rust, then compiling to WASM for use in a browser, or WASM will enable apps to run at native speed on mobile devices. Either way, this is something to keep an eye on for transitioning your project.

What I've learned from rolling out a React Native application.

The one thing I would caution against is assuming that React Native will be a smooth setup and deployment. Definitely spend the extra time up front to get your CI/CD pipeline working correctly, so you can push builds quickly to your testers and to the app stores. Removing that friction of manual builds and deployments will get your team moving faster (though this is true no matter the platform). Also, make sure that your native designs are simpler for your MVPs. Bring in more complex designs once you've established product fit, and you have feedback and feature requests coming in.

React Native may or may not have a long lifespan, but keeping as much of your business logic in reusable Javascript modules will absolutely make it easier to transition off the platform in case something catastrophic happens to the RN community. I would absolutely classify RN as something you can use today, especially if you aren't willing to put together scrum teams of dedicated iOS or Android engineers.

Good luck!