Advertisement
Scroll to top

In this tutorial, you'll learn how to lay out React Native apps and how to implement layouts commonly used in apps. This includes the Stack Layout, Grid Layout, and Absolute Layout. I'll be assuming that you already know the basics of styling a React Native app and how to use CSS in general, so I won't dwell too much on StyleSheet.create and how to add styling to different elements.

Project Setup

To simplify things, we are using Expo Snack, which is like CodePen but for React Native. To create a new Snack, simply go to snack.expo.dev. Later, you can also just export it to a local file using the Download as ZIP button in the top-right corner, and then use it like a local project. To set up the project for adding layouts, you need to do two things:

Open the App.js file and replace the default code with the following:

1
import * as React from 'react';
2
3
// Import available layouts

4
5
export default function App() {
6
  return (
7
    // Current Layout will go here

8
  );
9
}

Then, create a file named layouts. We will put all of our layouts in there.

Later on, you can import the components that we'll be creating and then render them from this file. Just remember that any component that we save in the layouts directory shouldn't be rendered with anything else. For example, if we have layouts/FlexStart.js, do the following in App.js:

1
import * as React from 'react';
2
3
// Import available layouts

4
import FlexStart from "./layouts/FlexStart"
5
6
export default function App() {
7
  return (
8
    <FlexStart/>
9
  );
10
}

How to Create Different Layouts

Layouts in React Native use a subset of Flexbox. (I say "subset" because not all features that are in the Flexbox specification are included.) So if you already know Flexbox, then you can readily apply those skills in React Native. It's also worth noting that there are no floats or percentage-based units in React Native. This means that we can only do layouts using Flexbox and CSS positioning.

Stack Layout

The first kind of layout that we will implement is the Stack Layout. For vertical orientation, it stacks elements on top of each other, while for horizontal orientation, the elements are placed side by side. Let's take a look at vertical orientation first:

Vertical Stack Layout

Here's the code to accomplish the layout above:

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View,
5
  Dimensions
6
} from 'react-native';
7
 
8
const { height } = Dimensions.get('window');
9
 
10
const box_count = 3;
11
const box_height = height / box_count;
12
  
13
const styles = StyleSheet.create({
14
  container: {
15
    flex: 1,
16
    flexDirection: 'column'
17
  },
18
  box: {
19
    height: box_height
20
  },
21
  box1: {
22
    backgroundColor: '#2196F3'
23
  },
24
  box2: {
25
    backgroundColor: '#8BC34A'
26
  },
27
  box3: {
28
    backgroundColor: '#e3aa1a'
29
  }
30
});
31
32
export default function VerticalStack() {
33
    return (
34
        <View style={styles.container}>
35
            <View style={[styles.box, styles.box1]}></View>

36
            <View style={[styles.box, styles.box2]}></View>

37
            <View style={[styles.box, styles.box3]}></View>

38
        </View>

39
    );
40
}

Breaking down the code above, we first get the height of the available space for the app to consume. Then we calculate what the height of each box will be. Since we have three boxes, we divide it by three.

1
const { height } = Dimensions.get('window');
2
3
const box_count = 3;
4
const box_height = height / box_count;

For the markup, the boxes should be wrapped inside a container. Common styles are declared in the box object, and unique background colors are applied to uniquely named objects (box1, box2, box3):

1
<View style={styles.container}>
2
    <View style={[styles.box, styles.box1]}></View>

3
    <View style={[styles.box, styles.box2]}></View>

4
    <View style={[styles.box, styles.box3]}></View>

5
</View>

To use Flexbox, you must use the flex property on the container. The value is the amount of space it will consume. If it's 1, it means that it will consume all the available space, provided that the element has no siblings. We'll take a look at an example of using flex with siblings later on.

flexDirection allows you to specify the primary axis of the layout. By default, this is set to column. Setting flexDirection to column means that the children of the container will be laid out vertically (stacked on top of each other), while setting it to row means that the children will be laid out horizontally (side by side). To achieve equal height, set the height of the box to that of the value that we calculated earlier.

1
const styles = StyleSheet.create({
2
  container: {
3
    flex: 1,
4
    flexDirection: 'column'
5
  },
6
  box: {
7
    height: box_height
8
  },
9
  box1: {
10
    backgroundColor: '#2196F3'
11
  },
12
  box2: {
13
    backgroundColor: '#8BC34A'
14
  },
15
  box3: {
16
    backgroundColor: '#e3aa1a'
17
  }
18
});

Here's an image to help you visualize how the content will flow based on the flexDirection that you specified.

Illustration of flexDirection row and column

The method I just showed you is the manual way of doing things. Using the Dimensions to compute the width or height of the elements will fail if your app supports both portrait and landscape device orientation. That's because as soon as the user flips their device, the width or height that you computed earlier will be wrong. React Native won't automatically recompute it for you, so the app ends up looking weird.

Flexbox can actually do the computation for you if you just supply the correct values. To achieve the same layout as above without using the Dimensions, all you have to do is specify flex: 1 for all the boxes instead of specifying the height:

1
box: {
2
    flex: 1
3
},

This is now an example of using flex with siblings. Now we have three siblings with the same flex value. This means that all three of them will equally share the available space since the flex value is the same. (You can actually use any flex value as long as the child elements all have the same value.)

Using this knowledge, you can now achieve layouts with a header, content, and a footer:

1
//header

2
box1: {
3
    flex: 1,
4
    backgroundColor: '#2196F3'
5
},
6
//content

7
box2: {
8
    flex: 10,
9
    backgroundColor: '#8BC34A'
10
},
11
//footer

12
box3: {
13
    flex: .5,
14
    backgroundColor: '#e3aa1a'
15
}

Here's what it will look like:

Stack Layout header content footer

Note that this will be static. So if your main content becomes higher than the maximum available height, then the rest of your content will be hidden. If you expect your content to go over that limit, you can use the built-in ScrollView component to automatically generate a vertical scrollbar just like in web pages.

Horizontal Stack Layouts

To implement horizontal stack layouts, all you have to do is change the flexDirection to row.

1
  container: {
2
    flex: 1,
3
    flexDirection: 'row'
4
  },

If we change the box flex value back to 1, this results in the following output:

The only thing we changed is the flexDirection, which is now set to row. Since the boxes are all set to flex: 1, they will have the same width and height. All the ideas from the vertical stack layout are equally applicable to this one.

Justify Content

If you want to control the distribution of children within a container, you use the justifyContent property on the container.

Below are the five possible values that can be used with this property. In the following examples, the height of each of the children is diminished to demonstrate how each would look. You wouldn't be able to see any difference if the flex value was 1 for each of the children, because they would end up consuming all the available space.

  • flex-start: child elements are aligned toward the starting point. Notice the white background right below the last child. That is how you know that this is using flex-start because all the children are aligned towards the starting point. This leaves a space towards the end.
Flex Start
  • flex-end: child elements are aligned toward the end line. Notice that this time the space is at the starting point.
  • center: child elements are placed towards the center. This time the space is equally divided between the starting and ending point.
Flex Center
  • space-around: child elements are distributed such that there would be equal space around each of them. This means that the elements in the outer part would have less space on their outer side and the space between the two children is doubled.
Flex Space Around
  • space-between: child elements are distributed such that there would be an equal amount of space between each of them.
Flex Space Between

As you may have noticed, each of these style properties is dependent on the height or width of the child elements. It's dependent on the width if the flexDirection is row, and on the height if the flexDirection is column.

For example, space-between won't really have any effect on a vertical stack layout if each of the child elements is using flex to control the height. This is because there will be no more space left for the gap between each child element to consume.

Align Items

At first glance, justifyContent and alignItems might look as if they're doing the same thing. They also share three possible values: flex-start, flex-end, and center, with the addition of a stretch value.

The main difference between justifyContent and alignItems is the axis on which the children are distributed. As you have seen earlier, justifyContent always uses the primary axis when distributing child elements. But alignItems uses the axis opposite to the primary one.

We already know that the axis is determined by the flexDirection that has been set. So if the flexDirection is row, the primary axis flows from left to right. This means that the cross axis will flow from top to bottom. On the other hand, if flexDirection is column then the cross axis will flow from left to right.

Below are some examples of justifyContent and alignItems implemented side by side with the flexDirection of row. The first one uses justifyContent, while the second uses alignItems.

  • flex-start: the positioning of the elements is the same, which is why the alignItems implementation looks exactly like justifyContent.
justifyContent and alignItems flex-start
  • flex-end: now we start to see a difference. In the first instance, it's at the end of the line of the first row, while the second instance appears to be at the starting line of the last row.
justifyContent and alignItems flex-end
  • center: center has the same idea as the rest of the values that we've used so far. In the first instance, the items are centered on the x-axis, while in the second, the items are centered on the y-axis.

justifyContent and alignItems center

  • stretch: use this to have the child elements stretch to fill the container. This is the default value for alignItems, so specifying this value is optional. You've already seen how this works when we implemented vertical and horizontal stack layouts.

Here's the code used in the examples above. Just play with the values for the flexDirection, justifyContent, and alignItems if you want to see how they look:

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View
5
} from 'react-native';
6
7
const styles = StyleSheet.create({
8
  wrapper: {
9
    flex: 1
10
  },
11
  container: {
12
    flex: .5,
13
    flexDirection: 'row',
14
    justifyContent: 'flex-start', //replace with flex-end or center

15
    borderBottomWidth: 1,
16
    borderBottomColor: '#000'
17
  },
18
  container2: {
19
    flex: .5,
20
    flexDirection: 'row',
21
    alignItems: 'flex-start' //replace with flex-end or center

22
  },
23
  box: {
24
    width: 100,
25
    height: 100
26
  },
27
  box1: {
28
    backgroundColor: '#2196F3'
29
  },
30
  box2: {
31
    backgroundColor: '#8BC34A'
32
  },
33
  box3: {
34
    backgroundColor: '#e3aa1a'
35
  }
36
});
37
38
export default function AlignItems() {
39
    return (
40
        <View style={styles.wrapper}>
41
        <View style={styles.container}>
42
            <View style={[styles.box, styles.box1]}></View>

43
            <View style={[styles.box, styles.box2]}></View>

44
            <View style={[styles.box, styles.box3]}></View>

45
        </View>

46
        <View style={styles.container2}>
47
            <View style={[styles.box, styles.box1]}></View>

48
            <View style={[styles.box, styles.box2]}></View>

49
            <View style={[styles.box, styles.box3]}></View>

50
        </View>

51
      </View>

52
    );
53
}

If you want to specify the alignment of individual elements within a container, you can use the alignSelf property. All the possible values for align-items are applicable to this property as well. So, for example, you can align a single element to the right of its container, while all the rest are aligned to the left.

Grid Layout

React Native doesn't really come with a grid layout system, but Flexbox is flexible enough to create one. By using the things we learned so far, we can recreate Grid layouts using Flexbox. Here's an example:

Grid Layout

And here's the code that creates that layout:

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View
5
} from 'react-native';
6
7
const styles = StyleSheet.create({
8
  row: {
9
    flex: 1,
10
    flexDirection: 'row',
11
    justifyContent: 'space-between',
12
    marginBottom: 10
13
  },
14
  box: {
15
    flex: 1,
16
    height: 100,
17
    backgroundColor: '#333',
18
  },
19
  box2: {
20
    backgroundColor: 'green'
21
  },
22
  box3: {
23
    backgroundColor: 'orange'
24
  },
25
  two: {
26
    flex: 2
27
  }
28
});
29
30
export default function Grid() {
31
    return (
32
        <View style={styles.container}>
33
          <View style={styles.row}>
34
            <View style={[styles.box, styles.box2]}></View>

35
            <View style={[styles.box, styles.box3]}></View>

36
            <View style={[styles.box, styles.two]}></View>

37
          </View>

38
          
39
          <View style={styles.row}>
40
            <View style={[styles.box, styles.two]}></View>

41
            <View style={[styles.box, styles.box2]}></View>

42
            <View style={[styles.box, styles.box3]}></View>

43
          </View>

44
45
          <View style={styles.row}>
46
            <View style={[styles.box, styles.box2]}></View>

47
            <View style={[styles.box, styles.two]}></View>

48
            <View style={[styles.box, styles.box3]}></View>

49
          </View>

50
51
          <View style={styles.row}>
52
            <View style={[styles.box, styles.box2]}></View>

53
            <View style={[styles.box]}></View>

54
            <View style={[styles.box, styles.box3]}></View>

55
          </View>

56
57
          <View style={styles.row}>
58
            <View style={[styles.box, styles.box2]}></View>

59
            <View style={[styles.box]}></View>

60
          </View>

61
62
          <View style={styles.row}>
63
            <View style={[styles.box]}></View>

64
          </View>

65
      </View>

66
    );
67
}

From the code above, you can see that we're emulating what they usually do in CSS grid frameworks. Each row is wrapped in a separate view, and the grid items are inside it. A default flex value of 1 is applied to each item so that they will equally share the space available on each row. But for items that need to consume larger space, a higher flex value is applied. This automatically adjusts the width of the other items so it accommodates all the items.

If you want to add spaces between each item in a row, you can add padding to each of them and then create a box inside each one.

This results in the following output:

Grid Layout With Spaces

Absolute Layout

React Native only supports absolute and relative positioning. This shouldn't limit you, though, because you can always combine these with Flexbox to position the different elements anywhere you want.

Let's look at how we would accomplish the following:

We can achieve this easily if we have full command over the positioning values that are available in the browser. But since we're in React Native, we need to think of this the Flexbox way first and then use CSS positioning for the small boxes.

Using Flexbox, this can be achieved in two ways. You can either use row or column for the flexDirection for the main container. How you arrange the different elements will depend on which method you choose. Here we're going to use row for the flexDirection, so the screen will be divided into three columns. The first column will contain the orange box, the second column will contain the black, gray, and green boxes, and the third will contain the blue and small purple boxes.

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View
5
} from 'react-native';
6
7
export default function Absolute() {
8
    return (
9
        <View style={styles.container}>
10
11
        <View style={styles.left}>
12
          <View style={[styles.box, styles.big_orange_box]}>
13
          </View>

14
        </View>

15
          
16
        <View style={styles.middle}>
17
          <View style={[styles.box, styles.big_black_box]}>
18
            <View style={[styles.inner_box, styles.red_box]}></View>

19
          </View>        

20
          <View style={[styles.big_gray_box]}></View>

21
          <View style={[styles.box, styles.big_green_box]}>
22
            <View style={[styles.inner_box, styles.orange_box]}></View>

23
          </View>

24
        </View>

25
  
26
        <View style={styles.right}>
27
          <View style={[styles.box, styles.big_lightblue_box]}>
28
            <View style={[styles.inner_box, styles.black_box]}></View>

29
          </View>

30
          <View style={[styles.inner_box, styles.purple_box]}></View>

31
        </View>

32
        
33
      </View>

34
    );
35
}

If you already know how each of the elements will be laid out, it's only a matter of applying the things we've learned so far. After all, we don't really need to apply CSS positioning on the big boxes, only the small ones.

The first column only has the orange box, so applying justifyContent: 'center' to its container should do the trick. In case you've already forgotten, flexDirection defaults to column. This means that if you set justifyContent to center, the children will be aligned on the center of the y-axis.

The second column has basically the same idea as the first one, only this time we don't want to align all the boxes to the center. What we want is for them to have equal spaces between each other, and justifyContent: 'space-between' gets that job done. But at the same time we also want to center all the children on the x-axis, so we use alignItems: 'center'.

The only tricky part here is that you shouldn't apply any width property to the gray box because we want it to stretch all the way to consume the full width of its parent. Since we didn't apply any width, we should apply alignSelf: 'stretch' to the gray box so that it will consume the full width of its parent.

Next, to position the small red box slightly away from its relative position, we use position: relative and then apply top and left values because its relative position is around the upper-left corner of its parent.

As for the small orange box, we use position: 'absolute' because we need to align it to the upper-right corner of its parent. This works because absolutely positioned elements in React Native are bound to their parent.

The third column basically applies the same idea, so I'm no longer going to explain it.

1
const styles = StyleSheet.create({
2
  container: {
3
    flex: 1,
4
    flexDirection: 'row'
5
  },
6
  left: {
7
    flex: 1,
8
    justifyContent: 'center'
9
  },
10
  middle: {
11
    flex: 5,
12
    justifyContent: 'space-between',
13
    alignItems: 'center'
14
  },
15
  right: {
16
    flex: 1,
17
    justifyContent: 'center',
18
    alignItems: 'flex-end'
19
  },
20
  box: {
21
    width: 100,
22
    height: 100,
23
    backgroundColor: '#333'
24
  },
25
  big_green_box: {
26
    backgroundColor: 'green'
27
  },
28
  big_orange_box: {
29
    backgroundColor: 'orange'
30
  },
31
  big_lightblue_box: {
32
    backgroundColor: '#03A9F4'
33
  },
34
  big_gray_box: {
35
    height: 100,
36
    alignSelf: 'stretch',
37
    backgroundColor: '#ccc'
38
  },  
39
  inner_box: {
40
    width: 20,
41
    height: 20,
42
  },
43
  red_box: {
44
    position: 'relative',
45
    backgroundColor: 'red',
46
    top: 10,
47
    left: 10
48
  },
49
  orange_box: {
50
    position: 'absolute',
51
    backgroundColor: 'orange',
52
    top: 10,
53
    right: 10
54
  },
55
  purple_box: {
56
    position: 'absolute',
57
    backgroundColor: 'purple',
58
    bottom: 10,
59
    right: 10
60
  },
61
  black_box: {
62
    position: 'relative',
63
    backgroundColor: 'black'
64
  }
65
});   

Next, let's try to implement a fixed header and footer layout. This is commonly found in apps that have tab navigation; the tabs are fixed at the bottom of the screen, while the main content can be scrolled.

For us to accomplish this, we need to use the ScrollView component so that if the main content goes over the height of the container, React Native will automatically generate a vertical scrollbar. This allows us to add marginTop and marginBottom to the main content container so that the fixed header and footer won't obstruct the main content. Also, note that the left and right values of the header and footer are set to 0 so that they will consume the full device width.

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View,
5
  ScrollView
6
} from 'react-native';
7
8
const styles = StyleSheet.create({
9
  container: {
10
    flex: 1,
11
    flexDirection: 'column',
12
    justifyContent: 'center'
13
  },
14
  header: {
15
    height: 40,
16
    position: 'absolute',
17
    left: 0,
18
    right: 0,
19
    top: 0,
20
    backgroundColor: '#03A9F4',
21
    zIndex: 10
22
  },
23
  content: {
24
    alignItems: 'center',
25
    marginTop: 50,
26
    marginBottom: 40
27
  },
28
  footer: {
29
    height: 40,
30
    position: 'absolute',
31
    left: 0,
32
    right: 0,
33
    bottom: 0,
34
    backgroundColor: '#8BC34A'
35
  },
36
  box: {
37
    width: 100,
38
    height: 100,
39
    backgroundColor: '#333',
40
    marginBottom: 10
41
  }
42
});
43
44
export default function HeaderFooter() {
45
    return (
46
        <View style={styles.container}>
47
        <View style={[styles.header]}></View>

48
        <ScrollView>
49
          <View style={[styles.content]}>
50
            <View style={[styles.box]}></View>

51
            <View style={[styles.box]}></View>

52
            <View style={[styles.box]}></View>

53
            <View style={[styles.box]}></View>

54
            <View style={[styles.box]}></View>

55
            <View style={[styles.box]}></View>

56
            <View style={[styles.box]}></View>

57
          </View>

58
        </ScrollView>

59
        <View style={[styles.footer]}></View>

60
      </View>

61
    );
62
}

Here's how it will look:

Fixed header and footer

Third-Party Libraries

React Native has a big community behind it, so there's no wonder that a few libraries have already been created to ease the implementation of layouts. In this section, I'll introduce you to a library called React Native Super Grid. You can use it to describe how you want to lay out your app by making use of the FlatGrid element.

You can just import it, and Expo will ask you if you want to install it if you are using Snack. Otherwise, you can install it with the following command:

1
npm install react-native-super-grid

Import the library and extract the different components in your file.

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View
5
} from 'react-native';
6
7
import { FlatGrid, SectionGrid } from 'react-native-super-grid';

FlatGrid is based on FlatList and is for rendering lists without sections. SectionGrid is based on SectionList and is for rendering lists with multiple subsections.

The super-grid API is based on passing arrays of data and methods to render it, rather than putting everything as JSX children. This helps with simplicity, since you do not have to write out the grid and can instead just throw your data in. 

1
import React from 'react';
2
import {
3
  StyleSheet,
4
  View,
5
  Text
6
} from 'react-native';
7
import { FlatGrid } from 'react-native-super-grid';
8
 
9
const styles = StyleSheet.create({
10
  Item: {
11
    color: "red"
12
  },
13
  Grid: {
14
    backgroundColor: "grey"
15
  }
16
})
17
18
export default function JustifyContent() {
19
    return (
20
        <FlatGrid style={styles.Grid}
21
          itemDimension={130}
22
          data={[1,2,3,4,5,6]}
23
          renderItem={({ item }) => (<Text style={styles.Item}>{item}</Text>)}

24
        />
25
    );
26
}

Conclusion

In this tutorial, you learned how to lay out React Native apps. Specifically, you learned how to use React Native's Flexbox to position things. You also learned how to use React Native Super Grid, which makes Grid implementation easier. You can check out the full example code and the Expo Snack, which contains all of the layouts.

In an upcoming tutorial, we'll put everything you learned into practice by recreating UI elements that are commonly found in apps: things like the calendar, lists, and tab navigation.

This post has been updated with contributions from Jacob Jackson. Jacob is a web developer, technical writer, freelancer, and open-source contributor.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.