Play store banner

Hello! Hi. Goodbye. Bye! I’m feeling the glow of having figured out how to do something that seemed daunting at first, and I’m here to share my findings in the hope that you’ll do it too: build a React Native app from scratch (well, almost) and publish it on the Play Store for the Android camp to use and cherish! I’m reporting back to my team about the process of building in React Native without using Expo and this post is a side effect. If you or your team are gearing up to do the same, this post is for you!

STEP 1 - build a React Native app

    — installed node version >=8.3.0 - brew install node
    — installed react-native-cli_ - npm install -g react-native-cli
    — installed watchman_ - brew install watchman
    — an android device with remote debugging enabled, as described here.
    — installed and configured Android Studio as described here.

Let’s build an inane amazing app, for research purposes only, of course.

In your terminal, navigate to the location where you want to create your app.

$ react-native init HiButton
$ cd HiButton

Next get the boilerplate up and running on your Android device; plug the remote-debugging-enabled device into your machine and check that it is connected to the Android Debug Bridge with abd, then fire it up on the device.

[HiButton]$ abd devices
00fe383ba4c24406        device

[HiButton]$ react-native run-android

You should now see the boilerplate screen on your device with “Welcome to React Native” and some instructions, along with a new terminal window with Metro Bundler running. Shake the device, and click ‘Enable Hot Reloading’. Now let’s edit App.js

// HiButton/App.js
import React, {Component} from 'react'
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native'

const greetings = ["Hello!", "Hi.", "Goodbye.", "Bye!"]

export default class App extends Component {
  constructor(props) {
    this.state = {
      count: 0
  render() {
    let { count } = this.state
    const index = count % 4
    const greeting = greetings[index]
    const nextIndex = (index + 1) % 4
    const next = greetings[nextIndex]

    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>{greeting}</Text>
        <TouchableOpacity onPress={() => this.setState({count})}>
          <Text>{ `Say ${next}` }</Text>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,

But you can build whatever you want. I’ll go get some coffee…
I’m back! What you built is totally rad! let’s get it out to your testers!

Step 2 - creating a file for release

    — Java installed on a Mac Computer
    if you need to do this on a windows machine, or want more details and troubleshooting help, visit Generating Signed APK in the React Native Docs, or go to the Signing Your Applications page of the Android Developers Documentation.

Use keytool to generate a private signing key. First find out where your jdk bin folder is, cd into it, and use the keytool command to generate the key, entering your own details at the prompts.

[HiButton]$ /usr/libexec/java_home
[HiButton]$ cd /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home
[Home]$ sudo keytool -genkey -v -keystore hi-button-release-key.keystore -alias hi-button-key-alias -keyalg RSA -keysize 2048 -validity 10000
Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:  Jeneve Parrish
What is the name of your organizational unit?
  [Unknown]:  Jeneve Parrish
What is the name of your organization?
  [Unknown]:  Jeneve Parrish
What is the name of your City or Locality?
  [Unknown]:  Monson
What is the name of your State or Province?
  [Unknown]:  Maine
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=Jeneve Parrish, OU=Jeneve Parrish, O=Jeneve Parrish, L=Monson, ST=Maine, C=US correct?
  [no]:  y

Generating 2,048 bit RSA key pair and self-signed certificate (**redactedSHA**) with a validity of 10,000 days
        for: CN=Jeneve Parrish, OU=Jeneve Parrish, O=Jeneve Parrish, L=Monson, ST=Maine, C=US
Enter key password for <hi-button-key-alias>
        (RETURN if same as keystore password):
[Storing hi-button-release-key.keystore]

Move the generated .keystore file into the android/app folder of your project. Make sure it is gitignored should you decide to check the project into version control.

Edit the file ~/.gradle/, adding the following global variables:

# ~/.gradle/

Make sure that file doesn’t get checked into version control.
Edit the file android/app/build.gradle in your project folder.

// HiButton/android/app/build.gradle
android {
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty('HI_BUTTON_RELEASE_STORE_FILE')) {
                storeFile file(HI_BUTTON_RELEASE_STORE_FILE)
                storePassword HI_BUTTON_RELEASE_STORE_PASSWORD
                keyAlias HI_BUTTON_RELEASE_KEY_ALIAS
                keyPassword HI_BUTTON_RELEASE_KEY_PASSWORD
    buildTypes {
        release {
            signingConfig signingConfigs.release

Now we can generate the .apk file. finally!

[HiButton]$ cd android
[android]$ ./gradlew assembleRelease

Woot! the .apk file can now be found under android/app/build/outputs/apk/release/app-release.apk.

Step 3 - Releasing the app to the Play Store.

    — a Google Play Console account, or 25$ to register one
    — a privacy policy, which you can generate for free here, at a public url; I put mine in a public repo on github, but your website would surely be a better place.
    — an app icon, 512 X 512 pixels, 32-bit PNG — a feature graphic, 1024w X 500h pixels, JPG or 24-bit PNG

Go to your Google Play Console, click on ‘All Applications > Create Application’, fill in an app title, and hit ‘Create’.

Next fill in details about you and your app, upload icons and images and provide a link to the privacy policy. Hit ‘Save Draft’. A green check mark will appear next to the ‘Store Listing’ item in the sidebar when you have satisfactorily completed this step.

Time to upload your .apk! In the side bar menu, click on ‘App releases’, you’ll be presented with a menu of release tracks, Choose ‘Internal test track’ to get started, if things go well, you can promote this build to Production after testing. Click on ‘Manage’ next to your chosen track. You’ll be prompted to enroll the app into app signing by google play, and on clicking ‘Continue’, you’ll be able to upload the .apk. Fill in the release notes at the bottom of this screen with a description of your app, and hit ‘Save’.

Now you should add testers for your amazing app! go back to ‘App Releases’, and again click ‘Manage’ next to ‘Internal Test’, Create a list and add up to 100 gmail or g-suite addresses. Hit ‘save’ when done adding testers.

Click on ‘Content Rating’ in the sidebar and fill in the survey, click ‘Save’ and then ‘Calculate Rating’. On the next screen you will get to review your app’s calculated rating, then hit ‘Apply Rating’.

Click on ‘Pricing and Distribution’ in the sidebar, and specify whether the app is free or paid, choose countries where it should be available, and tick the appropriate boxes regarding the app’s content and your agreement to content guidelines. Then hit ‘Save Draft’. When you have satisfactorily finished this step, you should see all of the checkmarks are green in the sidebar menu, and ‘Ready to Publish’ next to your app icon up top.

Click again on ‘App Releases’, then ‘Edit Release’ next to ‘Internal Testing’. Scroll to the bottom and click ‘Review’.

Screen Shot of ready for release console

Click on ‘Start Rollout to Internal Test’.

STEP 4 - share your app and celebrate!

You did it! Take some time to celebrate! (because your app won’t be available to test for at least an hour).

Please do not sit here and obsessively reload the page waiting for this:

Screen shot of pending publication

to become this:

Screen shot of published

And then when it does, please don’t keep clicking on ‘View on Google Play’ until this:

Screen shot of app not found

becomes this:

Screen shot of app found