How to Use Styled Components in React Native

Covering installation, basic usage, props and TypeScript support

Styled Components has been the most widely adopted solution for component-based CSS for a while now, with the package complimenting Javascript frameworks in general, in-particularly so for web-based applications.

But how effective is using Styled Components for React Native projects, that take a departure from styling DOM elements and instead require styling for JSX-Wrapped native mobile components? This article tackles this question by firstly looking at the differences between the two solutions, before exploring some implementations:

  • The differences between Styled Components and React Native’s styling solution in accordance with Stylesheet will firstly be discussed, along with their benefits and shortcomings.
  • Implementation will then be explored, covering how to install and start using Styled Components within React Native, with useful use cases to start using it effectively with props.
  • Finally, TypeScript support will be covered — a critical feature that Styled Components should use when a large amount of props are used.

This piece focuses on implementing styled-components for React Native. If you would like more details using the package with React JS, I have published an article specifically for do in so for theming: React Dark Mode with Styled Theming and Context.

On a high level, Styled Components offers the following benefits for React Native:

  • It allows us to use plain CSS, that may feel more natural for developers coming from web-based projects.
  • It can minimise syntax within components if you are heavily embedding style props.
  • It can work alongside Stylesheet’s or inline component styling. E.g. you can embed native styles within a styled component via the style prop.

This last point is interesting, as it suggests it is perfectly fine to adopt two styling solutions within a project, a departure from adhering to one solution for a particular task —a navigator or global state being good examples where multiple solutions are not necessary. Styling is different in this case; Styled Components does a couple of things much better than object-based styles, whereas Stylesheet and inline styling offer their own benefits too.

These benefits will become apparent throughout the piece. Let’s firstly take a look at some real-world comparison between the two.

Comparing StyleSheet with Styled Components

React Native’s solution to styling components is via its Stylesheet API, that supports a range of style properties that mostly reflect CSS. Stylesheet in React Native however does not recognise the stylesheet language of CSS, and instead relies on objects, where property names are camel cased and non-numeric values wrapped in quotes:

<Text style={{ fontSize: 14, fontWeight: '600' }}>
React Native styling!
</Text>

This is actually how React JS supports styling too, although you rarely use the style prop in favour of the className prop.

As you can see, we immediately see the differences that ascertain to object -based styling:

  • Camel-cased property names.
  • Values that are not inherently numbers need to be wrapped in quotes — including the fontWeight property, that in addition to numbers could also be normal and bold.
  • A lack of units for certain values, e.g. A font size of 14 instead of 14px. This carries over into padding, margin, shadows, width and height.

React Native’s “unit-less” values render differently on each device depending on the pixel density of the screen. A fontSize of 14 would render differently on screens with high pixel density than that of a lower pixel density due to the added precision. When considering this, it does not make a lot of sense to append px to CSS in React Native.

To read more about pixel density and how to access it and the font scale property, check out PixelRatio from the official docs.

With Styled Components however, you must still append the px or % unit to your property values, just like we’d do in React JS:

// styled component
const StyledText = styled.Text`
font-size: 14px;
`;
// Stylesheet
const styles = Stylesheet.create({
fontSize: 14
});

React Native Styled Components does not recognise em or rem units, and will cause a runtime error if you try to use them. The usage of px is a topic of discussion on GitHub, as seen in this issue.

Styled Components in React Native actually relies on the css-to-react-native package to convert CSS from a styled component to a recognisable Stylesheet object for React Native to handle. The reader does not need knowledge of this package to use Styled Components, but is worth noting if they’re interested in their own CSS to Stylesheet solution.

Beyond differences in syntax, the other major change is that Styled Component exports are in fact components themselves and not Stylesheet objects. This means that we can omit the style prop completely in favour of an imported component with styles already attributed to it, courtesy of Styled Components.

To highlight the benefit of this, take a look at the SafeAreaView component, where you will most likely have something very similar to the following throughout your React Native projects:

import { SafeAreaView } from 'react-native'const App = () => (
<SafeAreaView style={{ flex: 1 }}>
...
</SafeAreaView>
);

Having the flex: 1 property is important for SafeAreaView to consume the entire screen, but wouldn’t it be nice if the component came with this property by default? This is exactly what can be done with Styled Components; we can export a Styled Component with the same name as its React Native counterpart and use throughout the project:

// styles.jsximport styled from 'styled-components'export const SafeAreaView = styled.SafeAreaView`
flex: 1,
`;

With a SafeAreaView now defined with styling, courtesy of Styled Components, it can now be imported with styling already assigned:

// App.jsximport { SafeAreaView } from './styles'const App = () => (
<SafeAreaView>
...
</SafeAreaView>
);

And we have no more style prop! Using Styled Components in this way is a great example of how it can be used in conjunction with Stylesheet.

If you are concerned about confusing native components with styled ones, it is common to prefix the styled component name with “Styled”. In the above example, you could change the export name to StyledSafeAreaView.

It is also important to highlight that sometimes, your syntax will be completely different between Styled Components and Stylesheet, which could also affect how you manage props and other metadata that determine styles (we’ll visit props in the next section). Consider styling a box shadow. This is how we define a box shadow with Stylesheet:

// styling a box shadow with `Stylesheet`import { StyleSheet } from 'react-native'export const styles = StyleSheet.create({
shadowColor: '#000',
shadowOpacity: 0.25,
shadowOffset: {
width: 0,
height: 2
}
}

The structure of this object is completely different to what we’ve come to expect in CSS, where the entire box shadow is defined in one statement:

// styling a box shadow with a styled componentexport const StyledView = styled.View`
box-shadow: 0px 2px 1px rgba(0,0,0,0.25);
`;

The shadowOffset width and height consume the first two property values, followed by a blur (that is not configured in the Stylesheet version), followed by the spread colour. Notice though that Stylesheet provides the shadowOpacity property, whereas the CSS counterpart does not. In fact, here is the full signature of box-shadow:

box-shadow: none|h-offset v-offset blur spread color |inset|initial|inherit;

For this reason, the opacity has been implemented in the spread colour as an rgba value instead, with the alpha being set to 0.25.

The same result visually, but very different syntax that will undoubtedly have repercussions on your global styles or theming values. Nonetheless, if you wish to migrate some styling to Styled Components, adopting an iterative approach of converting a subset of components gradually should prevent too many simultaneous changes to your code base.

The next section will take a look at installing Styled Components, and how to pass props to Styled Components to determine specific property values.

Setting up Styled Components in React Native

Installing styled-components is done the same way as in React JS apps, using yarn or npm. Include the types package for TypeScript based projects:

yarn add styled-components @types/styled-components

When importing objects such as styled, they must be imported from styled-components/native, and not the top-level styled-components we’re accustomed to using with React JS projects:

import styled from 'styled-components/native'

The documentation does briefly touch on configuring a metro bundler to redirect import directories, but this may be confusing to developers who have came from React JS and assume that React Native uses the same components — it does not.

In fact, the styled object from styled-components/native supports most the primitive react-native components you would need to style. As an example, here is a TouchableHighlight component being styled with flexbox properties:

import styled from 'styled-components/native'export const StyledTouchableHighlight = styled.TouchableHighlight`
flex-basis: 33%;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
padding: 5px 0;
`;

This StyledTouchableHighlight component now comes loaded with flex properties that will fit to 33% of whatever orientation it is aligned to. In addition, content within the component will be vertically and horizontally aligned to the center.

Passing props into Styled Components is done in the same way we use props for any other component within JSX. It is common to either pass style props directly, or some conditional logic that the styled component itself will use to determine what styles to use:

// a styled.Text component with props<StyledText
color='#ff0f0f' // red
highlighted={true}
>
My Styled Text
</StyledText>

The color prop from above simply provides a style value, but the highlighted prop, being a boolean, provides logic for the styled component to use. We can test this prop within the styled component like so:

import styled from 'styled-components'export const StyledText = styled.Text`
color: ${props => props.color}
backgroundColor: ${props => props.highlighted
? 'yellow'
: 'transparent'
};
`;

As you can see, styled components rely heavily on template literals, otherwise known as literal strings, to embed expressions within the stylesheet itself. The ${ // expression } syntax is called a placeholder, where further style logic can be embedded.

Within a styled component, a props object is passed into every placeholder, that expects a function with props being its only argument.

The above StyledText example is actually short-hand syntax in place of a full-blown blocked function. This is what our styled component would look like with functions containing their own parameters brackets, blocks and return statements:

import styled from 'styled-components'export const StyledText = styled.Text`
color: ${(props) => {
return(props.color);
}


backgroundColor: ${(props) => {
const highlightColor = props.highlighted
? 'yellow'
: 'transparent'
return(highlightColor);
}
;
`;

This just exemplifies that props can be processed within the styled component just like any other function, although it is good practice to keep them pure.

To demonstrate props with simple conditionals, let’s say we wish to create a container with a thick border that changes colour when that container is selected. This will be a styled.View component, and handle styling via props depending if it is selected or not.

To make this element interactive, the Container needs to be wrapped in a touchable component — we’ll use TouchableOpacity this time. The JSX is as follows:

// StyledContainer is wrapped by a `TouchableOpacity` const Container = (props) => { const [selected, setSelected] = useState(false);return(
<TouchableOpacity
activeOpacity={0.75}
>
<StyledContainer
backgroundColor='#f1f1f1'
shadowColor='rgba(232,232,232,0.6)'
borderColor={selected ? 'blue' : '#eee'}
>
<Text>Title</Text>
<Text>Short description</Text>
</StyledContainer>
</TouchableOpacity>
);

Here is an example of what that container could be as a styled component. There nothing groundbreaking here, just CSS properties working alongside props:

export const Container = styled.View`
display: flex;
border-width: 2px;
border-radius: 6px;
border-color: ${props => props.borderColor};
background-color: ${props => props.backgroundColor};
width: 100%;
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
padding: 15px 10px;
box-shadow: ${props => props.shadowColor === undefined
? `none`
: `0px 3px 3px ${props.shadowColor}`
}
;
`;

Notice that we have used a placeholder within a placeholder within the box-shadow property to embed the supplied shadowColor prop amongst the rest of box-shadow’s values.

Components that rely on many props will benefit from TypesScript support to ensure the correct types are being passed into them. Let’s briefly cover TypeScript support next.

Styled provides a generic to pass in types for that particular component:

// generics allow us to type styled component propsconst StyledView = styled.View<T>`
...
`;

If we were to apply a type to the above Container component, an interface could be created to satisfy the props it is using:

// typing a styled component with an interface export interface ContainerInterface {
backgroundColor: string;
borderColor: string;
shadowColor: string;
}
export const Container = styled.View<ContainerInterface>`
...
`;

This simple solution ensures that 3 props (and 3 props only) are passed into the styled component. There are no optional types here, but in cases where boolean props are used, you could make the prop optional and assume the value of false if undefined:

export interface ButtonInterface = {
active?: boolean;
label: string;
disabled: boolean;
}
export const StyledButton = styled.TouchableOpacity<ButtonInterface>`
color: ${props => props.active === undefined
? `gray`
: props.active === true
? `green`
: `gray`
};
opacity: $props => props.disabled === undefined
? 1
: props.disabled === true
? 0.5
: 1
};
`;

Another way to support optional props is by wrapping the type with the Partial<T> utility type. The following statement will make all ButtonInterface types optional, whereby any subset of supplied types will be valid:

export const StyledButton = styled.TouchableOpacity<Partial<ButtonInterface>>`
...
`;

As a final note, it is good practice to define all your interfaces as exports in a separate types.ts file.

In Summary

The reader should now have enough knowledge to start using Styled Components in React Native, and be able to recognise the best scenarios in their projects to use it.

As a next step, you may be interested in integrating your themes into Styled Components, via props. To set up your theming, check out my article on configuring your own themes in React Native:

React Navigation v4 is used here, but the same concepts and theming mechanics can be applied to v5 too.

Programmer and Author. Director @ JKRBInvestments.com. Creator of LearnChineseGrammar.com for iOS and Android.

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