What does StyleX solve?
Tags: stylex, tailwindcss, frontend, webdev
Published: 2023-12-18 00:30:40

Facebook/Meta recently released the [StyleX](https://github.com/facebook/stylex) library, and already there are many articles and youtube videos explaining how it's **THE TAILWIND KILLER** (ooh, spooky).

I'm going to go through some of the main points from [this video](https://www.youtube.com/watch?v=dphmbB77W_4) by [Web Dev Simplified](https://www.youtube.com/@WebDevSimplified) to try and explain why I'm not convinced of the StyleX hype. If you're not familiar with the StyleX library, it's a nice way to get introduced to it. This channel has a lot of great videos, even though I don't agree with his take here.

So let's start with the first big StyleX feature:


## Co-location
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sm8j6cnx19xu02wkt79h.png)

Co-location is the practice of closely grouping things that belong together. So why not keep styles in the same place where you define your component code?

I have nothing against this approach. In fact, I'm glad that we are embracing this principle more and more. But this implementation honestly seems like a step back from what Tailwind is doing. If we take a look at the example:

```jsx
import { Button } from "./Button"
import * as stylex from "@stylexjs/stylex"

const styles = stylex.create({
  heading: {
    color: "blue",
  },
})

export default function App() {
  return (
    <>
      <h1 {...stylex.props(styles.heading)}>Heading</h1>
      <Button>Button</Button>
    </>
  )
}
```

Compared to how we would write it in Tailwind:

```jsx
import { Button } from "./Button"

export default function App() {
  return (
    <>
      <h1 className="text-blue">Heading</h1>
      <Button>Button</Button>
    </>
  )
}
```

Seems like the Tailwind implementation is actually better with co-location. The classes that impact the element are written **directly** on the element, instead of somewhere above or below.

The difference in the example is small, but imagine defining styles for a more complex component. With StyleX, you would have a big chunk of CSS-like code at the beginning of your file (as is visible later in the video), in which case you would have to keep scrolling up-and-down to find and change the styling for a given element. With Tailwind this is never the case, because you only change the classes on the element you want to change itself.

At that point, it might actually be helpful to move the StyleX code to another file, so you can keep the styling code open side by side with the component code. And if you're doing that, you're basically just doing CSS Modules, so I'm not really sure how this is better?

## Deterministic resolution
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mj01vs854c1abctp7xm3.png)


> "The last style applied always wins."

Conflicting CSS selectors can be a pain, especially if you don't use a convention like [BEM](https://getbem.com/) or something similar.
If you use Tailwind however, you never have these specificity selector issues, because the classes are atomic, and applied directly on the element in the HTML.

So unless you are just randomly creating selectors, without any convention or structure, and without scoping things with CSS Modules, I'm not really sure what this solves? 
If you are, then I don't think StyleX is gonna help you much. You have an organizational problem, not a tooling one.

## Bigger projects are easier to maintain with StyleX.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/plzvbf3m88ro19bngzce.png)

I don't really think this is the case. Actually I think on larger projects, StyleX will suffer from almost the same issues that CSS does.
One of the biggest issues with CSS on bigger projects, is knowing if a defined selector is still necessary, or if it can be removed from the stylesheet. Bigger projects tend to have a lot of selectors which get applied in multiple places, so knowing if a selector should or shouldn't be removed can be tricky.

Since "The last style applied always wins." (just like the selector with the most specificity wins in plain CSS), all the styles that are applied until the last one may or may not be partially needed. So on larger projects you will still have the issue described above, just with a different tool. It definitely helps a bit that these useless styles are stripped in production, but they still remain in the codebase and make maintenance harder.

## Reusability

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zp68cefr8wihziu2d35g.png)


I really don't think styles should be shared between components, expect maybe in **very** specific cases. A components style is an integral and identifiable part of it. Trying to reuse it, or a part of it just creates extra dependencies which makes things weird, and long term project maintenance difficult. 

Let me try to explain what I mean through this example: Let's say we have a simple button with a little bit of styling on our project:

```jsx
import * as stylex from "@stylexjs/stylex"

export const styles = stylex.create({
  base: {
    backgroundColor: "blue",
    color: "white",
    borderRadius: ".25em",
  },
})

export default function Button(props) {
  return (
      <button
        onClick={props.onClick}
        {...stylex.props(styles.base)}
      >
        {props.children}
      </button>
  )
}
```

And let's say that at some point we need to define another button, which should be the same as the original button, but a little bit different. 
So we might do something like this:

```jsx
import * as stylex from "@stylexjs/stylex"
import {styles} from "./Button.jsx

const newStyles = stylex.create({
  base: {
    backgroundColor: "red",
  },
})

export default function DeleteButton(props) {
  return (
      <button
        onClick={() => {
          if (confirm("Are you sure?") {
            props.onClick()
          }
        }}
        {...stylex.props(styles.base, newStyles.base)}
      > 
         {props.children}
      </button>
  )
}
```

This works, but puts the `DeleteButton` component in a weird spot. Co-location is effectively broken, because the styling for `DeleteButton` depends on the styling from the `Button` component. A dependency is also introduced, which means that any future style changes to the `Button` will also impact the `DeleteButton`. So your boss and QA are gonna love you, for making their job twice as difficult.

We could also extract the styling to a separate file like a `button-styles.js`:

```js
import {styles} from "./Button.jsx

export const styles = stylex.create({
  base: {
    backgroundColor: "blue",
    color: "white",
    borderRadius: ".25em",
  },
  delete: {
    backgroundColor: "red",
  },
})

```

But at this point we are just writing CSS with extra steps, so I don't really see the benefit?


A simpler approach would be to just keep the styles unique to each component, but at that point we have no need to share the styles, so reusability becomes a moot point. And since Tailwind is better with co-location as discussed above, it could look something like this:
```jsx
export default function Button(props) {
  return (
      <button
        onClick={props.onClick}
        className="bg-blue text-white rounded"
      >
        {props.children}
      </button>
  )
}
```

```jsx
export default function DeleteButton(props) {
  return (
      <button
        onClick={() => {
          if (confirm("Are you sure?") {
            props.onClick()
          }
        }}
        className="bg-red text-white rounded"
      > 
         {props.children}
      </button>
  )
}
```

This is my preferred approach, as it keeps things simple, and components contained and independent of each other. And your boss and QA not hating you is a nice bonus.


## Conditionally applying styles

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hn7zkbu529r5ahr27jdv.png)

This feature is nice, but libraries like [classnames](https://www.npmjs.com/package/classnames), and [clsx](https://www.npmjs.com/package/clsx) already solved this a long time ago.
If you are using Tailwind, you might be worried that your code won't look as nice if you have a bunch of classes on your element tags.

**We are not** in the business of making code look pretty, and satisfying your aesthetic wants for numbers of characters on screen per file.

**We are** in the business of building maintainable solutions that are as simple as possible, and easy to maintain.

Stop trying to "make code pretty". It doesn't have to be pretty, but [it should always be expressive](https://dev.to/noblica/expressive-code-clean-code-59o7). 

## Overriding styles from parent components.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8maorkq4n5bi20pznm5.png)

**Do not do this. Do not pass random styles around to components.**

**Not even once.**

Unless you are making a component library akin to [headlessui](https://headlessui.com/) you should never just pass random styles to components.

A better solution is to create an interface from the child component via props, where you can specify which version of the child component you want. If we use the button example from above, and the classnames library:

```jsx
import classnames from "classnames"

export default function Button(props) {
  return (
      <button
        onClick={() => {
          if (confirm("Are you sure?") {
            props.onClick()
          }
        }}
        className={classnames(
          {
            "bg-blue": !props.variant
            "bg-red": props.variant === "delete"
          }
          "text-white rounded"
       )}
      > 
         {props.children}
      </button>
  )
}
```

This way the styling stays internal to the child component, consistent across the application, but still flexible based on the `variant` property.

## Final Thoughts
As the title says, I am not sure what StyleX is supposed to solve, since most of the things that it solves have better solutions in other places. 

If you're starting a new project, just use Tailwind, and you're already winning.

If you already have a codebase in CSS, I don't see the need to switch to StyleX, or how it would "evolve" your styling. If you have some of the issues described above, try adopting a methodology like BEM, and using CSS Modules for scope isolation. It's easier then rewriting everything in StyleX, and you'll have more maintainable code.

If you have an existing large codebase using CSS-in-JS:
1. You are suffering more than any of us.
2. **StyleX might actually make sense for you**, but I still wouldn't recommend it.

You would definitely see some perf improvements, and the syntax seems quite similar so transition should be easy. But StyleX is quite young, so you will probably experience some issues with the transition.

Even though I crapped on StyleX throught this article, I always think it's cool when people are trying something new. And I think it's great that people behind StyleX are trying to make something to make our professional lives easier. 

I'm keeping an open mind about this topic, so feel free to try to change it in the comments!