Build your own styled-components
Tags: react, javascript, showdev
Published: 2021-08-04 14:10:54

styled-components is a CSS-in-JS library that uses the tagged template syntax in JavaScript and allows you to write actual CSS inside your React components as opposed to the object syntax. If you haven't used styled-components before, the below example from the official documentation should give you a brief idea about what styled-component is:
```javascript
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;
```

## Why is styled-components important

styled-components does a lot more than just enabling you to write CSS inside your React components. Here are some other advantages:

 1. **Automatic Vendor Prefixing**: Since some CSS features need to be prefixed for different vendors like `-moz` or `-webkit`, styled components handles this for you automatically, so you write styles without having to worry about browser compatibility
 2. **Duplicate class names**: In large projects, you may run into clashing class names. styled components prevents this by assigning random hashes as part of your class names. So your class names remain readable yet random and prevent clashes at the same time
 3. **Simplified dynamic styling**: The styled-components syntax makes it easier to apply dynamic styles without having to change the class name of components using JavaScript.

There's a lot more that I skipped for brevity's sake. Read more [here.](https://styled-components.com/docs/basics#motivation)

## Haven't used styled-components before?
styled components also allow you to pass the same props as you would pass to normal HTML tags.
```jsx
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

render(
  <Wrapper>
    <Title>
      Hello World!
    </Title>
  </Wrapper>
);
```
The above code renders the following

![screenshot of above code](https://raw.githubusercontent.com/burhanuday/images/master/2021/07/Screenshot%20from%202021-08-01%2010-49-15.png)
## The tagged template syntax
Tagged templates may look weird at first but it is valid JavaScript syntax. I won't go into a lot of details here but you can read more about it at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates)

## Setting up the development environment

>I am going to be using CodeSandbox as my development environment. If you want to publish your library to npm, I'll suggest using method 2 to generate a React library

### Method 1
Using codesandbox.io React template

### Method 2
Using [create-react-library](https://github.com/transitive-bullshit/create-react-library) to scaffold a React component library. 

First, create a new JavaScript project with
```shell
npx create-react-library stylish
```
then CD into the folder
```shell
cd stylish
```
To start the dev servers, open two terminals and use the following commands:

**Terminal 1**
```shell
npm start
```
**Terminal 2**
```shell
cd example
npm start
```
The first terminal compiles your JavaScript component. The second terminal starts a Create React App project dev server, which allows you to use the component in a project and makes it easy to visualize your changes.

## Let's work on the library
We will develop the project in steps

### Installing necessary libraries
1. **stylis** — stylis is a lightweight CSS preprocessor that handles cross browser compatibility for our library
2. **nanoid** — nanoid is a unique string generator that we will use for randomizing class names and preventing clashes.

Run the following command to install these two libraries only if you have setup your project with Method 2. In CodeSandbox, you can add these libraries from the left sidebar.
```shell
npm i stylis nanoid
``` 

### Basic structure
Let's create a function that returns a React component and export it as the default export from the file
```jsx
const stylish = (Tag) => (styles) => {
  const NewComponent = ({ children, ...props }) => {
    return (
      <Tag>
        {children}
      </Tag>
    );
  };
  return NewComponent;
};

export default stylish;
```
If you now consumed stylish, you would see that this renders an `h1` tag in your DOM. It does not match the exact styled components syntax, but we will fix that later. Also, the styles don't work yet since we aren't using the prop 
```jsx
import stylish from "./stylish";

const H1 = stylish("h1")`
  color: red;
`;

export default function App() {
  return (
    <div>
      <H1>Hello CodeSandbox</H1>
    </div>
  );
}
```
### Styling the component
Right now, we aren't using the styles passed down at all. But before we use them, these styles need to be preprocessed using stylis. To do that,
```javascript
import { compile, serialize, stringify } from "stylis";

const preprocessStyles = (styles) => serialize(compile(styles), stringify);
```
This does two things, it first adds vendor prefixes to your CSS code and minifies it so that it takes less memory

Now we want to generate a unique class name for our component and then inject it into the browser stylesheet. To generate a unique component, we will use nanoid.

```javascript
import { customAlphabet } from "nanoid";

const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const nanoid = customAlphabet(alphabet, 10);

const generateUniqueClassname = (styles) => {
  const uniqueId = nanoid(10);
  return uniqueId;
};
```
After we have generated a unique class name, we want to inject the styles in the browser. To do that:
```javascript
const injectCSS = (className, styles) => {
  const styleSheet = document.styleSheets[0]; // get the browser's stylesheet
  styleSheet.insertRule(`.${className} {${styles}}`);
};

```
Now that we have all the utility required for styling our components, it is time for us to use them
We first check if any styles are passed, if no styles have been passed, we return without doing any of the above steps
```javascript
const stylish = (Tag) => (styles) => {
  const NewComponent = ({ children, ...props }) => {
    if (!styles[0]) {
      return <Tag className={props.className || ""}>{children}</Tag>;
    }
    // ...
```
Otherwise
```javascript
const preprocessedStyles = preprocessStyles(styles[0]);
const className = generateUniqueClassname(preprocessedStyles);
injectCSS(className, preprocessedStyles);

return (
  <Tag className={className} {...props}>
    {children}
  </Tag>
);
```
Your component should now look like this
```javascript
import { compile, serialize, stringify } from "stylis";
import { customAlphabet } from "nanoid";

const alphabet =
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const nanoid = customAlphabet(alphabet, 10);

const preprocessStyles = (styles) => serialize(compile(styles), stringify);

const generateUniqueClassname = () => {
  const uniqueId = nanoid(10);
  return uniqueId;
};

const injectCSS = (className, styles) => {
  const styleSheet = document.styleSheets[0]; // get the browser's stylesheet
  styleSheet.insertRule(`.${className} {${styles}}`);
};

const stylish = (Tag) => (styles) => {
  const NewComponent = ({ children, ...props }) => {
    if (!styles[0]) {
      return <Tag>{children}</Tag>;
    }

    const preprocessedStyles = preprocessStyles(styles[0]);
    const className = generateUniqueClassname(preprocessedStyles);
    injectCSS(className, preprocessedStyles);

    return (
      <Tag className={className} {...props}>
        {children}
      </Tag>
    );
  };
  return NewComponent;
};

export default stylish;
```
You should see that it now works as expected, and it correctly renders the HTML
```jsx
import stylish from "./stylish";

const H1 = stylish("h1")`
  color: red;
`;

export default function App() {
  return (
    <div>
      <H1>Hello CodeSandbox</H1>
    </div>
  );
}
```
![screenshot of rendered html](https://raw.githubusercontent.com/burhanuday/images/master/2021/07/Screenshot%202021-08-02%20at%208.11.52%20PM.png)

## Exporting all HTML tags as components
The API does not yet match the styled-components API exactly. To use the same syntax, we must export all components as a function.

The styled-components DOM elements list is pretty handy for this — [domElements.ts](https://github.com/styled-components/styled-components/blob/main/packages/styled-components/src/utils/domElements.ts)

You can copy the array and put it in its own file in your codebase. Then export a stylish function for each of the DOM nodes, like this:

```javascript
domElements.forEach((domElement) => {
  stylish[domElement] = stylish(domElement);
});
```

The API should now be the same as the styled-components API and should work exactly the same way

```jsx
const H1 = stylish.h1`
  color: red;
`;
```

## End of Part 1
This is the end of Part 1 of this multipart series. Here's the tentative list of contents of the next articles in the series:

- Part 2 — Working with component composition and making reusable styled components
- Part 3 — Optimizing and deduplicating styles
- Part 4 — Global styles and handling multiple themes
- Part 5 — Publishing your library to NPM

You can find the complete code for this part at [CodeSandbox — Part 1](https://codesandbox.io/s/stylish-nslns)

You can follow me on DEV to get updated when I post my next article