• frontend
  • css
  • css-in-js
  • stylex
  • javascript

StyleX: Looking for the perfect CSS or the evolution of styles

StyleX cover

Over the past few years, I’ve worked with all kinds of styling approaches — from classic CSS to Styled Components. Every time, it was a challenge to find the balance between developer experience and performance.

For a long time, Sass Modules were my go-to solution. But recently, I discovered StyleX — a CSS-in-JS framework developed by Meta. Honestly, it’s the strongest approach to styling I’ve ever worked with. But to really understand its philosophy, let’s rewind a bit.

Traditional CSS — the beginning#

In 1996, CSS 1.0 was born. We wrote .css files, linked them in HTML, and it just worked — though with very limited capabilities.

.button {
  border: 1px solid black;
  background: gray;
  color: white;
}

Then came CSS 2.0 in 1998. It introduced positioning, z-index, media queries — and this model dominated the 2000s. But as projects grew, so did the issues: global styles, duplicated code, the infamous !important.

.button {
  border: 1px solid #000;
  background: #808080;
  color: #fff;
  padding: 4px 8px;
  cursor: pointer;
}

.button:hover {
  background: #a0a0a0;
}

To tackle the chaos, Yandex introduced BEM (Block-Element-Modifier) in 2007 — a methodology to structure CSS. It brought order but at the cost of verbosity and lots of boilerplate.

.button {
  border: 1px solid #000;
  padding: 8px 16px;
}

.button--state-primary {
  background: blue;
  color: white;
}

.button--state-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

The preprocessor era#

In 2006, Sass arrived — the first CSS preprocessor. It added variables, mixins, nesting, and more. We wrote .scss files, compiled it into clean CSS, and our lives got easier. Later came Less, Stylus, and LibSass.

$primary: #4361ee;

.button {
  padding: 12px 24px;
  border-radius: 4px;
  transition: background 0.2s;

  &--primary {
    background: $primary;
    color: white;

    &:hover {
      background: darken($primary, 10%);
    }
  }
}

But the core problem remained: global scope.

CSS-in-JS: a mindset shift#

From 2010 to 2014, early CSS-in-JS tools like JSS started emerging.

const styles = {
  button: {
    padding: "12px 24px",
    borderRadius: "4px",
    backgroundColor: "#4361ee",
    "&:hover": {
      backgroundColor: "#3a56d4",
    },
  },
};

function Button() {
  return <button style={styles.button}>Click</button>;
}

But the real breakthrough came in 2015 with Styled Components, and later Emotion. By 2017, even Material UI had gone all-in on CSS-in-JS (v3 and v4 versions used JSS, v5 version used Emotion).

const Button = styled.button`
  padding: 12px 24px;
  border-radius: 4px;
  background: ${(props) => (props.primary ? "#4361ee" : "#e0e0e0")};
  color: ${(props) => (props.primary ? "white" : "black")};
  transition: background 0.2s;

  &:hover {
    background: ${(props) => (props.primary ? "#3a56d4" : "#d0d0d0")};
  }
`;

const styles = css`
  padding: 12px 24px;
  border-radius: 4px;
  background: ${(props) => (props.primary ? "#4361ee" : "#e0e0e0")};

  &:hover {
    background: ${(props) => (props.primary ? "#3a56d4" : "#d0d0d0")};
  }
`;

function Button({ primary, children }) {
  return (
    <button css={styles} primary={primary}>
      {children}
    </button>
  );
}

CSS-in-JS solved several key problems:

  • Styles colocated with components;
  • Dynamic styling via props and state;
  • Automatic class name scoping;
  • Theme and context support.

But it came with downsides:

  • Runtime overhead — styles injected via JavaScript;
  • SSR issues — hydration could be slow or fragile.

CSS Modules — a middle ground#

Around 2015–2016, CSS Modules emerged. They kept the structure of classic CSS but scoped class names automatically. This partially solved the global scope problem and made styles more predictable. No need for BEM anymore — and BEM started to fade away.

.button {
  padding: 12px 24px;
  border-radius: 4px;
  background: #4361ee;
}

.button:hover {
  background: #3a56d4;
}
import styles from "./Button.module.css";

function Button() {
  return <button className={styles.button}>Click</button>;
}

Tailwind CSS — the atomic mindset#

Meanwhile, Tailwind CSS introduced a utility-first approach. Instead of writing traditional CSS, you composed your UI from small atomic classes. It became popular for its speed and flexibility — though it had a learning curve and scaling issues in large design systems.

function Button({ primary }) {
  return (
    <button
      className={`
        px-6 py-3 rounded
        ${
          primary
            ? "bg-blue-600 hover:bg-blue-700 text-white"
            : "bg-gray-200 hover:bg-gray-300"
        }
        transition-colors
      `}
    >
      Click
    </button>
  );
}

Zero-runtime CSS-in-JS#

In 2017, Linaria showed up — a CSS-in-JS solution with no runtime. Styles were extracted at build time, eliminating the runtime cost. With modern bundlers like Vite, Parcel, and Webpack + SWC, CSS-in-JS entered a new generation.

const styles = css`
  padding: 12px 24px;
  border-radius: 4px;
  background: ${(props) => (props.primary ? "#4361ee" : "#e0e0e0")};

  &:hover {
    background: ${(props) => (props.primary ? "#3a56d4" : "#d0d0d0")};
  }
`;

function Button({ primary, children }) {
  return (
    <button className={styles} primary={primary}>
      {children}
    </button>
  );
}

StyleX — the future of CSS#

In 2022, Meta introduced StyleX — a true zero-runtime CSS-in-JS tool. All styles are extracted and compiled during build time, with class names fully optimized. It’s used across Meta’s major projects and proven to work at scale.

import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    padding: "12px 24px",
    borderRadius: "4px",
    transition: "background 0.2s",
  },
  primary: {
    backgroundColor: "#4361ee",
    color: "white",
    ":hover": {
      backgroundColor: "#3a56d4",
    },
  },
});

function Button({ primary }) {
  return (
    <button {...stylex.props(styles.base, primary && styles.primary)}>
      Click
    </button>
  );
}

Why it’s a game changer:

  1. Zero-runtime — styles are precompiled, no JS injection;
  2. Blazing performance — minimal selectors, no hashes or noise;
  3. Deep React integration — including Server Components;
  4. Static analysis — like TypeScript for your CSS;
  5. Dead code elimination — no duplicates, no unused styles.

It’s not just another compiler — it’s incredibly efficient, with advanced optimizations you won’t find in other tools.

Final thoughts#

StyleX might be the best thing to happen to CSS in recent years. It’s not an experiment — it’s a production-grade tool built for serious frontend apps.

It’s ideal for:

  • Large-scale UI systems;
  • SSR applications;
  • React Server Components;
  • Teams who want strict typing and zero-runtime cost.

Tailwind, CSS Modules, Emotion — they all have their place. But if you’re after predictability, scalability, and raw performance — you really should give StyleX a try. It fundamentally changes how styling feels.