Introducing React Native Design System (RNDS) 🎉🎉

Stay in the loop!

Subscribe to my newsletter and stay up-to-date with my latest blog posts. Get an email every time I post something new.

Hey guys, I have been working on a library for a while now. It's a Design System around React Native and this blog answers some of the common questions that you might stumble while going through the repo. So let's start 😁.

What is RNDS 🧐?

RNDS in short for React Native Design System. It's a set of component library and design rules that lets you build React Native apps faster. It's documented, tested in production and works on Android, iOS and Web. Visit this link for documentation.

Why bother?

This title seems a little better than a generic title Motivation. So Why bother to create a library?
I was working on different projects and copy-pasting components I built over this past year. I thought (like most devs) that it will be a good idea to make a package out of it and import it in any project.
I did exactly that! I wanted something to prototype faster and it was a perfect solution.
At first, I decided to add a bunch of props which is common across all the components to have a low API surface area because seriously Who wants to remember a lot of props to use a component library?
I tried making it close to React Native itself so that I don't have to learn anything new. My goal was to create something that lets you get started in 10 minutes or less. (which is not that easy, I realized later 😅). I'm gonna go through my approach in the next section as Failures because that's where the magic happens.

Failures and Learnings

I will discuss my failures and what I learned while making this. Most of the things are pretty common if you're familiar with design systems but I had to learn the hard way. I was solving my own problems.

API

I wanted flexibility, freedom to create any component real quick so I added a bunch of props like size, color initially.
Size prop was supposed to take an integer value like size={17}. There was some calculation involved to create a component of an appropriate size but possibilities were endless. I can basically create a component from size say 10 to say 100. I just had to try a bunch of numbers and see what fits.
color was supposed to take a hex code of color and it will be the color of a component say Button. When you're working on a project, all you get is a color and it's easy to pass like that. So what failed?
If you're experienced dev, you might have already guessed what an idiot I am but hear me out. The problem was With too much freedom comes too much inconsistency.

I provided size and color to each usage of say Button in a project and it's difficult to maintain consistency when there are so many options available. size={16} and size={17} looks almost similar but are not. This was a big failure in terms of pixel-perfect design.
The second problem was passing the hex code to color prop. There aren't a lot of buttons when it comes to a real app. It means I was passing one single color to each and every button for no reason 😑.

The solution was using a tokenized system of configuration and restricting the possibilities to a bare minimum which covers most of the cases.

The key is to find the balance between Freedom and Consistency.

Customization and Consistency

So as we talked above, the solution for inconsistency was defining all the component-specific configurations in a single file. It solved one more problem than it was supposed. Now, with everything in one place, it was much easier to customize components. Suppose, I got 7 sizes of buttons as follows:

theme = {
  buttonSize: {
    'xxsmall': 8,
    'xsmall': 10,
    'small': 12,
    'medium': 14,
    'large': 16,
    'xlarge': 18,
    'xxlarge': 20,
  },
  ...
}

It means I can simply do this to assign a different size according to my requirements:

theme.buttonSize.medium = 15;

This also helps in making things consistent. There can be 7 different sizes of components and I feel that's enough. Any more than that will just introduce ambiguity. You can always modify things (or add more) in the theme file. This also works well for colors. Suppose, we have initially defined colors in theme file is as follows:

theme = {
  brandColor: {
    'primary': '#1e88e5',
    'secondary': '#f9a825',
    'tertiary': '#e53935',
    'background': '#f8f8f8',
    ...
  },
}

You can easily change primary (or any) brand color as:

theme.brandColor.primary = '#aeea00';

Sweet! Let's move on.

Design Language

Everything is interlinked with one another. Solving the first and second problems solved a third problem which I had no idea.
Communication was a problem with the earlier approach of size and color. You need to tell the exact (number of) sizes and (string) Hex code if you want someone to create a button for you. Token-based theme and restricting sizes eliminated this issue as well.
You just have to say that "Hey, can you create a medium size button with a primary color?" That's it. It favors better communication and avoids confusion.

Design Rules

Prototyping an app is much more than just dropping components on a screen. Space matters a lot more than actual components when you're working on the frontend. Managing space on each component was my first approach but it was difficult.
I added a few separate components which take care of space only. These are Layout components like Stack, Inline, Box and Card. Their sole purpose is to provide spacing to their children. You can go to the playground of Stack in the documentation for a live demo.
All the other components like Button, Badge, etc have 0 margins.

This separates the concerns in two:

  1. A normal component should worry about only one thing: rendering a component of appropriate size.
  2. A layout component should worry only about providing space to the component.

There are a bunch of other rules that will be discussed later in these articles (Also available on docs).

Layout components are inspired/stolen from Braid Design System 🙈

Documentation

What good is a library if you don't know how to use it. A design system at its minimum is the sum of Design Library + Design Rules + Documentation and it is supposed to boost your productivity. Which is certainly not possible if you waste your time figuring out how to use a component.

After creating the whole library, I instantly googled "How to document your library like a pro" and decided to use React DocGen. I wrote half the documentation but it wasn't working out well so I decided to go with Docz. It's really good and I re-wrote the whole documentation for Docz but...

Then I went to a Design System Meetup organized by JSLovers and someone suggested to use StoryBook instead. It was perfect and exactly what I needed. It's an interactive playground not only helps you explore the components but also enables to create them in isolation a lot faster. With StoryBook, you don't even have to have to read the whole documentation. Just playing with the component in the playground is enough.

So, for the third, I decided to rewrite the whole documentation with StoryBook in MDX and that's the final version that you will see at rnds.netlify.com.

There were other complications/hurdles on using MDX with React Native but I won't go in that much detail.

After working so hard, all I wanted was it to be perfect and look at what I created at first 😂 👇

first logo

First thought, definitely not good 😣. So, after much thinking I created these, thanks to my photoshop skills:

second logo

Looks good but I can do better. So this is the final version after giving a hell lot of thoughts 👇.

final logo

This looks pretty satisfying and states my thoughts on why I created this too.

So these were a few of my failures while working on this. I learned a lot of things from webpack configs to compound components and a lot that I can't cover in this article. I know there are other great libraries available like NativeBase and React Native Element but I was just trying to solve my own problems and this is the result of going through a lot of iteration.

Sanket Sahu and Nader Debit have done a great job on the libraries mentioned above. The name React Native Design System was suggested by Nader himself and I'm thankful to him for helping me in building this.

Design Rules

This section is the core of this library and I have spent most of my time iterating this to make it better. It's not much but it's honest work.
It is based on the four key aspects that I considered while creating this.

Feel free to send a PR for corrections, these are personal opinion and I'm here to learn. 😅

1. Low surface area

Most of the props are common across all components or they are the same as their parent component imported from React Native. This makes sure that you do not have to learn a whole set of extra props or just use what you already know.
For example:

  • If it's a Button, it receives all the props of Touchable component of React Native.
  • If it's a Modal, it receives all the props of Modal.
  • Each component receives a size prop whose value can be one of these: xxsmall, xsmall, small, medium, large, xlarge & xxlarge. You just have to pass size prop with one of these value and you're done.
  • Another example could be color prop. You can pass one of the colors defined in brandColor of the theme to any component.
  • If it's a layout based component like Box, Stack, Inline or our best friend Card then it receives a space prop. This also takes one of the values between xxsmall and xxlarge (with the addition of space="none") and provide appropriate spacing between components.
  • Each component receives a style and a textStyle prop (if there is a text involved). This is for some rare cases when you have to override the default styling. It's preferable to tweak the theme instead to maintain consistency and avoid adding that style again and again.

These props are common to all the components. Other component-specific props are pretty straight forward too.

2. Speed

For most of the cases, default styles like size={medium} or space={medium} will be enough. In other cases, it's just two to three props max to achieve any desired result. This makes it faster to prototype. The layout components make it easier to achieve the desired screen layout with space props. See the playground in Stack documentation.

One of the key aspects of pixel-perfect design is the spacing between components. This design system proposes two things:

  1. Every UI component has a margin of 0.
  2. The spacing of any component will be determined by its parent Layout component.

The separation of concerns makes the job easier. Layout component should take care of space only and UI component should worry about UI only i.e., color or size.

3. Consistency

The correct balance of freedom and consistency is hard to achieve.

  1. For freedom, you get straight forward props like color and size.
  2. For consistency, you need to define these configurations inside theme file i.e Single source of truth.

It helps in maintaining uniformity across all the usage. It also allows you to have several choices and use them as needed. Check out the theme section of this guide for the default configuration.

4. Connection

Big problems are always more manageable when broken into smaller pieces. The design language of the system is broken down into color, typography, size, and space. These API is followed by each component.

Color

There are basically two types of colors:

  • brandColor: "primary", "secondary", "tertiary", "background", "disabled", "semitransparent".
  • textColor: "default", "heading", "grey", "subtle", "disabled", and "white"

Typography

To keep things simple and consistent. There are two choices for fonts to use:

  • heading
  • text

Size

  • The available sizes you can use is "xxsmall", "xsmall", "small", "medium", "large", "xlarge" and "xxlarge". The default is "medium" and it will be applied if are not passing anything.
  • Font size is also similar and it ranges from "xxsmall" to "xxlarge".

Space

Space also shares similar API as size but with one addition. You can also pass "none" with the range of "xxsmall" to "xxlarge". Space is a dedicated prop for layout components like Stack, Inline, Box and Card.

You can contribute to the theme. Please share what are the common color tokens you use, what are the common font sizes that you use in any app, etc. Let's make the theme config more generic together.

Getting Started

Install

Simply go to the command line and run this command.

yarn add react-native-design-system

You can use yarn or npm as per your choice.

npm install react-native-design-system

This library needs react-native-vector-icons so go on and install that too to get all the cool icons. Check out Install guide.

Usage

Step 1. Import ThemeProvider and theme then wrap your root component.

This step is important. We are passing theme as context value that each component will access.

//your root component
import { ThemeProvider, theme } from 'react-native-design-system';

function App(){
  return (
    <ThemeProvider value={theme}>
      <Root />
    </ThemeProvider>
  );
}

Step 2. Use component.

//inside any file
import { Button } from 'react-native-design-system';

function HomeScreen(){
  return (
    <Button>
      Press Me
    </Button>
  );
}

Visit this for more info.

Reference:

Thanks to each person who helped me feedbacks. Other than that, these were talks and article which helped:

If this feels interesting, please drop a star on the repo. Click on this link.
Contributions are most welcome, you can reach here to read contribution guidelines.

Comments (4)

Madhankumar's photo

Thanks Mohd Shad Mirza for making a design system in React Native and appreciate your efforts.

Coming from the web background, I am just curious about how do you scale the font-size, spacing, height and width of an element. I hope even in the mobile also we have to support different media devices like landscape and portrait.

In the web, I used to set my base space-unit as 1em and then will calculate the remaining space units. Besides this will change the base unit for different devices. By this way I achieved the scaling.

Show +1 replies
MARK's photo

Mohd Shad Mirza

Did you think about setting font-size: 6.25%; As a base case? This makes each 1rem = 1 pixel, is scalable natively I believe as well. You could then just have each of your size definitions as rem instead of pixels and they will scale with a11y settings etc. Good luck!

Mohd Shad Mirza's photo

Full Stack Developer at Hashnode

MARK Thank you for the suggestion Mark. I'm gonna try it out.