Things I like 👍
Works at compile-time
StyleX compiles all styles into a static CSS file, eliminating the need for runtime style injection. Furthermore, the stylex.props()
function is also resolved at compile-time, unless it's provided with dynamic values.
// This is resolved at compile-time ...
const className = stylex.props(styles.card);
// ... into something like this
const className = { className: "x1p1rrjz" };
Atomic CSS
StyleX generates atomic CSS classes, where each unique property-value combination is created only once and then reused throughout the entire application. This approach helps maintain a minimal CSS output.
Co-location of styles
With StyleX, styles can be conveniently co-located with your components in the same file. This eliminates the need to constantly switch between files when working on a component.
const MyComponent = () => {
return <div {...stylex.props(styles.base)} />;
};
const styles = stylex.create({
base: {
color: "red",
},
});
Order of styles
StyleX allows for the merging of multiple styles, with the last style always taking precedence. For instance, if two styles both define the color
property, the one applied last will be used.
<div {...stylex.props(styles.blue, styles.red)}>This will be red</div>
<div {...stylex.props(styles.red, styles.blue)}>This will be blue</div>
Things I don't like 👎
Variables Format
StyleX provides support for variables, also known as design tokens, through the stylex.defineVars()
function in a *.stylex.ts
file. While it would be convenient to nest these design tokens, StyleX does not support this feature. Variables must be defined as a flat object. Additionally, re-exporting these variables is not supported.
// ✅ Tokens must be flat
export const tokens = stylex.defineVars({
fontSizeM: "1rem",
fontSizeL: "2rem",
spaceM: "16px",
spaceL: "20px",
});
// ❌ Tokens cannot be nested
export const tokens = stylex.defineVars({
font: {
size: {
m: "1rem",
l: "2rem",
},
},
space: {
m: "16px",
l: "20px",
},
});
// ❌ Tokens cannot be re-exported as a single object
export const tokens = {
font: fontTokens,
size: sizeTokens,
};
Props API
The stylex.props()
function manages both static styles through the className
prop and dynamic styles via the style
prop. Although it's beneficial to have a single API that handles both scenarios, it can be verbose and less than ideal when the vast majority of styles are static.
// StyleX
<div {...stylex.props(styles.card)} />
// Others like Panda CSS, Vanilla Extract, ...
<div className={card} />
Styles Object
In StyleX, styles cannot be passed directly to the stylex.create()
function. Instead, they must be encapsulated within an object with additional keys. While this approach may seem like unnecessary boilerplate when defining a single style, it does promote consistency as styles are always wrapped in an object.
const styles = stylex.create({
card: { // <- Nesting is necessary
boxShadow: "...",
},
});
No ampersand selector
In StyleX, styles are strictly applied to the element to which the class is added. The use of the &
selector to target other elements is not supported. While this design choice promotes encapsulation and reduces side effects, there are scenarios where targeting other elements could be beneficial. For instance, when constructing a Stack component or a Flow component.
// ❌ Not possible with StyleX
const styles = stylex.create({
flow: {
"& > * + *": {
marginTop: "1.5em",
marginBottom: 0,
},
},
});
Vanilla Extract does support the &
selector, but the styles must be applied directly to the element itself. For instance, :hover > &
is permissible, whereas & > :hover
is not. However, for the latter case, it's possible to utilize the globalStyle
API. This seems to be a reasonable compromise.
export const myStyle = style({
// Child elements cannot be styled here ...
});
globalStyle(`${myStyle} > :hover`, {
// ... but here they can
});
No global styles
StyleX is designed to support scoped styles for components exclusively. Consequently, it doesn't allow for global CSS resets or styling of the body
element (unless you have control over the element, which is typically not the case in a standard React + Vite project).
While you could resort to using plain CSS files for these tasks, this approach doesn't provide access to the design tokens.
Limited API
StyleX has a very limited API, which can be a good thing as well. But libraries like Vanilla Extract and Panda CSS offer much more flexibility for styling components (maybe too much?).