Writing composable CSS components for React
Tags: css, react
Published: 2023-08-10 15:32:10

**Written by [Abhinav Anshul](https://blog.logrocket.com/author/abhinavanshul/)✏️**

CSS Components is a tiny (2.3Kb) modern utility for composing CSS classes to React components. You can think of it as a wrapper with a best-in-class developer experience (DX). 

The release of React 18 has made server components more mainstream and the default way to create React apps. However, DX is a common pain point while using CSS-in-JS in modern React web apps. 

CSS Components aims to address this issue by modeling its APIs closely after popular [CSS-in-JS libraries such as styled-components](https://blog.logrocket.com/using-styled-components-react/) and Stitches. If you have already used these in your project, learning CSS Components will be smooth sailing. 

In this article, you will learn how to create your own composable components using CSS Components, convert them into Hooks, and use them across your React project. We’ll also compare CSS Components to similar solutions available. 

Jump ahead:

*   [Getting started with CSS Components](#getting-started-css-components)
*   [Adding CSS Components to your React project](#adding-css-components-react-project)
*   [Building a reusable `Button` utility](#building-reusable-button-utility)
*   [Extracting repetitive properties to optimize your styles](#extracting-repetitive-properties-optimize-styles)
*   [Using a CSS component as a custom React Hook](#using-css-component-custom-react-hook)
*   [Composing components with CSS Components](#composing-components-css-components)
*   [Comparing CSS Components with other popular libraries](#comparing-css-components-other-popular-libraries)

You can [explore the final project on GitHub](https://github.com/abhinav-anshul/logrocket-css-components) or simply follow along as we build it in this tutorial.

## Getting started with CSS Components <a name="getting-started-css-components">

CSS Components works on all modern browsers up to at least the last two versions. While it’s limited to React only, it strives to be really good at composing styles in your React application. 

Although you can begin creating apps with CSS Components, it is not yet considered production-ready. The core team is actively working on a stable release. 

Otherwise, using CSS Components is quite straightforward. You define a configuration using its `styled` API — [similar to Stitches](https://blog.logrocket.com/stitches-server-rendered-css-in-js/) — and then begin exporting your variants:

```javascript
import { styled } from "@phntms/css-components";
const Button = styled("button", {
  css: "rootStyles",
  variants : {
    // add your variants here
});
```

You can now export this `Button` component and use it anywhere inside your project by passing the various props that you defined in your configuration under `variants` in the code above.

## Adding CSS Components to your React project <a name="adding-css-components-react-project">

CSS Components was built with [modern server-side rendering](https://blog.logrocket.com/improve-app-performance-react-server-side-rendering/) in mind. It supports mixing and matching with any component library of your choice, or even CSS libraries such as CSS modules, SASS, and others. 

While working with a component library, you can turn the library’s pre-defined components into Hooks and mix them with the CSS Components `styled` API to add your own set of custom CSS styles. This can help make your code more readable and easier to manage. 

To add CSS Components to your React project, first, initialize a simple React App using a framework of your choice. This post uses [Vite for its simplicity](https://blog.logrocket.com/vite-3-vs-create-react-app-comparison-migration-guide/) and to focus more on CSS Components rather than the boilerplate itself. 

Create a new Vite project by running the following command:

```bash
npm create vite@latest
```

Select React as your choice of framework when prompted by the CLI. 

In the latest Vite update, you might get another CLI prompt asking for a language and compiler. You can choose either `javascript` or `javascript + swc`. 

This will generate a new React project for you. Now is the perfect time to install CSS Components as a dependency:

```bash
npm install @phntms/css-components
```

This command will add `css-components` to your project. Now, similar to Stitches, you can utilize the `styled` API to create utility classes that you can utilize throughout the project.

## Building a reusable `Button` utility <a name="building-reusable-button-utility">

This post demonstrates how you can create reusable `Button` components and render them according to the variant props provided. 

To create a `Button` component, you must first create a folder named `Button` under the `src` directory. Inside this folder, create two files — one from which the `Button` will be exported and another to add additional styling using CSS modules. The folder structure should look like this:

```bash
> src
  > Button
    - Button.jsx
    - button.module.css
```

If you’re using a global CSS file like `index.css` or `style.css`, to keep things simple, you can convert that file to a CSS module as well and rename it `style.module.css`. 

Now let’s move on to the actual implementation. Open your `Button.jsx` file, import `styled` from CSS Components, and initialize it. 

To build `Button` components as a utility, you can use the `variants` API exposed via `styled`. This might seem familiar if you’ve used Stitches before because CSS Components uses a similar API signature:

```javascript
import { styled } from "@phntms/css-components"
  const Button = styled("button", { 
        variants: {
          primary: {},
          secondary: {}
          destructive: {}
          ghost: {}
          outline: {}
      }
  }
```

The `Button` component has 5 variants — `primary`, `secondary`, `destructive`, `ghost`, and `outline` — following common design system naming conventions. 

Now for the actual usage, you might want to simply pass a variant prop like this:

```javascript
<Button primary>Secondary</Button>
//Or
<Button primary={true}>Secondary</Button>
```

To do this, you can define styles for each of the variants in your `button.module.css` file. Let’s do so using the `primary` variant:

```css
.primary {
  background-color: white;
  color: black;
  padding: 0.5rem 1rem 0.5rem 1rem;
  border-radius: 0.2rem;
  cursor: pointer;
  border: none;
  color: white;
}
.primary:hover {
  opacity: 0.95;
}
```

Now that we’ve created a button for the `primary` variant prop, we can invoke this variant whenever we pass `true` to the `primary` prop, like so:

```javascript
import { styled } from "@phntms/css-components"
import buttonStyles from "./button.module.css"
  const Button = styled("button", { 
        variants: {
          primary: {
            true: buttonStyles.primary,
          },
          secondary: {}
          destructive: {}
          ghost: {}
          outline: {}
      }
  }
```

Let’s continue adding CSS styles for the remaining variants in the `button.module.css` stylesheet, as well as adding references to those styles back in the `Button` variants API:

```javascript
import { styled } from "@phntms/css-components"
import buttonStyles from "./button.module.css"
  const Button = styled("button", { 
        variants: {
          primary: {
         true: buttonStyles.primary,
          },
         secondary: {
         true: buttonStyles.secondary,
          }
          destructive: {
           true: buttonStyles.destructive,
          }
          ghost: {
         true: buttonStyles.ghost,
          }
          outline: {
         true: buttonStyles.outline
         }
      }
  }
```

## Extracting repetitive properties to optimize your styles <a name="extracting-repetitive-properties-optimize-styles">

As you continue to add styles to the `button.module.css` stylesheet, you might see some repetitive properties among all the variants. To solve this, extract all the repetitive properties and create a new utility class called `.btn` like so:

```css
.btn {
  padding: 0.5rem 1rem 0.5rem 1rem;
  border-radius: 0.2rem;
  cursor: pointer;
  border: none;
  color: white;
}
/* primary variant */
.primary {
  background-color: white;
  color: black;
}
.primary:hover {
  opacity: 0.95;
}
/* secondary variant */
.secondary {
  background-color: #0f172a;
}
.secondary:hover {
  opacity: 0.95;
}
```

By extracting out the repetitive properties, you can now add `.btn` as the common style utility in the `styled` API under the `css` key like so:

```javascript
>const Button = styled("button", {
    css: buttonStyles.btn,
    variants: {
      primary: {
        true: buttonStyles.primary,
      },
     ...rest of the variants
    },
  })
```

This `css` key also accepts an array of styles, so you can import styles from root CSS files and add them here in the array. One possible use case for this might be adding a font family and other CSS resets in the root file and including them in the array like so:

```javascript
import { styled } from "@phntms/css-components"
import rootStyles from "../style.module.css"
import buttonStyles from "./button.module.css"
const Button = styled("button", {
    css: [rootStyles.root, buttonStyles.btn],
    variants: {
      // ...rest of the variants
    },
  })
```

## Using a CSS component as a custom React Hook <a name="using-css-component-custom-react-hook">

To make your components easier to use with improved DX and modularity, you can turn this component into a [custom React Hook](https://blog.logrocket.com/create-your-own-custom-react-hooks/). Simply wrap the entire Hook into a function called `useButton` and return the `Button` component. Then, export this Hook like so:

```javascript
import { styled } from "@phntms/css-components"
import rootStyles from "../style.module.css"
import buttonStyles from "./button.module.css"
function useButton() {
  const Button = styled("button", {
    css: [rootStyles.root, buttonStyles.btn],
    variants: {

    },
  })
  return Button
}
export { useButton }
```

You can then utilize this Hook like so:

```javascript
import { useButton } from "./Button/Button"
function App() {
  const Button = useButton()
  return (
    <>
      <main>
        <Button primary>Primary</Button>
      </main>
    </>
  )
}
export default App
```

Custom Hooks complement the composable behavior of CSS Components quite well. These custom Hooks follow the same rules as React Hooks and are defined only at the top level of the file. 

Converting CSS Components to Hooks would promote reusability across the application and improve codebase maintainability, especially while working on a large-scale application. 

By doing so, you have now achieved a fully functional and customizable `button` system design built on top of CSS Components API. You can also create other custom elements as well to implement similar concepts. Here’s how your `Button` component would look: ![Example Button Themes Built With Css Components Shown Vertically Stacked Against Black Background: White Primary Button, Dark Blue Secondary Button, Red Destructive Button, Black With White Outline Button, Black With No Outline Ghost Button](http://blog.logrocket.com/wp-content/uploads/2023/07/CSS-Components-themed-button-components.png)

## Composing components with CSS Components <a name="composing-components-css-components">

CSS Components allows you to compose two components via the `styled` API. This is useful when you already have a utility component defined and you want to reuse those styles. 

In the following code, the `SubscriptionButton` will inherit styles from the `BaseButton` component. If there are styling conflicts, the `SubscriptionButton` will safely override those properties:

```javascript
const BaseButton = styled("button", {
  css: buttonStyles.btn,
});
const SubscriptionButton = styled(BaseButton, {
  css: subscriptionStyles.btn,
});
```

This newly composed `SubscriptionButton` component can be turned into a Hook as well and can be used similarly to the `BaseButton`, inheriting its properties:

```javascript
<BaseButton primary>Base button</BaseButton>
<SubscriptionButton ghost>Base button</SubscriptionButton>
```

## Comparing CSS Components with other popular libraries <a name="comparing-css-components-other-popular-libraries">

The CSS Components team [conducted a benchmark comparison](https://www.css-components.net/docs/benchmarks) between this utility and other popular solutions out there: ![Benchmark Comparison Between Css Components And Stitches, Emotion, And Styled Components Showing Css Components Outperforming Other Libraries For Updating Style Or Css Prop](http://blog.logrocket.com/wp-content/uploads/2023/07/img1-Updating-style-css-prop-CSS-Components-outperforms-other-popular-libraries.png) As you can see, CSS Components outperformed Emotion, Stitches, and styled-components in terms of injecting initial styles as well as updating CSS prop. 

It also performs well when handling a larger number of DOM nodes in a tree, both in terms of deeply nested trees: ![Benchmark Comparison Between Css Components And Stitches, Emotion, And Styled Components Showing Css Components Outperforming Other Libraries For Handling Large Number Of Dom Nodes In A Deeply Nested Tree](http://blog.logrocket.com/wp-content/uploads/2023/07/img2-Mounting-tree-nested-nodes.png) As well as in terms of wide sibling nodes: ![Benchmark Comparison Between Css Components And Stitches, Emotion, And Styled Components Showing Css Components Outperforming Other Libraries For Handling Large Number Of Dom Nodes In A Widely Nested Tree With Many Sibling Nodes](http://blog.logrocket.com/wp-content/uploads/2023/07/img3-Mounting-wide-nested-tree-many-sibling-nodes.png) As you can see, CSS Components performs quite well when compared to popular CSS-in-JS libraries out there. This only proves its efficiency and shows how it’s a must-use utility in your React applications.

## Conclusion

CSS Components doesn't aim to replace popular component libraries out there. Instead, it’s perfect to use alongside them. 

You can use CSS Components to generate custom utility-based components in seconds. CSS Components also embraces a modular pattern of writing code that can prove to be quite maintainable and scalable. These features make this utility a must-use CSS-in-JS solution for React developers. 

You can find the source code for the examples above in this [GitHub repository](https://github.com/abhinav-anshul/logrocket-css-components).

---
## Get set up with LogRocket's modern React error tracking in minutes:

1.Visit https://logrocket.com/signup/ to get an app ID.
2.Install LogRocket via NPM or script tag. `LogRocket.init()` must be called client-side, not server-side.

NPM:
```bash
$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
```
Script Tag:
```javascript
Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
```

3.(Optional) Install plugins for deeper integrations with your stack:
*   Redux middleware
*   ngrx middleware
*   Vuex plugin

[Get started now](https://lp.logrocket.com/blg/signup)