Mobile15-minute read

React Native Beginners Tutorial—A Cold Dive

The development of cross-platform mobile applications has always been a source of many challenges. Although tools like Apache Cordova and Haxe do solve some of the associated issues, they are still not the ideal solution in many cases. React Native changes that by providing the power of React.js for mobile platforms and a promise of consistent developer experience across multiple platforms.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

The development of cross-platform mobile applications has always been a source of many challenges. Although tools like Apache Cordova and Haxe do solve some of the associated issues, they are still not the ideal solution in many cases. React Native changes that by providing the power of React.js for mobile platforms and a promise of consistent developer experience across multiple platforms.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Johannes Stein
Verified Expert in Engineering

Johannes is the author of two books and a highly skilled software architect specializing in JS, web tech, and cross-platform development.

Previously At

Gamesys
Share

When React Native was announced, the first reactions were overwhelmingly positive. Traditionally, when we think about web technologies in the mobile space, things like Apache Cordova spring to mind, which allow us to package websites or web applications as applications for mobile platforms. In this React Native tutorial for beginners, we will take a look at the architecture and the philosophy behind React Native, as well as how it differs from other solutions in the same space. By the end of the article, we will have transformed a React “Hello World” application into a React Native one.

Let’s start with React Native basics. It has been officially available since March 2015, having been in private beta since the start of that year, and internally used at Facebook for a while before that. The saying “Rome wasn’t built in a day” generally applies to technology as well. Tools like grunt and platforms like Node.js took years to mature. In the web world, things are moving quickly, and with a huge number of frameworks, packages, and tools coming out every day, developers tend to get a little more skeptical and don’t want to jump on every hype bandwagon only to realize that they ended up in a vendor lock-in situation. We will get into what makes React Native special, why it is a technology worth getting into, and cover a few instances where it’s not all unicorns and rainbows.

Under the Hood

When talking about web technologies on mobile, available solutions usually fall in one of the following categories.

Bundling Web Applications in a Mobile Web Browser

The web application lives in a mobile browser, typically called a WebView. Without any major refactoring, a website or web application works on the mobile device. We may need to consider mobile browser events such as tapping or listening to device orientation changes and the smaller screen for a complete user experience, but we have a working mobile version with minimal effort. Cordova/PhoneGap is the most popular option in this category. Unfortunately this option has a big downside: in some cases, applications developed using Cordova are significantly slower than native applications, especially for graphical-heavy applications. In other cases, the mobile operating system doesn’t actually provide all the features in the WebView that are available in the mobile browser. The user experience can also differ from native applications; this may happen due to the application or the platform itself. This problem may range from scrollbars not feeling the same to having a noticeable delay when tapping on elements.

Compiling to Native Technologies

A completely different solution is to create a native codebase in the end. This happens by transforming the original source code into another programming language. We trade in native performance for an abstraction layer with some uncertainties. In cases of closed-source solutions, we are not even sure what happens under the hood and with what kind of black box we are dealing with. In other cases we are not sure how much the next mobile operating system update will break our code and when fixes or updates will be available. A popular example of this category would be Haxe.

Using a JavaScript Layer

Here, we use the JavaScript engine of the mobile environment and execute our JavaScript there. Native controls are mapped to JavaScript objects and functions, so when we were to call a function called fancyButtonRightHere(), a button would appear on the screen. NativeScript or Appcelerator Titanium are well-known examples of this category.

React Native could be classified as something from the third category. For the iOS and Android versions, React Native uses JavaScriptCore under the hood, which is the default JavaScript engine on iOS. JavaScriptCore is also the JavaScript engine in Apple’s Safari browsers. OS X and iOS developers can actually directly interface with it if they want to.

One big difference is that React Native runs the JavaScript code in a separate thread, so the user interface does not block and animations should be silky and smooth.

React Is the Key Feature

It is worth noting that the “React” in React Native is not put there by accident. To learnReact Native, we need an understanding of what exactly React offers. The following concepts work the same in both React and React Native, although these code examples are tailored to be run in the browser.

Single Rendering Entry Point

When we take a look at a simple React component, the first thing we may notice is that the component has a render function. In fact, React throws an error if there is no render function defined inside the component.

var MyComponent = function() {
  this.render = function() {
    // Render something here
  };
};

The special thing is that we don’t mess with DOM elements here, but we return an XML-based construct that represents what will be rendered in the DOM. This XML-based construct is called JSX.

var MyComponent = function() {
  this.render = function() {
    return <div className="my-component">Hello there</div>;
  };
};

A special JSX transformer takes all of that XML-looking code and converts it into functions. This is what the component after the transformation will look like:

var MyComponent = function() {
  this.render = function() {
     return React.createElement("div", {
       className: "my-component"
     }, "Hello there");
  };
};

The biggest advantage is that by taking a quick look at the component, we always know what it’s supposed to do. For example a <FriendList /> component might render a number of <Friend /> components. We can’t render our components anywhere else than inside the render function, so there is never the concern that we don’t know where exactly our rendered component came from.

Unidirectional Data Flow

To build the content of a component, React provides properties or props for short. Similar to XML attributes, we pass in the props directly to a component and can then use the props inside the constructed component.

var Hello = function(props) {
  this.render = function() {
    return <div className="my-component">Hello {props.name}</div>;
  };
};

var Greeter = function() {
  this.render = function() {
    return <Hello name="there" />
  }
};

This leads to our components being in a tree-like structure, and we are only allowed to pass data when constructing child elements.

Re-Render on Changes

In addition to props, components can also have an internal state. The most prominent example of that behavior would be a click counter that updates its value when a button is pressed. The number of clicks itself would be saved in the state.

Each of the prop and state change triggers a complete re-render of the component.

Virtual DOM

Now when everything is re-rendered when the props or state changes, how come React itself is performing that well? The magic ingredient is the “Virtual DOM.” Whenever something is needed to be re-rendered, a virtual representation of the updated DOM is generated. The Virtual DOM consists of light representations of elements modeled after the component tree, making the process of generating them much more efficient than generating real DOM elements. Before applying the changes to the real DOM, checks are done to determine where exactly in the component tree the changes happened, a diff is created, and only those specific changes are applied.

Getting Started with this React Native Beginners Tutorial

There are certain prerequisites that beginners will need to set up in order to develop in this framework. Since iOS was the first platform supported, and the one we’re covering in this React Native tutorial, we need macOS and Xcode, at least version 6.3. Node.js is also needed. What helps is installing Watchman through the Brew package manager with brew install watchman. While this is not necessarily needed, it helps when dealing with a lot of files inside our React Native project.

React Native: The Sanest Mobile Application Development Framework.

To install React Native, we simply need to install the React Native command-line application with npm install -g react-native-cli. Calling the react-native command then helps us create a new React Native application. Running react-native init HelloWorld creates a folder called HelloWorld in which the boilerplate code can be found.

Terminal animation showing how to set up a React Native "Hello World" app.

Transforming a React Application

With React being the key feature and the core principles coming from the React library, let’s take a look at what we need to transform a minimal React “Hello World” application into a React Native one.

We use some ES2015 features in this code example, specifically classes. It is completely feasible to stick with React.createClass or use a function form similar to the popular module pattern.

var React = require('react');

class HelloThere extends React.Component {
  clickMe() {
    alert('Hi!');
  }
  render() {
    return (
      <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

Step 1: Embrace CommonJS Modules

In the first step we need to change requiring the React module to use react-native instead.

var React = require('react-native');

class HelloThere extends React.Component {
  clickMe() {
    alert('Hi!');
  }
  render() {
    return (
      <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

What is usually a part of the tooling pipeline when developing a React web application is an integral part of React Native.

Step 2: There Is No DOM

Not surprisingly, there is no DOM on mobile. Where we previously used <div />, we need to use <View /> and where we used <span />, the component we need here is <Text />.

import React from ‘react';
import {View, Text, Alert} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(‘hi!');
  }
  render() {
    return (
      <View className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</View>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

While it’s quite convenient to put text directly in <div /> elements, in the native world text can’t be put directly in a <View />. For that we need to insert a <Text /> component.

import React from ‘react';
import {View, Text, Alert} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(‘hi!');
  }
  render() {
    return (
      <View className="box" onClick={this.clickMe.bind(this)}>
        <Text>Hello {this.props.name}. Please click me.</Text>
      </View>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

Step 3: Inline Styles Are the Way to Go

React Native allows us to use the Flexbox modeling instead of messing around with float and inline-block that we are so familiar with in the web world. The interesting thing is that React Native does not use CSS.

import React from ‘react';
import {View, Text, StyleSheet, Alert} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(‘hi!');
  }
  render() {
    return (
      <View style={styles.box} onClick={this.clickMe.bind(this)}>
        <Text>Hello {this.props.name}. Please click me.</Text>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red',
    backgroundColor: '#fff',
    borderWidth: 1,
    padding: 10,
    width: 100,
    height: 100
  }
});

React.render(<HelloThere name="Component" />, document.getElementById('content'));

Using inline styles seems bewildering to beginners. It is similar to the transition React developers had to go through when being confronted with JSX and previously using templating engines like Handlebars or Jade.

The idea is that we don’t have stylesheets globally in the way we use CSS. We declare the stylesheets directly at component level, and so we have all the information we need to see what our component does, the layout it creates, and the styles it applies.

import React from ‘react';
import {Text} from ‘react-native';

var Headline = function(props) {
  this.render = () => <Text style={headlineStyle.text}>{props.caption}</Text>;
};

var headlineStyles = StyleSheet.create({
  text: {
    fontSize: 32,
    fontWeight: 'bold'
  }
});

module.exports = Headline;

Step 4: Handling Events

The equivalent to clicking in web pages is tapping an element on the mobile device. Let’s change our code so that the “alert” pops up when we tap the element.

import React from ‘react';
import {View, Text, StyleSheet, TouchableOpacity, Alert} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert("Hi!")
  }
  render() {
    return (
      <TouchableOpacity onPress={this.clickMe()}>
        <View style={styles.box}>
          <Text>Hello {this.props.name}. Please click me.</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red',
    backgroundColor: '#fff',
    borderWidth: 1,
    padding: 10,
    width: 100,
    height: 100
  }
});

React.render(<HelloThere name="Component" />, document.getElementById('content'));

Instead of events being directly available on <View /> components, we need to explicitly use elements that trigger events, in our case a touch event when pressing the view. There are different types of touchable components available, each of them providing a different visual feedback.

Step 5: Customize Behavior Across Platforms

It is possible to detect which platform the application is running on, by accessing the value of Platform.OS. Let’s say that, in the React Native code example above, we wanted to display a different alert message based on the platform we are running on. We can do it like this:

...
clickMe() {
  var message = ‘';
  if(Platform.OS == ‘ios') {
    message = ‘Welcome to iOS!';
  } else if(Platform.OS == ‘android') {
    message = ‘Welcome to Android!';
  }   
  Alert.alert(message);
}
...

Alternatively, the select method is also available, which provides a switch-like syntax:

…
clickMe() {
  Alert.alert(Platform.select({
    ios: ‘Welcome to iOS!',
    android: ‘Welcome to Android!'
  })
  );
}
...

In order to add a custom font, we need to jump through some hoops. First of all, make sure that the font full name and the font’s file name are the same: iOS will use the font’s full name in order to pick the font up, while Android uses the file name.

So, if your font’s full name is myCustomFont, make sure the font’s file name is myCustomFont.ttf.

After that, we need to create an assets folder and point npm to it. We can do it by creating the folder first, under assets/fonts in the application’s root directory. Any other directory will do, but this is the conventional name used for the fonts directory.

We can tell npm where we have our assets by adding an Assets property under React’s npm integration section, rnpm:

"rnpm": {
  "Assets": [
    "./assets/fonts/"
  ]
}

After we’ve done all that, we can finally run react-native link. That will copy the fonts to the right directories and will add the necessary xml to info.plist on iOS.

Once done, we can use our font by just referencing it in any stylesheet by its full name. Let’s use it on our Text element:

import React from ‘react';
import {View, Text, StyleSheet, TouchableOpacity, Alert} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert("Hi!")
  }
  render() {
    return (
      <TouchableOpacity onPress={this.clickMe()}>
        <View style={styles.box}>
          <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red',
    backgroundColor: '#fff',
    borderWidth: 1,
    padding: 10,
    width: 100,
    height: 100
  },
  message: {
    fontFamily: 'myCustomFont'
  }
});

React.render(<HelloThere name="Component" />, document.getElementById('content'));

Step 7: Moving Things Around

React Native uses the same rules as Flexbox for laying out components. Say we wanted to position our button at the bottom of the screen: let’s wrap our TouchableOpacity with a container View:

<View style={styles.container}>
    <TouchableOpacity onPress={this.clickMe.bind(this)}>
        <View style={styles.box}>
            <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text>
        </View>
    </TouchableOpacity>
</View>

And now let’s define the container style, together with the other already defined styles:

container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }

Let’s focus on justifyContent and alignItems. Those two properties control how the component is aligned respectively along its primary axis and its secondary axis. By default, the primary axis is the vertical one, and the secondary axis is the horizontal axis (you can change that by setting the flexDirection property to row).

justifyContent has six possible values it can be set to:

  • flex-start will position all the elements together, at the beginning of the component’s bounding box.
  • flex-end will position all the elements at the end.
  • center will position all the elements in the center of the bounding box.
  • space-around will spread the components evenly, and will center the components in their created bounding boxes.
  • space-evenly will spread the components evenly as well, but it will try to leave an equal amount of space between the components and the other boundaries.
  • space-between will spread the components by keeping the spacing between adjacent components equal.

alignItems can be set to four possible values: flex-start, flex-end, center, and stretch. The first three behave like they do for justifyContent, while stretch will set the component to occupy all the available space along the axis, so that the axis will be completely filled.

So, since we want our TouchableOpacity to be displayed at the bottom and centered along the horizontal axis, we can change the style like so:

container: {
  flex: 1,
  justifyContent: 'flex-end',
  alignItems: 'center'
}

More information about the values justifyContent and alignItems can have can be found here and here.

Step 8: Registering the Application

When developing with React for the browser, we just need to define a mount point, call React.render, and let React do its magic. In React Native, this is a little bit different.

import React from ‘react';
import {View, Text, StyleSheet, TouchableOpacity, Alert, Platform} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(Platform.select({
      ios: ‘Welcome to iOS!',
      android: ‘Welcome to Android!'
    }));
  }

  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={this.clickMe.bind(this)}>
          <View style={styles.box}>
            <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text>
          </View>
        </TouchableOpacity>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'flex-start',
    alignItems: 'center'
  },
  box: {
    borderColor: 'red',
    backgroundColor: '#fff',
    borderWidth: 1,
    padding: 10,
    width: 100,
    height: 100
  },
  message: {
    fontFamily: 'myCustomFont'
  }
});

var MainComponent = function() {
  this.render = function() {
    return <HelloThere name="Component" />;
  }
};

AppRegistry.registerComponent('MainComponent', function() {
  return MainComponent;
});

We have to register the component for the Objective-C side of things, which is done using the AppRegistry object. The name we give has to match with the name inside the Xcode project.

Our Hello World React Native application has significantly more lines of code than its web counterpart, but on the other hand, React Native takes separation of concerns a bit further, especially because styles are defined with the component.

As a side note, we shouldn’t rebind the clickMe method to the this context in the render method, especially if our React (Native) application grows to be a bit more complex. It rebinds the method on every render call which can become quite a lot. The alternative is to bind the method inside the constructor.

Running the Application

To run the application, we need to replace the contents of the index.ios.js file with the piece of code of our transformed application from the last step. Then we just need to open the Xcode project and press the big Run button. First, a terminal will open with the React Native server, and then the simulator window will appear. The React Native server creates a bundle, which the native application will then fetch. This allows for a web development-like rapid development cycle, where changes will be reflected almost instantly in the simulator.

For Android, it’s enough to add the following to your package.json file, under scripts:

"android-linux": "react-native bundle --platform android --dev false --entry-file index.ios.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/
main/res && react-native run-android"

And then run npm run android-linux. Make sure the android/app/src/main/assets directory exists beforehand.

After the terminal has popped up, our application will then show up in the simulator. Pressing CMD+D will show a development menu. Clicking on the box will then show an alert. The iOS version:

An Apple phone with an alert popup saying "Hi."

And Android renders something like this:

An Android phone with an alert popup saying "Hi."

For distribution, having an application that points to a local development server would not be working out for us. For this reason, we can create the bundle for usage when the React Native server isn’t running with the command react-native bundle. In that case, we need to update the didFinishLaunchingWithOptions method of AppDelegate to use the offline bundle.

This example application is also available on Github.

Working with React Native

Another thing worth mentioning is that we not only use React concepts and JavaScript for our mobile applications, but some of workflows web developers are used to are also available with React Native. When coming from web development, we are used to developer tools, inspecting elements, and live reloading.

The way React Native works is that it puts all of our JavaScript files in a bundle. This bundle is either served from a server or bundled together with the application. The first is incredibly useful for development in the Simulator, as we can enable live reloading. The developer menu React provides is by no means as mighty as the Chrome Developer Tools, but it provides a very web-like developer experience with live reloading and debugging with the Chrome (or Safari) developer/debugger tools.

Web developers are familiar with JSFiddle or JSBin, an online playground for quick web tests. There is a similar environment that allows us to try out React Native in a web browser.

React Native: A Solid, Modern Choice

I had originally suggested a more cautious approach to React Native. Today, it’s a mature and solid choice.

One of the big advantages with React is that it doesn’t impose on your workflow, since it just represents the view layer. Do you want to define your own Grunt pipeline? Or would you rather use Webpack? And will you use Backbone.js for your model needs? Or do you want go with plain JavaScript objects? Answers to all of these questions are totally up to you, because React does not put any restriction on these choices. As the official site had put it: “Since React makes no assumptions about the rest of your technology stack, it’s easy to try it out on a small feature in an existing project.”

To a certain degree, this is true for React Native as well. Mobile developers can integrate React Native as a part of their application, take advantage of the web-inspired development workflow, and choose to integrate the library on a larger scale if needs be.

In any case, one thing is certain: React Native isn’t going away. Facebook has a massive stake in it having multiple React Native-powered applications in app stores. The community around React Native is huge and continues to grow.

Understanding the basics

  • What is React Native?

    React Native is a framework for building native iOS and Android applications using JavaScript. It’s based on the same concepts as React, but uses native components instead of web components to render a user interface (UI).

  • React is a front-end JavaScript library, designed around the concept of using declarative views for efficiency and predictability.

  • If you are supporting more than one platform, React Native gives a solid advantage in terms of reusing core code from your app. You may still have to provide some platform-specific code, but it will certainly be less to write and maintain than if you wrote a native app for each platform.

  • A native app is one that is compiled for, and runs directly on, its target platform. By default, it will have a “look and feel” that fit in with the platform. React Native aims to give you this same native look and feel while still providing extra portability and a familiar methodology.

Hire a Toptal expert on this topic.
Hire Now
Johannes Stein

Johannes Stein

Verified Expert in Engineering

London, United Kingdom

Member since June 30, 2014

About the author

Johannes is the author of two books and a highly skilled software architect specializing in JS, web tech, and cross-platform development.

authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

PREVIOUSLY AT

Gamesys

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

Join the Toptal® community.