Developing React component library in 2021
Tags: react, designsystem, a11y, cssinjs
Published: 2020-09-12 15:24:44

In the previous article we've made our decisions on the tools we going to use for our new React component library. Let's recap:

- we use [stitches](https://stitches.dev/) as our CSS-in-JS solution
- [dokz](https://dokz.site/docs) will power our documentation website
- [`react-aria`](https://react-spectrum.adobe.com/react-aria/index.html) helps with accessibility
- and [Vercel](https://vercel.com/) will host all that

Assuming we already have a nice name and repository inside our organization, let's start from the documentation engine:

```bash
❯ yarn create dokz-app
yarn create v1.22.5
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Installed "create-dokz-app@0.0.6" with binaries:
      - create-dokz-app
✔ What is your project named? … awesome-ds
```

This will quickly create folder structure and add required dependencies to `package.json`. `dokz` is built on top of `Next.js`, hence folder structure may seem familiar to you. If not, I encourage you to try [Next.js](https://nextjs.org/) on your own as it's so awesome that I don't want it to outshine this article's main purpose.

You can now install the dependencies and verify that it works:

```bash
yarn install
```
```bash
yarn dev
```

And we see it on `http://localhost:3000/`!

![Initial setup preview](https://dev-to-uploads.s3.amazonaws.com/i/k66miovmm15ot9ejpbny.png)

Awesome, let's move on. Now we need to add [stitches](https://stitches.dev/) and try it out. As I mentioned in the previous article, the setup is super simple. Stop the server first and execute:

```bash
yarn add @stitches/react
```

And we're good to go! Let's create next two files: `lib/stitches.config.js` and `lib/Box.js` so that our fodler sturcutre is similar to:

```
awesome-ds
└───.next  
│
└───lib
│   │   Box.js
│   │   stitches.config.js
│   
└───node_modules
│   
└───pages
|   ...
```

Here I'm pushing two conventions: keep sources of the component library in `lib` folder (as it's shorter than `components`) and use `PascalCase` for React component names, as it's mentally easier to see that `Box.js` is a React component while `box.js` would be some utils file, not related to React.

`stitches.config.js` is where we create a `styled` function:

```js
import { createStyled } from '@stitches/react';

export const { styled, css } = createStyled({});
```

Here, we export `styled` and `css` returned by `createStyled`. This might seem inconvenient, as wouldn't it be easier to use:

```js
import { styled } from '@stitches/react'
```

But my take on this is that it's not only allows to have multiple instances of `styled` for whatever reason, but it also provides a nice way for TypeScript autocomplete. We will cover it later.

`createStyled` takes a configuration object we should use to make our design system to be a consistent system. If you're familiar with [styled-system](https://styled-system.com/theme-specification/), then this library has it out of the box. It allows us to define reusable variables that will be replaced at runtime with defined values, common colors, consistent spacing, font sizes, etc. So, we start from `tokens`:

```js
export const { styled, css } = createStyled({
  tokens: {
    colors: {
      $text: '#1A202C',
      $textLight: '#718096',
      $background: '#FFF',
      $accent: '#ED8936',
    },
    space: {
      $0: '0px',
      $1: '4x',
      $2: '8px',
      $3: '12px',
      $4: '16px',
      // etc
    },
    fontSizes: {
      $xs: '12px',
      $sm: '14px',
      $base: '16px',
      $lg: '18px',
      $xl: '20px',
    },
  }
});
```

Here I used `space` scaling and `colors` from [Tailwind CSS](https://tailwindcss.com/) as I really love it. And now let's see how it works. Going to `Box.js` we create a simple component:

```js
import { styled } from './stitches.config'

export const Box = styled('div', {
  // here we could define our custom styling if we needed to
})
```

Oh wow, I'm really impressed. That's all we need to create the main building block of a component library! This simple component now accepts `css` property to be styled using our created config. Let's see how it works. Go to `pages/index.mdx` and let's leave only `Playground` for us to write some code:

```mdx
---
name: Getting started
---

import { Playground } from 'dokz';
import { Box } from '../lib/Box';

## Box

<Playground>
  <Box
    css={{
      backgroundColor: '$text',
    }}
  >
    <Box as="p" css={{ color: '$accent' }}>
      Holy Moley!
    </Box>
  </Box>
</Playground>
```

Saving, starting the docs server with `yarn dev` and see:

![Styled Box component preview](https://dev-to-uploads.s3.amazonaws.com/i/ir565gaj45y6p5g2k1sq.png)

That was easy. And you may ask "but how is it different from using `style` prop on just `div`? Well, as you can see, we use our defined tokens for colors and can expand it to font styles, spacing, border radiuses, z-indicies – everything that forms a design system!

We also know that we will use some properties very often, let's make them easier to use via `stitches` [utils](https://stitches.dev/docs/utils):

```jsx
export const { styled, css } = createStyled({
  tokens: {
   // our defined tokens
  },
  utils: {
    p: () => value => ({
      paddingTop: value,
      paddingBottom: value,
      paddingLeft: value,
      paddingRight: value,
    }),
    px: () => value => ({
      paddingLeft: value,
      paddingRight: value,
    }),
    py: () => value => ({
      paddingTop: value,
      paddingBottom: value,
    }),
    
    bc: () => value => ({
        backgroundColor: value
    })
  }
});
```

Now we can use it on our docs page to see how it works:

```mdx
<Playground>
  <Box
    css={{
      bc: '$text', // instead of long backgroundColor
    }}
  >
    <Box as="p" css={{ color: '$accent', px: '$4' }}>
      Holy Moley!
    </Box>
  </Box>
</Playground>
```

And that's it. We can use consistent spacing and colors in any of our components! `utils` is very powerful, use it for the properties you want to short-hand. For example, `font` can produce `font-size`, `letter-spacing`, `line-height` and `font-family` to fit for every need. Or maybe you want to do `yAlign: '$center'` to center your content vertically.

## Note on usage

As you saw in our example, we inline objects passed as `css` prop. Opposite to many other CSS-in-JS solutions, this won't be extracted during build time. This means that `css` objects will be recreated on every component render. This won't be a problem for small applications, but I suggest to build useful habits even starting small, especially when it's that simple:

```jsx
const styles = {
  wrapper: {
    bc: '$text',
  },
  text: { color: '$accent', px: '$4' },
};
const ConsumerComponent = () => (
  <Box css={styles.wrapper}>
    <Box as="p" css={styles.text}>
      Holy Moley!
    </Box>
  </Box>
);
```

Another apprach would be to use `styled` function to compose class names. This will create a new component that will manage the class names passed as a `className` prop to the styled component itself:

```jsx
const Wrapper = styled(Box, {
  bc: '$text',
})

const Text = styled(Box, {
  color: '$accent',
  px: '$4'
})

const ConsumerComponent = () => (
  <Wrapper>
    <Text as="p">
      Holy Moley!
    </Text>
  </Wrapper>
);
```

**Alright, this looks good to me, now we move can move on to the next component: Button. We are going to cover accessibility, TypeScript and see how we can bundle our components library**