In the first part of this Getting Started with React-Native series, we talked about what’s React-Native and Expo, what are some key similarities and differences between React and React-Native, and we also initialized our project.
In this episode, we’ll build the home screen of our React-Native application.
For a quick reminder, here’s the sketch of it:

Table of contents:
- Splitting index.js and styles.js
- How to separate the main logic and the stylesheets?
- Creating the Header
- Creating the rainbow text
- Using stateless function components
- Fixing the Status Bar
- Using Fragments in React-Native
- Modifying the system status bar's look
- Adding Interactive Elements
- How do you make an element interactive?
- Importing images in React-Native
- Building the High-Score Panel
- DIY Excercise
- Build the leaderboard button by yourself!
- Adding a Copyright Banner & a Speaker Icon
- How to fill up empty spaces in React-Native?
- How to make toggable icons?
Splitting the Home screen
First things first, let’s split up the Home screen into two files: an index.js
and a styles.js
, just so that the main logic and the stylesheets are well-separated.
# the screens’ directory structure as of now
screens
├── Home
│ ├── index.js
│ └── styles.js
└── Routes.js
Let’s initialize the styles.js
with a basic container style:
import { StyleSheet } from "react-native";
export default StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0a0a0a",
justifyContent: "center",
alignItems: "center"
}
});
Next, import the styles in the index.js
, delete the StyleSheet and modify the JSX a bit, so that the index.js
looks like this:
// basic imports ...
import styles from './styles';
export default class Home extends Component {
render() {
return (
<View style={styles.container}>
{/* // this View is empty on purpose! */}
</View>
);
}
}
// notice the deleted styles - they are imported from the styles.js!
Creating the Header
Let’s create the header! We want it to be visible on the Home and the Game screen too, so we will build a component named Header
that will display the rainbow text, and re-use it on both screens.
Simply create a Header.js
file in the components
directory and copy-paste the code below.
import React from "react";
import { Text, View, StyleSheet } from "react-native";
const Header = () => (
<View style={{ flexDirection: "row" }}>
<Text style={[styles.header, { color: "#E64C3C" }]}>c</Text>
<Text style={[styles.header, { color: "#E57E31" }]}>o</Text>
<Text style={[styles.header, { color: "#F1C431" }]}>l</Text>
<Text style={[styles.header, { color: "#68CC73" }]}>o</Text>
<Text style={[styles.header, { color: "#3998DB" }]}>r</Text>
<Text style={styles.header}>blinder</Text>
</View>
);
const styles = StyleSheet.create({
header: {
fontSize: 50,
color: "#ecf0f1",
fontFamily: "dogbyte"
}
});
export { Header };
Because the Header
does not need an internal state, we can define it as a stateless functional component. If you aren’t familiar with the idea of using SFCs yet, Hackernoon has a really great summary of what they are and how they work.
Next off, let’s initialize Components/index.js
:
export * from './Header'
And import the Header component in the Home/index.js
:
import { Header } from '../../components'
// …
<View style={styles.container}>
<Header />
</View>
If you check your Expo Client at this point, your app will look like this:

This looks cool, but there’s a small error that we should fix before moving on to our next component: the iOS status bar blends into the background. We can fix this in a few lines of code on the root level, at the App.js,
with the StatusBar component.
Fixing the Status Bar
First, import the StatusBar
from react-native
and the Fragment
from react
(if you don’t know about React.Fragment yet, be sure to check the documentation, but in a nutshell, you can use Fragments when you don’t want to add another div to the DOM, but you need to return two or more components from somewhere for React).
import React, { Component, Fragment } from 'react';
import { StatusBar } from 'react-native';
Then add the StatusBar
component to our app:
else {
return (
<Fragment>
<StatusBar barStyle="light-content" />
<Routes />
</Fragment>
)
}
It’s just a few lines of code that’s definitely worth adding - the app will now look like this:

That’s one small step for the code, one giant leap for the overall UX.
Adding Interactive Elements to our React-Native App
The logo looks pretty cool, but maybe it’s time to make some elements that the user can interact with - so let’s start with the big Play button that will dominate the screen:
Since we aren’t going to make a traditional button (it will contain an image, a text, and won’t have its own background or border), we won’t use a <Button>
- instead, we’ll use a <TouchableOpacity>
. It’s a react-native
component that gives any component the ability to be tappable and respond to the interactions by dimming the opacity of it. You can read more about it in the React-Native docs.
Let’s import Text, Image, and TouchableOpacity along with View:
import { View, Text, Image, TouchableOpacity } from "react-native";
Create a callback named onPlayPress
. Until we figure out what to do with it, a placeholder console.log()
will be fine:
onPlayPress = () => {
console.log("onPlayPress event handler");
};
And finally, the button itself:
<TouchableOpacity onPress={this.onPlayPress} style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image
source={require("../../assets/icons/play_arrow.png")}
style={styles.playIcon}
/>
<Text style={styles.play}>PLAY!</Text>
</TouchableOpacity>
Notice how you can import images with the require()
function. We will use it along this course because the documentation features it, however, there are better solutions out there, so be sure to check them out if you are aiming for production.
If you run the app, you’ll have to realize that we already passed the styles, but they are not defined yet, so let’s go to styles.js
and create them:
play: {
fontSize: 45,
fontFamily: "dogbyte",
color: "#ecf0f1",
marginTop: 5
},
playIcon: {
height: 60,
width: 60,
marginRight: 15
}
There’s nothing special there that would need any explanation, so let’s move on. The app should look like this at this point:

Let’s continue with the Hi-score component:
we can worry about the layout later.
Building the High Score Panel
The high score panel has a similar layout to the Play button, but it’s not tappable, and it’s slightly smaller too - thus we’ll need another bunch of styles for the new, smaller sizes:
hiscore: {
fontSize: 28.5,
fontFamily: "dogbyte",
color: "#ecf0f1",
marginTop: 5
},
trophyIcon: {
height: 45,
width: 45,
marginRight: 12.5
}
And include the new <Image />
and <Text>
in a <View>
:
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image
source={require("../../assets/icons/trophy.png")}
style={styles.trophyIcon}
/>
<Text style={styles.hiscore}>Hi-score: 0</Text>
</View>
We will make this section show the real high score later in the 5th post of this series in the “Persisting data -
storing the high scores” section.
DIY: Build the Leaderboard Button!
This button will look and behave very similar to the Play button:
At this point, try to pause reading and copy-pasting things from the article, and try to build this on your own! Practice makes perfect. If you are stuck with it, try to look back at the Play button and the Hi-score panel’s source for some inspiration, or if you really don’t feel like it,
check the finished code in the GitHub repo.
Let’s add a copyright banner & speaker icon!
In the sketch, there were a few smaller things on the bottom of this screen: a copyright banner, and a speaker icon for muting the game.
The container is just an absolute positioned View, nor the text style isn’t too exciting:
bottomContainer: {
position: "absolute",
left: 15,
right: 15,
bottom: 12.5 // the 2.5px bottom margin from the text is subtracted from the 15px spacing
},
copyrightText: {
fontSize: 16,
fontFamily: "dogbyte",
marginBottom: 2.5
}
And the JSX isn’t a big hack either:
<View style={styles.bottomContainer}>
<Text style={[styles.copyrightText, { color: "#E64C3C" }]}>
Music: Komiku
</Text>
<Text style={[styles.copyrightText, { color: "#F1C431" }]}>
SFX: SubspaceAudio
</Text>
<Text style={[styles.copyrightText, { color: "#3998DB" }]}>
Development: RisingStack
</Text>
</View>
You may notice that toggling the speaker icon only updates an internal state as of now. We will add music and SFX later, in the 5th post of this series. Let’s define our initial state:
state = {
isSoundOn: true
};
Toggling the sound will also change the icon, and as it has two possible states (music muted and enabled), we’ll have two corresponding icons:
In the render function, we want to dynamically import the icon based on the state:
render() {
const imageSource = this.state.isSoundOn
? require("../../assets/icons/speaker-on.png")
: require("../../assets/icons/speaker-off.png");
// ...
We need to add a TouchableOpacity
with an image in it. It will display the speaker icon, but to push it to the right side of the screen, you can either play with adding margins, or adding a <View style={{ flex: 1 }} />
before the button.
The empty view will fill up all the empty space on the screen because of its flex: 1
property. It may seem a bit odd for first, but it’s a commonly used practice in React-Native development to use this when doing MVPs, but in production, you should probably stick with using margins or any other solution that’s idiomatic to your use case.
<View style={{ flex: 1 }} />
<TouchableOpacity onPress={this.onToggleSound}>
<Image source={imageSource} style={styles.soundIcon} />
</TouchableOpacity>
Currently, our app looks like this:
You may have already noticed that the spacing is messed up, so let’s fix that by adding some spacing with margin:
- For the
TouchableOpacity
wrapping the play button, addmarginTop: 80
to thestyle
property - For the
View
wrapping the Hi-score, addmarginTop: 20
- For the
TouchableOpacity
wrapping the Leaderboard button, addmarginTop: 80
What to expect in Part 3 of this React-Native Tutorial Series
Now that the UI elements can breathe and our home screen looks nice, we can move on the screen that the players are going to spend most of their time on - the game screen.
You can access the code that’s written at this point here.
In the next episode, we're building the main logic of our mobile game. Check it out!
Check out every episode:
- Part I: Getting Started with React Native - intro, key concepts & setting up our developer environment
- Part II: Building our Home Screen - splitting index.js & styles.js, creating the app header, and so on..
- Part III: Creating the Main Game Logic + Grid - creating multiple screens, type checking with
prop-types
, generating ourflex
grid - Part IV: Bottom Bar & Responsible Layout - also, making our game pausable and adding a way to lose!
- Part V: Sound and Animation + persisting data with React-Native AsyncStorage