ProNextJS
    Loading
    lesson

    Intro to Meta's StyleX

    Jack HerringtonJack Herrington

    If you're working at a large company or on an open-source project that requires strict control over CSS styles and their extensibility, the StyleX library from Meta that might be a good choice for you.

    StyleX provides a more rigid approach compared to CSS modules and Tailwind, but it can be beneficial when you need well-defined and locked-in styles. Let's apply StyleX to our current unstyled application.

    Installation and Configuration

    StyleX has installation instructions for Next.js in the docs that will always be up to date, but we'll run through them here as well.

    The first step is to install dependencies. The @stylexjs/open-props and @stylexjs/stylex packages are required for the application, and we'll also add some dev dependencies:

    pnpm add @stylexjs/open-props @stylexjs/stylex
    pnpm add -D rimraf @stylexjs/babel-plugin @stylexjs/eslint-plugin @stylexjs/nextjs-plugin
    

    This will create some new configuration files in our project.

    Babel Setup

    StyleX is developed as a compiler extension, which is configured via the .bablerc.js file. The Next.js Babel presets will still be in place, but we'll add the StyleX Babel plugin on top of it. This will switch the compilation from the faster SWC to the slightly slower Babel because SWC cannot handle JavaScript plugins.

    Here's how it looks:

    // inside .babelrc.js
    module.exports = {
      presets: ["next/babel"],
      plugins: [
        [
          "@stylexjs/babel-plugin",
          {
            dev: process.env.NODE_ENV === "development",
            test: process.env.NODE_ENV === "test",
            runtimeInjection: false,
            genConditionalClasses: true,
            treeshakeCompensation: true,
            unstable_moduleResolution: {
              type: "commonJS",
              rootDir: __dirname,
            },
          },
        ],
      ],
    };
    

    ESLint Configuration

    The ESLint configuration makes it so StyleX extensions without freaking out. The configuration file will also be changed from JSON to JS format:

    // inside .eslintrc.js
    
    module.exports = {
      extends: "next/core-web-vitals",
      plugins: ["@stylexjs"],
      rules: {
        // The Eslint rule still needs work, but you can
        // enable it to test things out.
        "@stylexjs/valid-styles": "error",
      },
    };
    

    Next.js Configuration

    As of this recording, the NextJS installation instructions use JS for the configuration. However, an App Router application gives an MJS (ModuleJS) config. Inside of the next.config.mjs file, we need to import the StyleX plugin and export its output by default:

    // inside next.config.mjs
    
    /** @type {import('next').NextConfig} */
    import stylexPlugin from "@stylexjs/nextjs-plugin";
    
    const nextConfig = {
      transpilePackages: ["@stylexjs/open-props"],
    };
    
    const __dirname = new URL(".", import.meta.url).pathname;
    export default stylexPlugin({
      rootDir: __dirname,
    })(nextConfig);
    

    Now that the setup is complete, let's start styling our application with StyleX.

    Styling the Application

    The first thing we'll do is set up a CSS reset in the globals.css file. This will ensure that we start from a clean slate:

    /* inside globals.css */
    * {
      box-sizing: border-box;
      padding: 0;
      margin: 0;
    }
    

    We can now import globals.css and stylex into the layout.tsx file:

    // inside layout.tsx
    import * as stylex from "@stylexjs/stylex";
    
    import "./globals.css";
    

    Now we can move on to adding light and dark mode.

    Light and Dark Mode

    At the bottom of layout.tsx, we'll establish styles using StyleX. We'll set up classes for html and reset, where the html class will define the default light mode styles with an option for dark mode and the reset class will reset the default styles:

    const DARK = "@media (prefers-color-scheme: dark)";
    
    const styles = stylex.create({
      html: {
        fontFamily: "system-ui, sans-serif",
        backgroundColor: "white",
        color: "black",
        [DARK]: {
          backgroundColor: "black",
          color: "white",
        },
      },
      reset: {
        minHeight: "100%",
        margin: 0,
        padding: 0,
      },
    });
    

    The [DARK] syntax acts as a media query that applies the dark mode styles when the user prefers a dark color scheme.

    Styles are then added as props on the tags by spreading the stylex.props function:

    // inside layout.tsx
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html {...stylex.props(styles.html, styles.reset)} lang="en">
          <body {...stylex.props(styles.reset)}>{children}</body>
        </html>
      );
    }
    

    The stylex.props function injects the class names into the tags, allowing StyleX to have both class name and style output.

    Previewing our changes in Polypane, we can see that the light and dark mode styles are applied correctly:

    dark mode is working

    Apply Styles to the Home Component

    Over in the page.tsx file, your task is to import StyleX and set up styling using the styles provided here:

    const MOBILE = "@media screen and (max-width: 768px)";
    
    const s = stylex.create({
      main: {
        margin: "0 auto",
        maxWidth: 960,
        fontFamily: "sans-serif",
        display: "flex",
        flexWrap: "wrap",
      },
      card: {
        padding: "0.2rem",
        width: {
          default: "33%",
          [MOBILE]: "100%",
        },
        maxWidth: {
          default: "33%",
          [MOBILE]: "100%",
        },
      },
    });
    

    Apply these styles to the <main> and <div key={product.id}> tags.

    In the next lesson, we'll complete the example and see how everything comes together with StyleX.

    Transcript