Exploring Pigment CSS, a zero-runtime CSS-in-JS library by MUI
Tags: css, webdev
Published: 2024-07-03 19:04:15

**Written by [Ivy Walobwa](https://blog.logrocket.com/author/ivywalobwa/)✏️**

CSS-in-JS is a modern approach to styling web applications where CSS is written directly in JavaScript files. This allows CSS styles to be scoped into a component. Many developers, especially in the React community, have adopted the CSS-in-JS approach. 

The most popular CSS-in-JS libraries are [styled-components](https://styled-components.com/) and [Emotion](https://emotion.sh/). MUI, a React component library, recently released a promising, zero-runtime CSS-in-JS library called [Pigment CSS](https://github.com/mui/material-ui/tree/master/packages/pigment-css-react). This article will explore the features and benefits of Pigment CSS, offering a comparison to styled-components and Emotion based on performance, features, developer experience, and community support.

## Pigment CSS

![Pigment CSS](https://blog.logrocket.com/wp-content/uploads/2024/06/pigment-css.png) Pigment CSS is a zero-runtime CSS-in-JS library maintained by the Material UI team and built on top of [WyW-in-JS](https://wyw-in-js.dev/), or “Whatever-you-want-in-JS.” It extracts colocated styles to their own CSS files at build time. Pigment CSS currently supports Next.js and Vite. 

The concept of zero-runtime CSS-in-JS seeks to combine the benefits of CSS-in-JS with the performance benefits of traditional CSS. Here, all styles are compiled to static CSS files at build time, eliminating runtime overhead. This improves performance, especially on initial page loads.

## Key features of Pigment CSS

Pigment CSS offers the benefits of CSS-in-JS, such as locally scoped styles and themeability, while avoiding the runtime performance cost typically associated with CSS-in-JS libraries. Some of the key features include:

### Zero runtime

Styles are preprocessed during the build phase. No styles are injected and recalculated during runtime. This improves the performance of the application.

### Theming

Theming is an optional feature that lets you reuse the same style values across your application. The theme objects are only used at build time and are not included in the final JavaScript bundle.

### Integration with build tools

Pigment CSS seamlessly integrates with Next.js and Vite with support for more bundlers in the future. This makes it easy to add Pigment CSS to existing React applications without significant configuration. 

Pigment CSS allows you to define CSS in two ways: using object-style syntax, where styles are defined using JavaScript objects, or template-style syntax, where CSS styles are written using template literals.

## Comparing Pigment CSS, Emotion, and styled-components

Pigment CSS uses zero-runtime CSS-in-JS, while [styled-components](https://blog.logrocket.com/using-styled-components-react/) uses runtime CSS-in-JS, and [Emotion](https://blog.logrocket.com/emotion-in-react/) uses runtime CSS-in-JS with options for extracting static styles. 

The three libraries can be compared as follows:

*   **Performance**: Due to its zero-runtime approach, Pigment CSS has reduced runtime performance but increased build time
*   **Bundle size impact**: Pigment CSS styles compile to pure CSS files at build time, thus having minimal impact on the JavaScript bundle size. On the other hand, styled-components inject styles into the DOM at runtime using JavaScript. Emotion can operate in two modes — pure runtime or static style extraction
*   **Dynamic styling**: Pigment CSS requires you to declare all styles and account for all combinations of props. styled-components and Emotion excel in applications where styles need to react dynamically to component states or props
*   **Developer experience**: Pigment CSS offers a developer experience similar to styled-components and Emotion. All three enhance readability by keeping styles closely tied to components. However, a notable difference lies in how the styles are defined: in Pigment CSS, unlike passing props for conditional styling, all style variants must be pre-defined
*   **Tooling and ecosystem**: styled-components and Emotion have a slight edge in terms of integrations and community resources due to their popularity and longer history in the market

Check out our [article comparing styled-components and Emotion](https://blog.logrocket.com/styled-components-vs-emotion-for-handling-css/) for a deeper dive.

## Using Pigment CSS

To use Pigment CSS, you must first configure it in your Next.js or Vite application. In this tutorial, we’ll use a Next.js app. 

Pigment CSS simplifies the creation of reusable styles and components for your application by providing various APIs. You can use the `css` API to create reusable styles, the `styled` API to create a component by passing styles at the end, or the `keyframes` API to create reusable animation keyframes. A theme object can also be used to reuse the same styling values across your application.

### Setting up Pigment CSS

In your Next.js application, install Pigment CSS using the following command:

```yarn
npm install @pigment-css/react
npm install --save-dev @pigment-css/nextjs-plugin
```

This command installs the Pigment CSS library and the Next.js plugin. 

Next, in your `next.config.mjs` file, import the `withPigment` plugin, and wrap the Next.js config as shown below:

```javascript
import { withPigment } from '@pigment-css/nextjs-plugin';
const nextConfig = {};
export default withPigment(nextConfig);
```

In your `layout.tsx` file, import the Pigment CSS stylesheet as shown:

```javascript
import '@pigment-css/react/styles.css';
```

With that, you’re ready to make use of Pigment CSS styles.

## Basic usage of Pigment CSS

First, we’ll use the `css` API to create our styles. You can use the `template` or `object` syntaxes as shown below:

```javascript
import {css } from "@pigment-css/react";
// template syntax
const bodyBackground = css`
  background-color: #1D2125;
  color: #fff;
`;
// object syntax
const mainClass = css({
  display: "flex",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
});
```

To apply the styles to your DOM element, add the styling as a class name to your element:

```html
<html lang="en" className={bodyBackground}>
</html>
<main className={mainClass}>
</main>
```

Next, we’ll use the `styled` API to create our styled components. Here, we create a styled heading and add variants based on the props:

```javascript
const Heading = styled('div')({
  fontSize: "2rem",
  color: "#9FADBC",
  fontWeight: "bold",
  margin: "1rem",
  variants: [
    {
      props: { variant: 'success' },
      style: { color: '#23AD79' },
    },
    {
      props: { size: 'small' },
      style: { fontSize: '1.5rem' },
    },
  ],
});
```

We then add the component to our DOM as shown below. One heading uses the base styles while the other heading uses the variant styles on top of the base style:

```javascript
<Heading>Pigment CSS</Heading>
<Heading variant="success" size="small">Test Styling</Heading>
```

You can also style your components based on runtime values. The `isError` prop value is unknown ahead of time. It’s used to style the heading in a callback function:

```typescript
const Heading = styled('div')({
  fontSize: "2rem",
  color: ({ isError }: { isError: boolean }) => (isError ? 'red' : '#9FADBC'),
  fontWeight: "bold",
  margin: "1rem",
});
```

The prop value is passed to your component. Here, the heading color is set based on the `isError` value:

```css
<Heading isError>Test Styling</Heading>
```

### Theming with Pigment CSS

This is an optional feature that allows you to reuse the same styling values across your application using a theme object. You can make use of the `extendTheme` utility to generate CSS variables from your theme object. 

First, for type safety, let’s define our theme interface in a `theme.d.ts` file as shown below:

```typescript
import { ExtendTheme } from "@pigment-css/react/theme";
declare module "@pigment-css/react/theme" {
    interface ThemeTokens {
        colorScheme:{
            light: {
                primary: string;
                secondary: string;
                background: string;
                text: string;
                error: string;
            };
            dark: {
                primary: string;
                secondary: string;
                background: string;
                text: string;
                error: string;
            };
        }
    }
    interface ThemeArgs {
        theme: ExtendTheme<{
            colorScheme: "light" | "dark"
            tokens: ThemeTokens
        }>;
    }
}
```

In the code snippet above, we changed the Pigment CSS theme module. We added our `ThemeTokens`, which need to match our theme object. We then redefined the `ThemeArgs` with our color scheme and tokens. 

Now, we’ll set the theme module to `include` in the TypeScript config file:

```typescript
  "include": ["theme.d.ts","next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
```

In our `next.config` file, we added a theme option to the `withPigment` function. We also used the `extendTheme` utility to generate CSS variables for our theme:

```javascript
import { withPigment, extendTheme } from '@pigment-css/nextjs-plugin';
const nextConfig = {};
export default withPigment(nextConfig, {
    theme: extendTheme({
        colorScheme: {
            light: {
                primary: "#9FADBC",
                secondary: '#23AD79',
                background: '#fff',
                text: '#000',
                error: '#CC3131'
            },
            dark: {
                primary: "#9FADBC",
                secondary: '#23AD79',
                background: '#1D2125',
                text: '#fff',
                error: '#CC3131'
            },
        }
    }),
});
```

We can use the generated theme variables in the `css` or `styled` APIs as shown. You can apply styles based on the color scheme by using the `prefers-color-scheme` media query or the `applyStyles` functions attached to `extendTheme`:

```javascript
// template syntax
const bodyBackground = css`
  background-color: ${({ theme }) => theme.colorScheme.dark.background};
  color: ${({ theme }) => theme.colorScheme.dark.text};
  @media (prefers-color-scheme: light) {
    background-color: ${({ theme }) => theme.colorScheme.light.background};
    color: ${({ theme }) => theme.colorScheme.light.text};
  }
`;
// object syntax
const bodyBackground = css(({ theme }) => ({
  ...theme.applyStyles("light",{
    backgroundColor: theme.colorScheme.light.background,
    color: theme.colorScheme.light.text,
  }),
  ...theme.applyStyles("dark",{
    backgroundColor: theme.colorScheme.dark.background,
    color: theme.colorScheme.dark.text,
  })
})
)
```

## Conclusion

This tutorial explored the new CSS-in-JS library by MUI, Pigment CSS, a zero-runtime library. We discussed its key features and compared it to common CSS-in-JS libraries like Emotion and styled-components. We also went through some basic Pigment CSS library usage such as using the `css` and `styled` APIs and theming. 

Pigment CSS is still in the early release as of writing this article. The library will have more features and improvements as it grows. Happy coding!