App Development

React Native Tips and Tricks: Styling in JS

blog-featured-image-react native tips tricks styling in js JS-510x296

One of the biggest paradigm shifts when developing in React Native, especially coming from a web development background, is learning to think about styles in JavaScript. For many of us, inline styling still feels dirty and we long to cling to some semblance of separate stylesheets, even if they are written in JavaScript.

While the creators of React Native have built-in tools to help us cope, design patterns are still emerging for a relatively young platform. As of writing this, React Native is at v0.31.0. It’s still in its infancy compared with React.js, which has emerged as the pre-eminent JavaScript web platform in the past couple years.

As we have found niche projects for React Native both internally and client-facing over the past few months, several patterns have crystallized to help us write clean, reusable code. For the scope of this post, we will focus on some tips and tricks that have helped make the transition into styling in JavaScript a bit easier.

Platform Styles

Coming from a web development background, having to maintain styles for both Android and iOS takes some getting used to. React Native ships with a couple ways to handle this. For complex components with vast disparities, it might be a good idea to separate out iOS and Android components into separate files. More on this approach can be found in the docs on platform-specific code.

For our purposes here, I will focus on the approach we are using with the Platform module. As the example below illustrates, importing the Platform module into a stylesheet will tap into React Native’s platform-detection logic and apply the appropriate styles. In this case, we are applying a base font family for each platform.

import { Platform, StyleSheet } from 'react-native';
 
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {
        fontFamily: 'Arial',
      },
      android: {
        fontFamily: 'Roboto',
      },
    }),
  },
});

While this pattern is useful, it’s a little verbose. With a bit of configuration, we can fairly easily create our own PlatformStyleSheet that wraps the StyleSheet object and automatically selects and applies platform-specific styles.

/*
* @providesModule PlatformStyleSheet
*/
import { Platform, StyleSheet } from 'react-native';


const PlatformStyleSheet = {
  create(styles) {
    const styleKeys = Object.keys(styles);
    const keptStyles = {};


    styleKeys.forEach((key) => {
      const { ios, android, ...style } = styles[key];


      keptStyles[key] = { ...style, ...Platform.select({ ios, android }) };
    });


    return StyleSheet.create(keptStyles);
  },
};


export default PlatformStyleSheet;

Here we are declaring a PlatformStyleSheet object with a create method that takes in a style’s object as a parameter. It then iterates through the keys of the style object and selects both the generic styles and the styles specific to that platform before returning a StyleSheet created with those keptStyles. The providesModule PlatformStyleSheet is a little bit of syntactic sugar from the folks at Facebook for module loading that allows us to import anywhere from PlatformStyleSheet without having to remember its relative path. So far we’ve chosen to use the providesModule sparingly for ubiquitous modules while defaulting to ES6 module loading via relative path.

Here is an example of a stylesheet created with our new PlatformStyleSheet. All we have to do now is confine our platform-specific styles to the iOS and Android keys for each declared style’s object and our custom create method will take care of the filtering process.

import PlatformStyleSheet from 'PlatformStyleSheet';
  
  
const styles = PlatformStyleSheet.create({
    container: {
        backgroundColor: '#ffffff',
        ios: {
            fontFamily: ‘Arial’,
        },
        android: {
            fontFamily: ‘Roboto’,
        },
    },
});

Inheritance

This certainly makes it easier to write platform-specific code, but imagine having to declare the font family or indeed any base styles for each platform everywhere we want to use a Text component in our application. For web developers who are used to the inheritance pattern of CSS, this kind of repetition just won’t do.

Fortunately, we can recreate a different sort of inheritance pattern for styles using JavaScript. For our projects, we create a common directory inside the main js directory. This folder houses commonly-reused components throughout the app. For example, we can declare common Text and Header components like so:

/*
 * @providesModule CommonText
 */
import React from 'react';
import ReactNative from 'react-native';
import PlatformStyleSheet from 'PlatformStyleSheet';


const styles = PlatformStyleSheet.create({
  font: {
    ios: {
      fontFamily: ‘Arial’,
    },
    android: {
      fontFamily: ‘Roboto’,
    },
  },
  h2: {
    fontWeight: '700',
    lineHeight: 26,
    fontSize: 20,
  },
});
 
 
export const Text = ({ style, ...props }) =>
  <ReactNative.Text style={[styles.font, style]} {...props} />;
 
 
export const H2 = ({ style, ...props }) =>
  <ReactNative.Text style={[styles.font, styles.h2, style]} {...props} />;

Here we are importing the base Text component from React Native and applying some base styles that will be inherited wherever this common Text component is imported elsewhere in the app. This also allows us to declare those heading tags that we all know and love from the web. Using the ProvidesModule again, we can simply import these common text components rather than the React Native defaults.

import { Text, H2 } from ‘CommonText’;

We can still apply custom styles to these components, but no longer do we have to declare font family over and over. This pattern is also extendable to other commonly-used components. Anywhere you find yourself redeclaring base styles in multiple locations is a good place to pause and refactor a base component for reuse throughout the app. This makes code maintenance much easier since any changes to base styles can now be done through a single component, rather than throughout the codebase. For example, that Touchable Opacity button that keeps being rewritten over and over might better serve as a Button component imported from the common directory.

Conclusion

Ultimately, the design patterns we propose here are what we have found to work for us to achieve scalable and maintainable styles in React Native. The fun part of development is finding solutions tailored to the needs of each project.

Quickstart-Guide-to-Kotlin-Multiplatform

A Quick Start Guide to Kotlin Multiplatform

Kotlin Multiplatform, though still experimental, is a great up-and-coming solution...

Read the article