We’ve been using React Native for over a year now and we’re loving how quickly we can create feature-rich and performant apps for iOS and Android. So far we’ve put 4 React Native apps into production. With each project we tried things out and learned lots along the way. We want to share some of that in this series of posts about React Native.
In Part 1 I talked about how styling in React Native is very different from styling on the web. It took us some time to get used to, but after using React Native for a while we’ve seen how its styling concepts provide useful and flexible ways of creating well-structured, extensible apps.
The biggest head-shifts were:
- Styles being scoped to components rather than globally across the app
- Styles not cascading down to their children
On the web, you might set up some base styles like this:
This would affect all text and text inputs, except those with color rules defined with more specific selectors.
You might also define rules that affect certain classes of DOM element:
There is no equivalent for this in React Native though. Styles can only be passed directly to a component, in the same way that you would pass styles to DOM elements through the style attribute:
There are plenty of modules out there that provide tools and constraints for organising React and React Native applications. Instead of heading straight to npm to choose our favourite, we though we’d explore the possibilities of what we could do just using React Native. These are some of the patterns we’ve found useful.
Inspired by CSS preprocessors like SASS, mixins are a great way of keeping your styles DRY and establishing consistency.
One method we tried out for sharing styles was to compose React Native components into our own custom set of components. For example, a HeadingText component:
Which can be used like this:
Base style sets
In many cases though, creating custom components just for styling overrides seems like overkill, adding unnecessary complexity to your app.
Another option is to create base styles that can be applied to components when they are used:
In one of our projects, we used a Tachyons-inspired approach to styling. The meant setting up a single global stylesheet that all component definitions would import.
The stylesheet defines a set of styling primitives that are shorthands for a specific set of rules:
The actual file is currently over 600 lines long. Applying styles to a component looks like this:
We found that a big time saver with this pattern was not having to think about the naming of styles. This was particularly noticeable when modifying components. Often even a small change to the way a component works means that the names of styles don’t fit. With a tachyons-style approach, once you get to know the style primitives, modifying the styles of a component is quick and easy.
The downside though is that inevitably there will be some styles you want to apply which can’t be described by your primitives. For example, if a component needs a very specific margin, or the width of a component needs to be a certain proportion of the screen width. These styles are usually best defined within the component definition, rather than adding new styles to the global stylesheet.
Here’s a simple example of how functions helped us implement some responsive styles. Originally, we created a simple method for switching styles based on screen height (we found height to be more useful than width):
But this started to get messy when we wanted to define rules for more than 2 screen sizes:
So we wrote a small function that lets us do this:
Passing context (props, state, etc.) to stylesheets
Similarly, functions helped us to simplify the way we were allowing a component’s styling to adapt to props. In this example, a <Button/> component can be passed colour (string) and disabled (boolean) props, both of which affect it’s style:
Having the prop -> style logic within the component definition felt a bit unclear, so we tried out moving it into the style definition. Instead of returning the stylesheet itself, we return a function that takes props as an argument and returns the stylesheet:
For us, this felt clearer and allowed us to keep styling logic within our stylesheets.
This approach has a downside in terms of optimisation though. When StyleSheet.create is called, React Native sends the styles to the native layer. In the example above, StyleSheet.create is called each time the component renders, which will happen whenever props or state change. This could have a noticeable performance impact if the component needs to re-render frequently, or if lots of instances of <Button/> are instantiated.
There are also lots of different libraries to explore: styled-components makes styling a primary constituent of your component definitions; React Native Extended StyleSheet brings lots of the features of CSS to React Native; and Further demonstrates how styling could be composed with functional programming.
Illustrations by Sam Russell Walker
We won’t share your email address with any organsations other than our technology providers. There’s an unsubscribe link in every newsletter we send.