4 Ways to Add Style to React Native App

I'm currently developing a mobile application called MournTogether, the purpose of which is to connect people who have lost a loved one.

Before developing the app I created wireframes with the style that I wanted and read React Native documentation. I used React Native because I have several years of experience with React and (wrongly) assumed that the two frameworks would be the same

To start building, I used official React Native documentation, which I assumed would outline any differences between React and React Native.

When it came to styling, the official documentation recommended inline styling or creating a style component on the same page which I will discuss below. These two approaches are limited so I had to do some digging to find approaches more well-suited for my mobile app.

Method 1: Inline styling

The tutorials in the official React Native documentation recommended using inline styling. This approach is not ideal for large projects like mine but it gets us familiar with the syntax we would use for styling in React Native.

See the example below. To keep it simple let's pretend we want to write text and make it purple within our React Native app.

Let's start with the text.

import React from 'react';
import {StyleSheet, Text, View} from 'react-native';

const Sample = () => {
  return (
     <View>
        <Text style={{color: 'purple'}}>>Make this text purple</Text>
     </View>
    )
}

It should look like this.

This is a fine approach for smaller projects but there are a few issues with this approach especially for much larger projects like mine but the following reasons.

Issues with inline styling in React Native

  1. It violates the golden rule of separation of concerns as the style and the content are not only in the same file but in the same lines of code

  2. It could be difficult to find, read, and add features to or edit in large code bases

  3. It has the potential to repeat itself causing the code to be WET (Write Every Time) or not DRY (Don't Repeat Yourself). Say I wanted my text to be purple and I wanted another line of text to also be purple. I would have to write it like this:

import React from 'react';
import {StyleSheet, Text, View} from 'react-native';

const Sample = () => {
  return (
     <View>
        <Text style={{color: 'purple'}}>>Make this text purple</Text>
        <Text style={{color: 'purple'}}>More purple text</Text>
     </View>
    )
}

Method 2: Use Style Components within the same file

Another possibility is using Style Components or style props and code them in the same file. It would look like this:

import React from 'react';
import {StyleSheet, Text, View} from 'react-native';

const Sample = () => {
  return (
     <View>
        <Text style={styles.text}>>Make this text purple</Text>
     </View>
    )
}

const styles = StyleSheet.create({
    text: {
        color: 'purple'
    }
})

Creating style components in the same file solves the WET or clean code issue but it doesn't solve the problem of separation of concerns. What happens when the code base gets bigger or you're working on a React Native app with several people? You'll have to scroll all the way down to the bottom of the file to get to your style components.

There are two other approaches that can allow us to write cleaner code and to adhere to separation of concerns.

Method 3: Put style component in a separate .js or .ts file

So here's where I discovered React and React Native are different. I thought I could just put my style in a separate .css file, create classes and give my components a className like I would in React. Nope! No such luck with React Native.

I tried this and my app was like a teenage boy at prom in 1998: it had no style at all.

It turns out React Native doesn't accept the className attribute in its components nor does it compile .css files (at not without setup which I'll discuss below).

The first workaround I found was creating a .js file for my style component.

That looks like having one .js file (or .ts file if you're using TypeScript) for the components as such:

//sample.js
import React from 'react';
import { Text, View } from 'react-native';
import styles from './styles'

const Sample = () => {
  return (
     <View>
        <Text style={styles.text}>>Make this text purple</Text>
     </View>
    )
}

And another .js or .ts file for the style component:

//styles.js

import { StyleSheet } from "react-native-web";

export default StyleSheet.create({
    text: {
        color: 'purple'
    }
})

In a way, this helps to keep the code more organized.

Whether or not this violates the separation of concerns is up for debate. The StyleSheet is in a different file so the code is organized and DRY. However, the style would still be in a .js or .ts file.

Method 4: Use CSS modules

According to LogRocket “CSS Modules offer a portable or more robust” way to style components than inline styling (method 1) or style props (method 2 and 3) and can make your code more readable and cleaner. It also does not violate separation of concerns, like method 3 might and methods 1 and 2 definitely are! And something else I didn’t think about: it reduces the risk of selector collision. (So it goes without saying, this is the method I decided to use!)

CSS Modules Set up

Here’s how you can set up CSS modules (assuming you’ve already set up your React Native app):

Step 1: Install dependencies

cd into your frontend file and in your BASH, if you are using yarn, run the following:

yarn add babel-plugin-react-native-classname-to-style babel-plugin-react-native-platform-specific-extensions react-native-css-transformer react-native-paper --dev

If you’re using npm, run the following two lines in your BASH:

npm install --save-dev babel-plugin-react-native-classname-to-style babel-plugin-react-native-platform-specific-extensions react-native-css-transformer
npm install react-native-paper

In your babel.config.js file, delete your code and replace it with the following:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      'react-native-classname-to-style',
      ['react-native-platform-specific-extensions', { extensions: ['css'] }],
    ],
  };
};

If you don’t have a metro.config.js file, create one. Once you have that added or if it’s already there, add the following:

const { getDefaultConfig } = require("metro-config");

module.exports = (async () => {
  const {
    resolver: { sourceExts }
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve("react-native-css-transformer")
    },
    resolver: {
      sourceExts: [...sourceExts, "css"]
    }
  };
})();

Finally add the following to your app.json file:

{
  "expo": {
    "packagerOpts": {
      "config": "metro.config.js",
      "sourceExts": ["js", "jsx", "css"]
    }
  }
}

Now you can yarn start or if you’re like me, npm start

Step 2: Set up your CSS module file

Before we were just changing the color of the text, so I’m going to stick with that example

Now our JS file will look just like it did in Method 3 when were were using Style components in a separate file:

//sample.js
import React from 'react';
import { Text, View } from 'react-native';
import styles from '.App.module.css'

const Sample = () => {
  return (
     <View>
        <Text style={styles.text}>>Make this text purple</Text>
     </View>
    )
}

But now instead of putting our files in a .js or .ts file, we’re going to import and use a CSS module that looks like this:

/* App.module.css*/

text: {
        color: 'purple'
    }

This makes me feel a lot more comfortable! We’re not violating separation of concerns at all. The code is much cleaner, and it’s kinda like how we would normally use CSS. The only downside, in my opinion is having to install dependencies.

If you notice in the .js file, we also imported App.modules.css instead of from ./styles.

For more information, check out the following resources:

https://blog.logrocket.com/css-modules-react-native/
https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/