ARTICLE

In-Depth Styling with React Native and Flexbox

From React Native in Action by Nader Dabit
__________________________________________________________________

Save 37% off React Native in Action with code fccdabit at manning.com.
__________________________________________________________________

This article goes deep into styling with React Native and Flexbox.

Using Flexbox to layout components

Flexbox is a layout implementation that React Native uses to provide an efficient way for users to create UIs and control positioning in React Native. The React Native Flexbox implementation is based on the W3C Flexbox web specification but it doesn’t share 100% of the API. It aims to give us an easy way to reason about, align and distribute space among items in our layout, even when their size is unknown or even when their size is dynamic. Flexbox layout’s only available for use on View components.

You’ve already seen Flexbox used in many of the examples. It makes laying out items much easier than alternative methods; it’s hard not to use it. It’s powerful. It will benefit you greatly to take your time and understand the material in this section.

Here are the alignment properties that you’ll use to control the Flexbox layout:

  • flex
  • flexDirection
  • justifyContent
  • alignItems
  • alignSelf
  • flexWrap

Altering a component’s dimensions with flex

The flex property specifies the ability of a component to alter its dimensions to fill the space of the container it’s in. This value is relative to the flex properties specified for the rest of the items within the same container.

This means that if we have a View element with a height of 300 and a width of 300, and a child View with a property of flex: 1, then the child view completely fills the parent view. If we decide to add another child element with a flex property of flex: 1, they’ll each take up equal space within the parent container.

Another way to look at this is to think of the flex properties as being percentages. For example, if you want your child components to take up 66.6% and 33.3% respectively, you could use flex:66 and flex:33. The first item occupies two-thirds, and the second item occupies one-third of the parent container. The flex number is only important relative to the other flex items occupying the same space. Thinking of flex numbers as percentages makes it much easier to comprehend. Rather than flex:66 and flex:33, you could have specified flex:2 and flex:1 and achieved the same layout effect.

To better understand how this all works, let’s take a look at a few examples shown in figure 1.

Figure 1. Three examples of layouts using the flex property: 1:1 A = {flex: 1} and B = {flex: 1} resulting in each taking up 50% of the space, 1:2 C = {flex: 1} and D = {flex: 2} resulting in C taking up 33% of the space and D taking up 66%, and 1:3 E = {flex: 1} and F = {flex: 3} resulting in E taking up 25% of the space and F taking up 75% of the space.

This is easily achieved by setting the appropriate flex value on the individual elements. Listing 1 shows you the exact steps necessary to create such a layout.

Listing 1 Flex views with a 1:2 ratio, 1:3 ratio, and a 1:4 ratio.


render() {
return (
<View style={styles.container}>
<View style={[styles.flexContainer]}>
<Example style={[{flex: 1},styles.darkgrey]}>A 50%</Example> #A
<Example style={[{flex: 1}]}>B 50%</Example>
</View>
<View style={[styles.flexContainer]}> #B
<Example style={[{flex: 1},styles.darkgrey]}>C 33%</Example>
<Example style={{flex: 2}}>D 66%</Example>
</View>
<View style={[styles.flexContainer]}> #C
<Example style={[{flex: 1},styles.darkgrey]}>E 25%</Example>
<Example style={{flex: 3}}>F 75%</Example>
</View>
</View>
);
}

#A The items have the same flex value, and they take up the same amount of space within their parent container.
#B C takes up 1/3 of the total space, and D takes up 2/3 of the total space.
#C E takes up 1/4 of the total space, and F takes up 3/4 of the total space.

Specifying the direction of the flex with flexDirection

You may notice that the items in our flex containers are laying out in a column (Y-axis), meaning from top to bottom. A is stacked on B, C is stacked on D, and E is stacked on F. Using the flexDirection property, we can change the primary axis of the layout, and therefore change the direction of the layout. flexDirection is applied to the parent view that contains child flex views.

Figure 2. The same example as Figure 5.16, but the flexDirection has been set to ‘row’. Now the items take up space horizontally within the row rather than vertically within the column.

All you need to achieve the layout in figure 2 is to add a single line of code to the flexContainer style, which is the parent container for each of the example components. Changing the flexDirection on this container affects the layout of all its flex children. Add flexDirection: 'row' to the style and see how it changes the layout.

Listing 2 Adding flexDirection: ‘row’ to the parent container

flexContainer: {   #A
width: 150,
height: 150,
borderWidth: 1,
margin: 10,
flexDirection: 'row' #B
},

#A flexContainer is the parent container of each example
#B Adding flexDirection: 'row' to the parent container causes the children to be laid out horizontally.

Two options for flexDirection are row and column. The default setting is column. If you don’t specify a flexDirection property, your content will be laid out in a column.

As you can see, the child elements now lay out left to right. This property is something that you’ll use a lot when developing apps in React Native; it’s important to grasp it and understand how it works.

Defining how space is used around a component with justifyContent

Using the flex property, you can specify how much space each component takes up within its parent container, but what if you’re not trying to take up the entire space? How can you use Flexbox to lay out components using their original size?

justifyContent defines how space is distributed between and around flex items along the primary axis of the container (the flex-direction). justifyContent is declared on the parent container. Five options are available:

  • center
  • flex-start
  • flex-end
  • space-around
  • space-between

Specifying center causes the children to be centered within the parent container. The free space is distributed on both sides of the clustered group of children.

flex-start groups the components at the beginning of the flex column or row depending upon what value is assigned to flexDirection. flex-start is the default value for justifyContent.

flex-end acts in the opposite manner. It groups items together at the end of the container.

space-around attempts to evenly distribute space around each element. Don’t confuse this with distributing the elements evenly in the container; the space is being distributed around the elements. If it were based on the elements you’d expect: “space – element – space – element – space”. Instead Flexbox allocates the same amount of space on each side of the element, yielding: “space – element – space – space – element – space”. In both cases, the amount of whitespace is the same, but in the latter, the space between elements ends up being greater.

space-between doesn’t apply spacing at the start or end of the container. The space between any two consecutive elements is the same as the space between any other two consecutive elements.

Figure 3 demonstrates how each of the justifyContent properties distribute space between and around the flex elements. Every example uses two elements to help depict what’s happening.

Figure 3. Examples of how justifyContent affects the distribution of space between flexible child elements for each of the supported options: center, flex-start, flex-end, space-around, and space-between.

Listing 3 is the code used to generate figure 3. Have a look at it in detail to understand how it works, then try to do a couple things on your on: add more elements to each example to see what happens as the number of items increases and set the flexDirection to row to see what happens when the items are laid out horizontally instead of vertically.

Listing 3 Examples showing each of the justifyContent options

...
render() {
return (
<View style={styles.container}>
<FlexContainer style={[{justifyContent: 'center'}]}> #A
<Example>center</Example>
<Example>center</Example>
</FlexContainer>
<FlexContainer style={[{justifyContent: 'flex-start'}]}> #B
<Example>flex-start</Example>
<Example>flex-start</Example>
</FlexContainer>
<FlexContainer style={[{justifyContent: 'flex-end'}]}> #C
<Example>flex-end</Example>
<Example>flex-end</Example>
</FlexContainer>
<FlexContainer style={[{justifyContent: 'space-around'}]}> #D
<Example>space-around</Example>
<Example>space-around</Example>
</FlexContainer>
<FlexContainer style={[{justifyContent: 'space-between'}]}> #E
<Example>space-between</Example>
<Example>space-between</Example>
</FlexContainer>
</View>
);
}
...

#A An example of how to use the justifyContent: ‘center’ option.
#B An example of how to use the justifyContent: ‘flex-start’ option.
#C An example of how to use the justifyContent: ‘flex-end’ option.
#D An example of how to use the justifyContent: ‘space-around’ option.
#E An example of how to use the justifyContent: ‘space-between’ option.

Aligning children in a container with alignItems

alignItems defines how to align children along the secondary axis of its container. This property is declared on the parent view and affects its flex children as flexDirection does. Four possible values for alignItems are:

  • stretch
  • center
  • flex-start
  • flex-end

stretch is the default alignment, and it’s what we’ve seen in the figures 2 and 3. Each example component’s stretched to fill its parent container. Let’s revisit Figure 1 and see what happens when we use the other options: center, flex-start, and flex-end.

Figure 4. Modified the examples in Figure 1 to use the non-default alignItems properties: center, flex-start, and flex-end.

Because we haven’t specified a precise width for any of the example components, they only take up as much space horizontally as is necessary to render their contents rather than stretch them to fill the entire space. In the first case, alignItems is set to 'center'. In the second case alignItems is set to 'flex-start'. In the final case, alignItems is set to 'flex-end'.

Use the code snippet from listing 4 to change the alignments on each of the examples from listing 1.

Listing 4 Using non-default alignItems properties: center, flex-start, and flex-end

render() {
return (
<View style={styles.container}>
<View style={[styles.flexContainer,{alignItems: 'center'}]}> #A
<Example style={[styles.darkgrey]}>A 50%</Example>
<Example>B 50%</Example>
</View>
<View style={[styles.flexContainer,{alignItems: 'flex-start'}]}> #B
<Example style={[styles.darkgrey]}>C 33%</Example>
<Example style={{flex: 2}}>D 66%</Example>
</View>
<View style={[styles.flexContainer,{alignItems: 'flex-end'}]}> #C
<Example style={[styles.darkgrey]}>E 25%</Example>
<Example style={{flex: 3}}>F 75%</Example>
</View>
</View>
);
}

#A Changing the alignItems property to center.
#B Changing the alignItems property to flex-start.
#C Changing the alignItems property to flex-end.

Now that you’ve seen how to use the other alignItems properties and their effects on the default column layout, why don’t you go ahead and set the flexDirection to 'row' and see what happens?

Overriding the parent container’s alignment with alignSelf

This far, all of the flex properties have been applied to the parent container. alignSelf is applied to an individual flex child directly.

With alignSelf, we can get access to the alignItems property for individual elements within the container. alignSelf gives us the ability to override whatever alignment was set on the parent container, and allows a child object to be aligned independently of its peers. The available options are:

  • auto
  • stretch
  • center
  • flex-start
  • flex-end

The default value for alignSelf is auto. auto takes the value from the parent container’s alignItems setting. The remaining properties: stretch, center, flex-start, and flex-end, affect the layout in the same way as their corresponding properties on alignItems.

In figure 5, the parent container doesn’t have an alignItems value set, and it defaults to stretch. You can see in the first example that the auto value inherits stretch from its parent container. The next four examples: stretch, center, flex-start, and flex-end, layout exactly as you’d expect. The final example has no alignSelf property set, it defaults to auto, and it’s laid out the same as the first example.

Figure 5. Shows how each of the alignSelf properties affect the layout when their parent container’s alignItems property is set to the default value of stretch.

In listing 5, I’ve done something a little different. Rather than supply the style directly to the Example element, I created a new component property align. You can see how align is passed down to the Example component and is used to set the alignSelf property. Otherwise, the example is the same as many others in this article; it explores the effects of each value applied to the style.

Listing 5 Using alignSelf to override the parent container’s alignItems property

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

export default class App extends Component<{}> {
render() {
return (
<View style={styles.container}>
<FlexContainer style={[]}>
<Example align='auto'>auto</Example> #A
<Example align='stretch'>stretch</Example> #B
<Example align='center'>center</Example> #C
<Example align='flex-start'>flex-start</Example> #D
<Example align='flex-end'>flex-end</Example> #E
<Example>default</Example> // F
</FlexContainer>
</View>
);
}
}

const FlexContainer = (props) => (
<View style={[styles.flexContainer,props.style]}>
{props.children}
</View>
);

const Example = (props) => (
<View style={[styles.example,
styles.lightgrey,
{alignSelf: props.align || 'auto'}, #G
props.style
]}>
<Text>
{props.children}
</Text>
</View>
);

const styles = StyleSheet.create({
container: {
marginTop: 50,
alignItems: 'center',
flex: 1
},
flexContainer: {
backgroundColor: '#ededed',
width: 120,
height: 180,
borderWidth: 1,
margin: 10
},
example: {
height: 25,
marginBottom: 5,
backgroundColor: '#666666'
},
});

#A Setting alignSelf to auto picks up the parent container’s value of stretch.
#B Setting alignSelf explicitly to stretch.
#C Set the example’s alignSelf property to center.
#D Set the example’s alignSelf property to flex-start.
#E Set the example’s alignSelf property to flex-end.
#F The default value for alignSelf is auto.
#G The align property is used to set the Example component’s alignItems style.

Preventing clipped items with flexWrap

You learned earlier that the flexDirection property takes two values, column (the default) and row. You saw how column laid out items vertically and how row laid out items horizontally. What I didn’t show you was a situation in which items flowed off the screen because they didn’t fit.

flexWrap takes two values: nowrap and wrap. The default value is nowrap, meaning that items flow off the screen if they don’t fit. The items are clipped, and the user can’t see them. To work around this problem, we use the wrap value.

Figure 6. An example of two overflowing containers, one with flexWrap set to nowrap and the other with flexWrap set to wrap.

As you can see in Figure 6, the first example uses nowrap, and the squares flow off of the screen. The row of squares is chopped off at the right edge, and you can’t see anything after that point. The second example uses wrap, and the squares wrap around and start a new row.

Listing 6 Example of how the flexWrap values nowrap and wrap affect layout

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

export default class App extends Component<{}> {
render() {
return (
<View style={styles.container}>
<NoWrapContainer> #A
<Example>A nowrap</Example>
<Example>1</Example>
<Example>2</Example>
<Example>3</Example>
<Example>4</Example>
</NoWrapContainer>
<WrapContainer> #B
<Example>B wrap</Example>
<Example>1</Example>
<Example>2</Example>
<Example>3</Example>
<Example>4</Example>
</WrapContainer>
</View>
);
}
}

const NoWrapContainer = (props) => (
<View style={[styles.noWrapContainer,props.style]}> #C
{props.children}
</View>
);

const WrapContainer = (props) => (
<View style={[styles.wrapContainer,props.style]}> #D
{props.children}
</View>
);

const Example = (props) => (
<View style={[styles.example,props.style]}>
<Text>
{props.children}
</Text>
</View>
);

const styles = StyleSheet.create({
container: {
marginTop: 150,
flex: 1
},
noWrapContainer: {
backgroundColor: '#ededed',
flexDirection: 'row', #E
flexWrap: 'nowrap',
borderWidth: 1,
margin: 10
},
wrapContainer: {
backgroundColor: '#ededed',
flexDirection: 'row', #F
flexWrap: 'wrap',
borderWidth: 1,
margin: 10
},
example: {
width: 100,
height: 100,
margin: 5,
backgroundColor: '#666666'
},
});

#A The first example with flexWrap set to nowrap. The squares overflow off the screen.
#B The second example with flexWrap set to wrap. The row of squares wraps around to start a new line rather than overflowing off the screen.
#C Using the noWrapContainer style for the first example.
#D Using the wrapContainer style for the second example.
#E The noWrapContainer sets the flexDirection to row and flexWrap to nowrap.
#F The wrapContainer sets the flexDirection to row and flexWrap to wrap.

It’s easy to see which behavior is more preferable when laying out tiles, but you may come across a situation in which nowrap serves you better. Either way, you should now have a clear understanding of Flexbox and the many ways in which it can help you build responsive layouts in React Native.

That’s all for now. For more insight into React Native, check out the book on liveBook here and see this slide deck.

About the author:
Nader Dabit has been developing with React Native since the framework’s release and is active in the React Native community.

Originally published at freecontent.manning.com.

Written by

Follow Manning Publications on Medium for free content and exclusive discounts.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store