Written by Ivy Walobwa✏️
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 and Emotion. MUI, a React component library, recently released a promising, zero-runtime CSS-in-JS library called Pigment CSS. 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 is a zero-runtime CSS-in-JS library maintained by the Material UI team and built on top of WyW-in-JS, 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 uses runtime CSS-in-JS, and Emotion 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 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:
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:
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:
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:
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 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:
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:
<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:
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:
<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:
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:
"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:
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
:
// 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!
Top comments (0)