Organizing Styles in React Native

John Schoeman and Devin Jameson

In 2019, we wrote up a few practical guidelines for building resilient and ergonomic styles in React Native: Structure for Styling in React Native.

While everything in the original article still applies, it’s now 2021, and we’ve updated our approach in a few ways:

  • Use TypeScript
  • Use objects to organize modules from less to more specificity
  • When the order matters, use numbers
  • Prefer objective names over subjective names

This advice, along with that in the previous post, will enable you to style React Native apps more quickly and maintain better organization of your styles over time.

Use TypeScript

If you are unfamiliar with TypeScript, here is the short description from the official docs:

TypeScript is an open-source language which builds on JavaScript, one of the world’s most used tools, by adding static type definitions.

Types provide a way to describe the shape of an object, providing better documentation, and allowing TypeScript to validate that your code is working correctly.

In particular, using TypeScript solves several time-consuming issues when it comes to applying styles to React Native components:

Type annotations help us avoid mistakes

How many times have you had the experience of applying styles to your screen, waiting for the simulator to boot up or refresh, and seeing that you have a typo in your color name? From our experience, these kinds of errors happen all the time. Even if they only take a minute or two to fix each time, that can add up throughout a project.

Type annotations are a way for our computer to tell us if we have a typo before building our ReactNative project, saving us time and making us more productive.

an image of a typo in a code editor with a TypeScript compiler warning indicating that there is a typo.

TypeScript allows us to use code completion

No longer do we need to remember what styles are available or open the styles folder to check. We can rely on our editor to know what is available and use autocomplete to input them.

an image of a an style being written using autocomplete

TypeScript documents our styles

With TypeScript, we can express the intent of how the styles should be structured. So as the style system grows over time, we can be more confident that they will grow in a controlled and deliberate way, making the tendency for style systems to become disorganized over time less likely.

an image of React Native style code that is documented using TypeScript

Use objects to organize modules from lesser to greater specificity

Grouping styles by specificity with objects gives you both flexibility and control.

// Avoid

export const xxSmall = 8
export const xSmall = 10
export const small = 12
export const medium = 14
// Prefer

export const layout = {   
  x10: 10,
  x20: 18,
  x30: 26,
}

This allows us to both stay general when we want to and have a finer level of control when we are polishing a design. Which leads to several benefits:

Easier to tweak designs

When building components we often want to try out several adjacent styles. This is faster if we only need to change one character rather than a whole word.

// Avoid
const styles = StyleSheet.create({
  container: {
    margin: Sizing.xxSmall
    padding: Sizing.medium
  }
})
// Prefer

const styles = StyleSheet.create({
  container: {
    margin: Sizing.layout.x10
    padding: Sizing.layout.x30
  }
})

Easier to provide context

When grouping with objects, we have a way to provide context without needing to adjust variable name, which often leads to long, often awkward names like avatarXxxSmall and xxLargeIcon.

// Avoid

export const avatarXxxSmall = 10
export const xxLargeIcon = 36
export const small = 12
export const medium = 14
// Prefer

export const layout = {
  x10: 10,
  x20: 18,
}

export const avatar = {
  x10: 10,
  x30: 64
}

export const icons = {
  x10: 10
  x40: 36
}

When order matters, use numbers for naming

Naming ordered keys with numbers enables us to add or remove keys without breaking the naming scheme.

// Avoid

const gray = {
  light: "#dddddd",
  lighter: "#eeeeee",
  // What's between lighter and lightest? Lighterer?
  lightest: "#ffffff",
}
// Prefer

const gray = {
  shade100: "#ffffff",
  // shade150: "#efefef",
  shade200: "#eeeeee",
  shade300: "#dddddd",
}

Prefer objective names over subjective names

// Avoid

const sizing = {
  small: 10,
  medium: 20,
  large: 30,
}
// Prefer

const sizing = {
  x10: 10,
  x20: 20,
  x30: 30,
}

As programmers, we tend to enjoy semantic questions, like “Is a hot dog a sandwich?” In fact, we can spend entire lunches debating such concerns. While this is a fun leisure activity, it’s helpful to avoid these kinds of questions when naming our styles.

Using objective language actually saves a lot of time debating a word’s true meaning by altogether avoiding the conversation.

For example, consider the difference between using relative numbers and subjective names to describe size. How large is large exactly? Is it half the screen, or is it a tenth? Depending on the context, both are correct. When we use x10, x20, x30, etc., we leave the subjective and ambiguous baggage of language at the door and clearly express that we want the bigger size than x30 and less than x50.

Conclusion

Styling is important. We spend a lot of time doing it and doing it well is table stakes for making quality, maintainable software. Spending a little time up front being thoughtful about how we approach our style organization we can make our development practice more productive and more enjoyable.

You can see these patterns in practice here: react-native-typescript-styles