Build React Native Layouts Without Margins With Stacks

Ivan Mariić

Aug 10, 2021 • 12 min read
Man is watching at the computer

Nowadays, everyone in the world has a mobile phone, and every mobile phone has different dimensions.

This is a big problem for developers and designers because they must provide a perfect UI for all screen sizes. Luckily, we have many tools to solve that problem — one of them is the Stacks library.

Stacks helps us create a responsive layout easily without using margins. Margins are considered bad because they break component encapsulation and make reusability harder. If you want to learn more about margins, check out this interesting article. The Stacks library is built in ReScript. ReScript compiles to JavaScript which means that Stacks can be used in projects written in TypeScript, Flow, and ReScript.

Stacks library is based on the spacer components. Spacer components are components that move the responsibility of managing positions of child components to the parent component. Spacer components work similarly to Flexbox. We pass the right prop to the parent, and then the children will change their position accordingly.

The library contains many layout components, some of them are Stack, Tiles, Box, Column, and Columns. In order to use these components, you have to put one of the main components at the top of your component tree. Grid and StacksProvider are the main components that define which options you want to use in creating an app layout. The library also contains some useful hooks, for example, useCurrentBreakpoint returns the type of device on which the app is installed, and useWindowDimensions returns an object that has screen width and height properties.

 const breakpoint = useCurrentBreakpoint();

const isItemHorizontal = () => {
return breakpoint === 'tablet';
};

useCurrentBreakpoint example

React Native layout tutorial

Here is a simple React Native app that demonstrates a basic usage of the Stacks library. It is a movie app that shows the most popular movies right now.


HomeScreen and DetailsScreen on a mobile phone

HomeScreen and DetailsScreen on a tablet

This is the layout of the application. It consists of two screens — the first screen shows a list of movies while the second screen shows movie details. Please take a look at the StacksProvider component. It is added to the root of our component tree. It uses the spacing prop which defines a default spacing for child components.

   <StacksProvider spacing={2}>
<AppNavigator />
</StacksProvider>
App.js

   <NavigationContainer>
<Stack.Navigator initialRouteName={SCREENS.home}>
<Stack.Screen
options=
name={SCREENS.home}
component={HomeScreen}
/>
<Stack.Screen name={SCREENS.details} component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
AppNavigator

HomeScreen

HomeScreen has two components — MainMovie and MoviesScroll. The root element in MainMovie is Stack. Stack is used for arranging the child components horizontally or vertically. By specifying space props you compute the distance between child elements. The distance is computed this way: spacing * space. In this case, the distance is 4*4 = 16.

   <Stack space={4} align="center" paddingTop={5}>
<Image
style={resolveStyle(styles.tabletImage, styles.mobileImage)}
source={source}
resizeMode="stretch"
/>
<Text style={resolveStyle(styles.tabletTitle, styles.mobileTitle)}>
{title}
</Text>
<Text style={resolveStyle(styles.tabletRating, styles.mobileRating)}>
{vote_average}/10
</Text>
</Stack>
MainMovie.js


MoviesScroll is a very interesting component because it behaves differently on each device type. On mobile phones, the component will be rendered vertically, but on tablets horizontally. The component contains the Box component that behaves the same as View. It is really important to mention that Stack accepts an array of values to determine alignment. The first value of the array will be applied to mobile phones and the second value will be applied to tablets. That way, you achieve responsiveness.

   <Box padding={2}>
<ScrollView
horizontal={isItemHorizontal()}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}>
<Stack
align={['center', 'stretch']}
space={2}
horizontal={isItemHorizontal()}>
{constructMoviesList()}
</Stack>
</ScrollView>

</Box>
MoviesScroll.js

DetailsScreen

DetailsScreen has only one component — MovieDetails. If we click on a movie, DetailsScreen will open. MovieDetails layout is built by using Rows and Row components provided by Stacks. Rows is a spacing component as well. Rows and Row are used to align items vertically. Stacks and Rows are pretty similar except that Rows can’t be aligned horizontally.

   <ScrollView>
<Rows space={4} alignX={['center']} paddingTop={4}>
<Row width="content" padding={3}>
<Text style={titleStyle}>{title}</Text>
<Text style={descriptionStyle}>{overview}</Text>
</Row>
<Row width="content">
<Image
resizeMode="stretch"
style={imageStyle}
source={generateImageSource(poster_path)}
/>
</Row>
<Row width="content">
<Text style={releaseDateAndRatingStyle}>
Release date: {release_date}
</Text>
</Row>
<Row width="content">
<Text style={releaseDateAndRatingStyle}>Rating: {vote_average}</Text>
</Row>
</Rows>
</ScrollView>
MovieDetails.js

Conclusion

Stacks is a very interesting library and a new way to build layouts in React Native. It makes the development easier because you absolutely do not need to use margins. However, the library is not perfect because useCurrentBreakpoint does not work on iOS. Also, the library does not have an option to make font sizes responsive so you have to do that manually

More posts by this author

Ivan Mariić

codestories