ProNextJS
    Loading
    lesson

    Styling with Material UI's Pigment CSS

    Jack HerringtonJack Herrington

    Now we'll look at using Pigment CSS, a build-time CSS solution that works seamlessly with React Server Components.

    Comparing Pigment with Material UI

    Material UI is a popular component framework for Next.js applications that use the Pages Router. However, it doesn't work natively with React Server Components. This is because Material UI is based on Emotion, which uses context for theming. Since React Server Components can't handle context, Material UI components need to be client components to manage their own styling. This is a fundamental issue that can't be fixed with a spot fix, so the team behind Material UI created Pigment CSS.

    Installation

    As usual, the installation instructions for Pigment CSS can be found in the official documentation, but we'll work through them here as well.

    First, we need to install the @pigment-css/react library:

    pnpm add @pigment-css/react
    

    Then we'll update the next.config.mjs file to import the Pigment CSS plugin. The nextConfig object is currently empty, but we'll wrap it in the withPigment function to set up Pigment CSS which will be the default export:

    // inside next.config.mjs
    
    import { withPigment, extendTheme } from "@pigment-css/nextjs-plugin";
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    export default withPigment(nextConfig);
    

    Implementing Dark and Light Mode

    In layout.tsx, import the base styles from Pigment as well as its css function:

    // inside layout.tsx
    import { css } from "@pigment-css/react";
    
    import "@pigment-css/react/styles.css";
    

    Styles can be defined by using either Pigment's object syntax or the backtick syntax Here's how the html class would be defined using both methods:

    const DARK = "@media (prefers-color-scheme: dark)";
    
    // Using object syntax
    const htmlClass = css(({ theme }) => ({
      backgroundColor: "white",
      color: "black",
      [DARK]: {
        backgroundColor: "black",
        color: "white",
      },
    }));
    

    Then the htmlClass can be added to the html element in the RootLayout component:

    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html lang="en" className={`${inter.className} ${htmlClass}`}>
          <body>{children}</body>
        </html>
      );
    }
    

    Saving our work, Polypane should show the dark background is working as expected.

    Styling the Main Content

    Over in the page.tsx file, we'll bring in the styled and css functions from Pigment::

    // inside page.tsx
    import { styled, css } from "@pigment-css/react";
    

    This time we'll use the backtick syntax to specify CSS for the mainClass:

    const mainClass = css`
      margin: 0 auto;
      max-width: 960px;
      font-family: sans-serif;
      display: flex;
      flex-wrap: wrap;
    `;
    

    It's generally a good idea to stick with one syntax for your entire application, but Pigment CSS allows you to choose the one that works best for you. There's no runtime performance issue here since all the styles are generated at build time.

    Next, we'll create a new Card component that will be a div element that has CSS applied to it with the styled function:

    const MOBILE = "@media screen and (max-width: 768px)";
    
    const Card = styled("div")`
      padding: 0.2rem;
      width: 33%;
      max-width: 33%;
      ${() => MOBILE} {
        width: 100%;
        max-width: 100%;
      }
    `;
    

    We'll stop here so you can update the existing Home component to use apply the mainClass styles and use the new Card component. In the next video, we'll review how it's done.

    Transcript