Using Styled Components with React Native

2019-03-19

Tldr;

  • Introduction
  • About styled-components
  • Installing styled-components
  • Using styled-components
  • props in styled-components
  • Building the app — “Grocery UI”
  • Adding user avatar image
  • Absolute Positioning in React Native
  • Adding icons in a React Native
  • Adding horizontal ScrollView
  • Adding a vertical ScrollView
  • Building a card component
  • Conclusion

Introduction

Whether you are a web developer or mobile app developer, you know that without the proper styling of your application, the UI would probably suck. Styling an application is important. I cannot put enough emphasis on how important it is for a mobile app to have a pleasing design and good use of colors.

If you are getting into React Native or have already dipped your toes, you know that there are different ways you can style a React Native app. I have already discussed the basics and some of the different ways to style your React Native components in the article below. Such as, to create a new style object you use **StyleSheet.create()** method and encapsulating them. Go check it out 👇

Styling The React Native Way
_React Native comes with many in-built components that you can use to style the cross-platform application. Styling a…_levelup.gitconnected.com

This tutorial is going to be about styling your React Native apps using 💅 Styled Components. Yes, styled-components is a third party library. Using it is a matter of choice, but also another way to add styling to your app, and many might find it easy to use, especially if you have used this library before with other frameworks. One common use case is web apps built with React.

What are Styled Components?

Styled Components is a CSS-in-JS library that enables developers to write each component with their own styles and allows the code to be in a single location. By coupling your styles with the components, it results in optimizing developer experience and output.

In React Native, the styling of components is already done by creating JavaScript objects and if you do not encapsulate them, in most cases, your components and their styling are going to end up in one place.

React Native tends to follow a certain convention when it comes to styling your app. Such as all CSS property names should be in camelCase such as for background-color in React Native is:

1backgroundColor: 'blue'

Occasionally, web developers get uncomfortable by these conventions. Using a third party library like styled components can give you wings. You do not have to switch between the context of conventions much, apart from the properties and React Native’s own Flexbox rules.

Behind the scenes, styled components just converts the CSS text into a React Native stylesheet object. You can check how it does that here.

Enough with story, let’s get to work!

Installing Styled Components

To install the styled-components library in a React Native project, we will first initialize the app. To get started quickly, I am going to use the awesome Expo library. Make sure you have expo-cli installed.

1# To install expo-cli
2
3npm install -S expo-cli
4
5# Generate a project
6
7expo init [YourApp-Name]

When running the last command, the command line prompt will you a few questions. First one is, Choose a template, where I chose expo-template-blank, then enter display name of your app and then either use npm or yarn to install dependencies. I am using npm.

Once all the dependencies are installed, you can open this project in your favorite code editor. The next step is to install the latest version of styled-components library.

1npm install -S styled-components

That’s it for installation.

Using Styled Components

Open up App.js file and make some modifications.

1import React from 'react'
2import { StyleSheet, Text, View } from 'react-native'
3
4export default class App extends React.Component {
5 render() {
6 return (
7 <View style={styles.container}>
8 <Text>Open up App.js to start working on your app!</Text>
9 </View>
10 )
11 }
12}
13
14const styles = StyleSheet.create({
15 container: {
16 flex: 1,
17 backgroundColor: '#fff',
18 alignItems: 'center',
19 justifyContent: 'center'
20 }
21})

From your terminal, run the command: npm run ios if you are on macOS. For Linux and Windows users the command is npm run android, but make sure you have an Android virtual device running in the background. Our code currently looks like below.

Let’s make some changes to it and use our newly installed library. To get started, import the library like below.

1import styled from 'styled-components'

Make changes to the component’s render function like below. Replace both View and Text with Container and Title. These new elements are going to be custom using semantics from styled-components.

1export default class App extends React.Component {
2 render() {
3 return (
4 <Container>
5 <Title>React Native with 💅 Styled Components</Title>
6 </Container>
7 )
8 }
9}

styled-components utilizes tagged template literals to style your components using backticks. When creating a component in React or React Native using styled-components, each component is going to have styles attached to it.

Notice the Container is a React Native View and has styling attached to it.

1const Container = styled.View`
2 flex: 1;
3 background-color: papayawhip;
4 justify-content: center;
5 align-items: center;
6`
7
8const Title = styled.Text`
9 font-size: 20px;
10 font-weight: 500;
11 color: palevioletred;
12`

The complete code for App.js file after changes.

1import React from 'react'
2import styled from 'styled-components'
3
4export default class App extends React.Component {
5 render() {
6 return (
7 <Container>
8 <Title>React Native with 💅 Styled Components</Title>
9 </Container>
10 )
11 }
12}
13
14const Container = styled.View`
15 flex: 1;
16 background-color: papayawhip;
17 justify-content: center;
18 align-items: center;
19`
20
21const Title = styled.Text`
22 font-size: 24px;
23 font-weight: 500;
24 color: palevioletred;
25`

In the above snippet, do take a note that we are not importing a React Native core components such as View, Text, or the StyleSheet object. It is that simple. It uses the same flexbox model that React Native Layouts. The advantage here is that you get the advantage of using the same understandable syntax that you have been using in web development and standard CSS.

Using Props in Styled Components

Often you will find yourself creating custom components for your apps. This does give you the advantage to stay DRY. Using styled-components is no different. You can leverage this programming pattern by building custom components that require their parent components. props are commonly known as additional properties to a specific component. To demonstrate this, create a new file called CustomButton.js.

Inside this file, we are going to create a custom button that requires props such as backgroundColor, textColor and the text itself for the button. You are going to use TouchableOpacity and Text to create this custom button but without importing react-native library using a functional component CustomButton.

1import React from 'react'
2import styled from 'styled-components'
3
4const CustomButton = props => (
5 <ButtonContainer
6 onPress={() => alert('Hi!')}
7 backgroundColor={props.backgroundColor}
8 >
9 <ButtonText textColor={props.textColor}>{props.text}</ButtonText>
10 </ButtonContainer>
11)
12
13export default CustomButton
14
15const ButtonContainer = styled.TouchableOpacity`
16 width: 100px;
17 height: 40px
18 padding: 12px;
19 border-radius: 10px;
20 background-color: ${props => props.backgroundColor};
21`
22
23const ButtonText = styled.Text`
24 font-size: 15px;
25 color: ${props => props.textColor};
26 text-align: center;
27`

By passing an interpolated function ${props => props...} to a styled component's template literal you can extend its styles. Now add this button to App.js file.

1render() {
2 return (
3 <Container>
4 <Title>React Native with 💅 Styled Components</Title>
5 <CustomButton text="Click Me" textColor="#01d1e5" backgroundColor="lavenderblush" />
6 </Container>
7 );
8 }

On running the simulator, you will get the following result.

Building the app — Grocery UI

In this section we are building a UI screen for an app that would be used for a grocery store. You are going to build the home screen that looks like the one below.

We will be using our knowledge of styled-components so let's get started! Open up App.js. Declare a new Container View using styled. Inside the backticks, you can put pure CSS code there with the exact same syntax. The View element is like a div in HTML or web programming in general. Also, create another view called Titlebar inside Container.

Inside Titlebar, it will contain three new elements. One is going to be an image Avatar and the other two are text: Titleand Name.

1import React from 'react'
2import styled from 'styled-components'
3
4export default class App extends React.Component {
5 render() {
6 return (
7 <Container>
8 <Titlebar>
9 <Avatar />
10 <Title>Welcome back,</Title>
11 <Name>Aman</Name>
12 </Titlebar>
13 </Container>
14 )
15 }
16}
17
18const Container = styled.View`
19 flex: 1;
20 background-color: white;
21 justify-content: center;
22 align-items: center;
23`
24
25const Titlebar = styled.View`
26 width: 100%;
27 margin-top: 50px;
28 padding-left: 80px;
29`
30
31const Avatar = styled.Image``
32
33const Title = styled.Text`
34 font-size: 20px;
35 font-weight: 500;
36 color: #b8bece;
37`
38
39const Name = styled.Text`
40 font-size: 20px;
41 color: #3c4560;
42 font-weight: bold;
43`

Run npm run ios and see it in action.

Right now, the content is in the middle of the screen. We need the Titlebar and its contents at the top of the mobile screen. So styles for Container will be as below.

1const Container = styled.View`
2 flex: 1;
3 background-color: white;
4`

Adding user avatar image

I am going to use an image that is stored in assets folder in the root of our project. You are free to use your own image but you can also download the assets for this project below.

amandeepmittal/react-native-workspace_⚛️ + 📱 React Native Things. Contribute to amandeepmittal/react-native-workspace development by creating an account on…_github.com

To create an image with styled-components, you need the Image component. You can use the source props to reference the image based on where it is located.

1<Titlebar>
2 <Avatar source={require('./assets/avatar.jpg')} />
3 <Title>Welcome back,</Title>
4 <Name>Aman</Name>
5</Titlebar>

The styling for Avatar will begin with a width and height of 44 pixels. Having a border-radius exactly half the value of width and height, which makes the image a circle. border-radius is the property that you will be using frequently to create rounded corners.

1const Avatar = styled.Image`
2 width: 44px;
3 height: 44px;
4 background: black;
5 border-radius: 22px;
6 margin-left: 20px;
7`

You will get the following result.

Now notice that the avatar image and the text are piling up. They are taking the same space on the screen. To avoid this, you are going to use position: absolute CSS property.

Absolute Positioning in React Native

CSS properties such as padding and margin are used to add space between UI elements in relation to one another. This is the default layout position. However, you are currently in a scenario where it will be beneficial to use absolute positioning of UI elements and place the desired UI element at the exact position you want.

In React Native and CSS in general, if position property is set to absolute, then the element is laid out relative to its parent. CSS has other values for position but React Native only supports absolute.

Modify Avatar styles as below.

1const Avatar = styled.Image`
2 width: 44px;
3 height: 44px;
4 background: black;
5 border-radius: 22px;
6 margin-left: 20px;
7 position: absolute;
8 top: 0;
9 left: 0;
10`

Usually, with position absolute property, you are going to use a combination of the following properties:

  • top
  • left
  • right
  • bottom

In our case above, we use top and left both set to 0 pixels. You will get the following output.

Adding icons in a React Native

Expo boilerplate comes with a set of different icon libraries such as Ionicons, FontAwesome, Glyphicons, Material icons and many more. The complete list of icons you can find here, a searchable website.

To use the library, all you have to do is write the import statement.

1import { Ionicons } from '@expo/vector-icons'

Inside the Titlebar view, add the icon.

1<Titlebar>
2 {/* ... */}
3 <Ionicons name="md-cart" size={32} color="red" />
4</Titlebar>

Each icon needs props for the name that you can choose, size and color. Right now, if you look at the simulator, you will notice the same problem we had when adding the avatar image. There is no space between the icon and other UI elements inside the title bar.

To solve this, let us use the absolute positioning property as an inline style to <Ionicons />.

1<Ionicons
2 name="md-cart"
3 size={32}
4 color="red"
5 style={{ position: 'absolute', right: 20, top: 5 }}
6/>

Why an inline style? Because Ionicons is not generated using styled-components.

Mapping through a List

Inside components/ folder create a new file called Categories.js. This file is going to render a list of category items for the Grocery UI app.

1import React from 'react'
2import styled from 'styled-components'
3
4const Categories = props => (
5 <Container>
6 <Name>Fruits</Name>
7 <Name>Bread</Name>
8 <Name>Drinks</Name>
9 <Name>Veggies</Name>
10 </Container>
11)
12
13export default Categories
14
15const Container = styled.View``
16
17const Name = styled.Text`
18 font-size: 32px;
19 font-weight: 600;
20 margin-left: 15px;
21 color: #bcbece;
22`

All the data is static right now. Import this component in App.js and place it after Titlebar.

1import Categories from './components/Categories'
2
3// ...
4
5return (
6 <Container>
7 <Titlebar>{/* ... */}</Titlebar>
8 <Categories />
9 </Container>
10)

You will get the following output.

There can be a number of categories. To make the names of categories dynamic, we can send it through App.jsfile.

1const Items = [
2 { text: 'Fruits' },
3 { text: 'Bread' },
4 { text: 'Drinks' },
5 { text: 'Veggies' },
6 { text: 'Meat' },
7 { text: 'Paper Goods' }
8]
9
10// Inside the render function replace <Categories /> with
11
12{
13 items.map((category, index) => (
14 <Categories name={category.text} key={index} />
15 ))
16}

In the above snippet, you are using the map function from JavaScript to iterate through an array render a list of items, in this category names. Adding a key prop is required. To make this work, also modify Categories.js.

1const Categories = props => <Name>{props.name}</Name>

Adding Horizontal ScrollView

This list is right now not scrollable. To make it scrollable, let us place it inside a ScrollView. Open up App.js file place the categories inside a ScrollView, but first, import it from React Native core.

1import { ScrollView } from 'react-native'
2
3// ...
4;<ScrollView>
5 {items.map((category, index) => (
6 <Categories name={category.text} key={index} />
7 ))}
8</ScrollView>

You will notice not a single change in the UI. By default scrollable lists in React Native using ScrollView are vertical. Make this horizontal by adding the prop horizontal.

1<ScrollView horizontal={true}>
2 {items.map((category, index) => (
3 <Categories name={category.text} key={index} />
4 ))}
5</ScrollView>

It works but does not looks good.

Let us add some inline styles to the ScrollView.

1<ScrollView
2 horizontal={true}
3 style={{
4 padding: 20,
5 paddingLeft: 12,
6 paddingTop: 30,
7 flexDirection: 'row'
8 }}
9 showsHorizontalScrollIndicator={false}
10>
11 {items.map((category, index) => (
12 <Categories name={category.text} key={index} />
13 ))}
14</ScrollView>

Now it looks better. The prop showsHorizontalScrollIndicator hides the horizontal scroll bar that by default appears beneath the name of the categories.

Adding a vertical ScrollView

Next step is to add a ScrollView that acts as a wrapper inside the Container view such that the whole area becomes scrollable vertically. There is a reason to do this. You are now going to have items separated into two columns as images with texts related to a particular category.

Modify App.js file.

1return (
2 <Container>
3 <ScrollView>
4 <Titlebar>{/* and its contents */}</Titlebar>
5 <ScrollView horizontal={true}>
6 {/* Categories being rendered */}
7 </ScrollView>
8 <Subtitle>Items</Subtitle>
9 </ScrollView>
10 </Container>
11)

Notice that we are adding another styled component called Subtitle which is nothing but a text.

1const Subtitle = styled.Text`
2 font-size: 20px;
3 color: #3c4560;
4 font-weight: 500;
5 margin-top: 10px;
6 margin-left: 25px;
7 text-transform: uppercase;
8`

It renders like below.

Building a card component

In this section, we are going to create a card component that will hold an item’s image, the name of the item and the price as text. Each card component is going to have curved borders and box shadow. This is how it is going to look like.

Create a new component file called Card.js inside components directory. The structure of the Card component is going to be.

1import React from 'react'
2import styled from 'styled-components'
3
4const Card = props => (
5 <Container>
6 <Cover>
7 <Image source={require('../assets/pepper.jpg')} />
8 </Cover>
9 <Content>
10 <Title>Pepper</Title>
11 <PriceCaption>$ 2.99 each</PriceCaption>
12 </Content>
13 </Container>
14)
15
16export default Card

Currently, it has static data, such as the image, title, and content. Let us add the styles for each styled UI elements in this file.

1const Container = styled.View`
2 background: #fff;
3 height: 200px;
4 width: 150px;
5 border-radius: 14px;
6 margin: 18px;
7 margin-top: 20px;
8 box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
9`
10
11const Cover = styled.View`
12 width: 100%;
13 height: 120px;
14 border-top-left-radius: 14px;
15 border-top-right-radius: 14px;
16 overflow: hidden;
17`
18
19const Image = styled.Image`
20 width: 100%;
21 height: 100%;
22`
23
24const Content = styled.View`
25 padding-top: 10px;
26 flex-direction: column;
27 align-items: center;
28 height: 60px;
29`
30
31const Title = styled.Text`
32 color: #3c4560;
33 font-size: 20px;
34 font-weight: 600;
35`
36
37const PriceCaption = styled.Text`
38 color: #b8b3c3;
39 font-size: 15px;
40 font-weight: 600;
41 margin-top: 4px;
42`

The Container view has a default background of white color. This is useful in scenarios where you are fetching images from a third party APIs. Also, it provides a background to the text area below the image.

Inside the Container view, add an Image and wrap it inside a Cover view. In React Native there two ways you can fetch an image

If you are getting an image from the static resource as in our case, you use source prop with keyword require that contains the relative path to the image asset stored in the project folder. In case of networking images or getting an image from an API, you use the same prop with a different keyword called uri. Here is an example of an image being fetched from an API.

1<Image
2 source={{
3 uri: 'https://facebook.github.io/react-native/docs/assets/favicon.png'
4 }}
5/>

The Cover view uses rounded corners with overflow property. This is done to reflect the rounded corners. iOS clips the images if coming from a child component. In our case, the image is coming from a Card component which is a child to App component.

The Image component takes the width and height of the entire Cover view.

Now let us import this component inside App.js file, after the Subtitle and let us see what results do we get.

1render() {
2 return (
3 <Container>
4 <ScrollView>
5 {/* ... */}
6 <Subtitle>Items</Subtitle>
7 <ItemsLayout>
8 <ColumnOne>
9 <Card />
10 </ColumnOne>
11 <ColumnTwo>
12 <Card />
13 </ColumnTwo>
14 </ItemsLayout>
15 </ScrollView>
16 </Container>
17 )
18}
19
20// ...
21
22const ItemsLayout = styled.View`
23 flex-direction: row;
24 flex: 1;
25`;
26
27const ColumnOne = styled.View``;
28
29const ColumnTwo = styled.View``;

After Subtitleadd a new view called ItemsLayout. This is going to be a layout that allows different cards to be divided between two columns in each row. This can be done by giving this view a flex-direction property of value row. ColumnOne and ColumnTwo are two empty views.

On rendering the screen of the simulator, looks like below.

Conclusion

Have you tried styled-components with React Native before? If not, are you going to try it now in your next project? Do comment below if you do or do not find styled-components a comfortable way to use in your React Native applications. You can extend this application too! Let your imagination wander. You are welcome to submit a PR if you decide to do so.

You can find the complete code for this article in the Github repo 👇

amandeepmittal/react-native-workspace

Originally published at Level up coding

I'm Aman working as an independent fullstack developer with technologies such as Node.js, ReactJS, and React Native. I try to document and write tutorials to help JavaScript, Web and Mobile developers.