
How to do it...
- First, we need to create a Post folder in the root of the project. We need to also create an index.js and a styles.js file in the new Post folder. We will use this Post component to display each post for our app. Finally, we need to add a data.json file to the root of the project, which we will use to define a list of posts.
- Now we can move on to building the App.js component. First, we need to import the dependencies for this class. We are going to use a ListView component to render the list of posts. We'll also need Text and View components for content containers. We are going to create a custom Post component to render each post on the list, and we will also need to import the data.json file:
import React, { Component } from 'react'; import { ListView, StyleSheet, Text, View } from 'react-native'; import Post from './Post'; import data from './data.json';
- Let's create the class for the App component. Here, we will use the data from the .json file to create the dataSource for the list. We will add some actual data to our data.json file in the next step. In the render method, we are going to define a simple top toolbar and the List component. We are going to use the Post component for every record and get the dataSource from the state.
If you have any questions regarding the ListView component, you should take a look at the recipe in Chapter 2, Creating a Simple React Native App, where we created a list of orders:
const dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2, }); export default class App extends Component { state = { dataSource: dataSouce.cloneWithRows(data.posts), }; render() { return ( <View style={styles.container}> <View style={styles.toolbar}> <Text style={styles.title}>Latest posts</Text> </View> <ListView dataSource={this.state.dataSource} renderRow={post => <Post {...post} />} style={styles.list} contentContainerStyle={styles.content} /> </View> ); } }
- Two files are still missing: the .json file with the data and the Post component. In this step, we will create the data that we are going to use for each post. To make things simple, there is only one record of data in the following code snippet, but the rest of the POST object I used in this recipe can be found in the data.json file of the code repository for this recipe, located at https://github.com/warlyware/react-native-cookbook/blob/master/chapter-3/tablet-flexbox/data.json:
{
"posts": [
{
"title": "The Best Article Ever Written",
"img": "https://i.imgur.com/mf9daCT.jpg",
"content": "Lorem ipsum dolor sit amet...",
"author": "Bob Labla"
},
// Add more records here.
]
}
- Now that we have some data, we are ready to work on the Post component. In this component, we need to display the image, title, and button. Since this component does not need to know about state, we will use a stateless component. The following code uses all the components we learned about in Chapter 2, Creating a Simple React Native App. If something is unclear, please review that chapter again.
This component receives the data as a parameter, which we then use for displaying the content in the component. The Image component will use the img property defined on each object in the data.json file to display the remote image:
import React from 'react'; import { Image, Text, TouchableOpacity, View } from 'react-native'; import styles from './styles'; const Post = ({ content, img, title }) => ( <View style={styles.main}> <Image source={{ uri: img }} style={styles.image} /> <View style={styles.content}> <Text style={styles.title}>{title}</Text> <Text>{content}</Text> </View> <TouchableOpacity style={styles.button} activeOpacity={0.8}> <Text style={styles.buttonText}>Read more</Text> </TouchableOpacity> </View> ); export default Post;
- Once we have defined the component, we also need to define the styles for each post. Let's create an empty StyleSheet export so that the Post component relying on styles.js will properly function:
import { StyleSheet } from 'react-native'; const styles = StyleSheet.create({ // Defined in later steps }); export default styles;
- If we try to run the app, we should be able to see the data from the .json file on the screen. It won't be very pretty though, since, we haven't applied any styles yet.
- We have everything we need on the screen. Now we are ready to start working on the layout. First, let's add styles for our Post container. We'll be setting width, height, borderRadius, and a few others. Let's add them to the /Post/styles.js file:
const styles = StyleSheet.create({ main: { backgroundColor: '#fff', borderRadius: 3, height: 340, margin: 5, width: 240, } });
- By now, we should see small boxes vertically aligned. That's some progress, but we need to add more styles to the image so we can see it onscreen. Let's add an image property to the same styles const from the last step. The resizeMode property will allow us to set how we want to resize the image. In this case, by selecting cover, the image will keep the aspect ratio of the original:
image: { backgroundColor: '#ccc', height: 120, resizeMode: 'cover', }
- For the content of the post, we want to take up all of the available height on the card, therefore we need to make it flexible and add some padding. We'll also add overflow: hidden to the content to avoid overflowing the View element. For the title, we only need to change the fontSize and add a margin to the bottom:
content: { padding: 10, overflow: 'hidden', flex: 1, }, title: { fontSize: 18, marginBottom: 5, },
- Finally, for the button, we will set the backgroundColor to green and the text to white. We also need to add some padding and margin for spacing:
button: { backgroundColor: '#1abc9c', borderRadius: 3, padding: 10, margin: 10, }, buttonText: { color: '#fff', textAlign: 'center', }
- If we refresh the simulator, we should see our posts in small cards. Currently, the cards are arranged vertically, but we want to render all of them horizontally. We are going to fix that in the following steps:

- Currently, we can only see the first three items on the list in a column instead of in a row across the screen. Let's return to the App.js file and start adding our styles. We add flex: 1 to the container so that our layout will always fill the screen. We also want to show a toolbar at the top. For that, we just need to define some padding and color as follows:
const styles = StyleSheet.create({ container: { flex: 1, }, toolbar: { backgroundColor: '#34495e', padding: 10, paddingTop: 20, }, title: { color: '#fff', fontSize: 20, textAlign: 'center', } });
- Let's add some basic styles to the list as well. Just a nice background color and some padding. We'll also add the flex property, which will ensure the list takes all the available height on the screen. We only have two components here: the toolbar and the list. The toolbar is taking about 50 px. If we make the list flexible, it will take all of the remaining available space, which is exactly what we want when rotating the device or when running the app in different screen resolutions:
list: { backgroundColor: '#f0f3f4', flex: 1, paddingTop: 5, paddingBottom: 5, }
- If we check the app in the simulator once more, we should be able to see the toolbar and list being laid out as expected:

- We are almost done with this app. All we have left to do is to arrange the cards horizontally. This can be achieved with flexbox in three simple steps:
content: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-around', },
The first step is applying these content styles via the contentContainerStyle property in the ListView component. Internally, the ListView component will apply these styles to the content container, which wraps all of the child views.
We then set the flexDirection to row. This will horizontally align the cards on the list; however, this presents a new problem: we can only see one single row of posts. To fix the problem, we need to wrap the items. We do this by setting the flexWrap property to wrap, which will automatically move the items that don't fit in the view to the next row. Lastly, we use the justifyContent property and set it to center, which will center our ListView in the middle of our app.
- We now have a responsive app that looks good on a tablet in landscape mode:

And looks just as good in portrait mode:
