Stylex Facebook - Is It a CSS Solution
In this post you will learn about StyleX library which was made by Facebook and released not so long ago.
There are already lots of posts about StyleX and how amazing it is. People are saying that it's a best library for styling React and it is a killer of TailwindCSS.
Official website says that StyleX is a simple, easy to use Javascript syntax and compiler for styling web apps.
From my perspective this is just another library of writing CSS in Javascript but it is done by Facebook with quite good API. This is why this library will for sure be popular.
Just to remind you from the history first we just wrote CSS as an additional language to write styles for our HTML. Then at some point we got the idea of writing CSS in Javascript. It helps us to easier change our CSS based on Javascript variables because we are writing all our styling directly inside specific component.
It makes a lot of sense but it is still much easier to just load CSS than to execute Javascript and calculate all these styles on the fly.
Additionally at some point later we got solutions like TailwindCSS.
It's a library where you get lots of small helpers and you can combine them and avoid writing CSS at all and simply using hundreds of different classes inside your HTML.
StyleX is for sure not a replacement for TailwindCSS because they are too different.
Saying that StyleX is a killer of TailwindCSS is not correct. TailwindCSS allows you to write styles in a matter of seconds. StyleX on the other hand allows you to structure your styles, reuse them, and scale.
It's a CSS in Javascript library which can be built in CSS and it is type safe.
Configuration
Now we need to configure it. My typical project is generated with Vite. The first package that we want to install here is StyleX itself.
npm i @stylexjs/stylex
But it is not enough because you can't use StyleX just like a normal library. You need a compiler for all these styles inside your project. You can configure it with Webpack, Rollup or Babel but as people are typically using Vite for local development it makes a lot of sense to configure it together with Vite. We can easiely do that with additional package vite-plugin-stylex
.
npm i vite-plugin-stylex
After installation we must tune our Vite config a bit.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import styleX from "vite-plugin-stylex";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), styleX()],
});
Here we added styleX
plugin in the list of existing plugins.
Another thing that I prepared in our small project is a Button
component.
interface ButtonProps {
text: string
}
const Button = ({text}: ButtonProps) => {
return <button>{text}</button>
}
This is nothing more that a button where inside we are rendering text.
Writing basic styles
import * as stylex from '@stylexjs/stylex'
const styles = stylex.create({
base: {
fontSize: 18
}
})
const Button = ({text}: ButtonProps) => {
return <button {...stylex.props(styles.base)}>{text}</button>
}
Here we created a list of styles with stylex.create
. Every property holds multiple styles just like a class in plain CSS. All styles are written in camel case like in typical CSS in JS library.
In order to apply class we spread it with stylex.props
and providing a property of styles inside.
As you can see in browser our font size is bigger. Inside markup we are getting a unique class and on the right we see these styles. Most importantly these are inline styles which we get in the head
of the page.
Let's add several more styles.
const styles = stylex.create({
base: {
fontSize: 18,
backgroundColor: 'teal',
color: 'white'
}
})
As you can see extremely similar to plain CSS. You just need to remember that we use create
to generate styles and props
to spread them.
Working with specific styles
Here is an important point. If you previously used CSS in Javascript you always have questions there how to override a specific style. Inside StyleX it is done in extremely easy way.
It merges all styles partially and the last element in props always wins.
const styles = stylex.create({
base: {
fontSize: 18,
backgroundColor: 'teal',
color: 'white'
},
highlight: {
backgroundColor: 'orange'
}
})
const Button = ({text}: ButtonProps) => {
return <button {...stylex.props(styles.base, styles.highlight)}>{text}</button>
}
Here we passed to props
multiple styles. So here StyleX merges two objects but the question is how it overrides properties. On our case instead of backgroundColor
in base
it will apply backgroundColor
in highlight
.
Which essentially means that last element always wins.
As you can see we got a button with overridden background. But most importantly on the right we see just a single background and not lots of crossed out and not applies styles like in other CSS in JS libraries.
Hover & Focus
Now let's talk about things like hover and focus. It is completely possible to use them inside StyleX. Let's say that inside our base
we want to change our color on hover.
const styles = stylex.create({
base: {
fontSize: 18,
backgroundColor: {
default: 'teal',
':hover': 'blue'
},
color: 'white'
},
highlight: {
backgroundColor: 'orange'
}
})
const Button = ({text}: ButtonProps) => {
return <button {...stylex.props(styles.base, styles.highlight)}>{text}</button>
}
As you can see in order to apply a hover we converted our backgroundColor
from string
to an object
. Then we provided a key default
which gives us the same behavior as using a string
. After this we can apply :hover
that we want.
But most importantly our code won't work. Why is that? We still override the whole backgroundColor
with highlight
block and it doesn't merge them at all. The whole key backgroundColor
is removed.
Let's remove the highlight part for now.
const Button = ({text}: ButtonProps) => {
return <button {...stylex.props(styles.base)}>{text}</button>
}
As you can see now our hover effect works correctly. So this is the way how you can apply hover or focus effects when they are needed.
Media queries
In exactly the same way you can apply different media queries.
const styles = stylex.create({
base: {
fontSize: 18,
backgroundColor: {
default: "teal",
":hover": "blue",
},
color: "white",
width: {
default: "100px",
"@media (max-width: 800px)": "100%",
},
}
})
Here we applied a default width of our Button
and we wrote a media query that it must take full width when it is smaller that 800px.
As you can see now our button takes full width when browser size is smaller that 800px.
Styles resetting
Now the question is how we can remove a style without overriding it with something else.
const styles = stylex.create({
base: {
fontSize: 18,
backgroundColor: {
default: 'teal',
':hover': 'blue'
},
color: 'white'
},
highlight: {
backgroundColor: null
}
})
const Button = ({text}: ButtonProps) => {
return <button {...stylex.props(styles.base, styles.highlight)}>{text}</button>
}
In our case here inside backgroundColor
of highlight
I provided null
as a value. It will reset our backgroundColor
on inital. As you can see in browser our button don't have any background at all.
Styles with conditions
The next important question is how to apply some styles based on the conditions in our component.
interface ButtonProps {
text: string;
isHighlighted: boolean;
}
const Button = ({ text, isHighlighted}: ButtonProps) => {
return (
<button
{...stylex.props(
styles.base,
isHighlighted && styles.highlighted,
)}
>
{text}
</button>
);
};
Here I added isHighlighted
as a prop of our Button
component. Now we only want to apply our highlighted
styles when we passed inside this property. We simply used here an &
operator to apply styles with condition. If isHighlighted
is false then we will provide false
as a second parameter to props
and it won't be applied.
As you can see when we provide isHighlighted
prop to our component our styles are set to orange.
Variants for children
Another case that you often need is to implement different variants of the same component. Typically with button you have different styles with danger
, primary
, secondary
props. For such case we use variants.
const styles = stylex.create({
...
danger: {
backgroundColor: "red",
},
primary: {
backgroundColor: "green",
},
});
interface ButtonProps {
text: string;
isHighlighted: boolean;
variant: "danger" | "primary";
}
const Button = ({ text, isHighlighted, variant}: ButtonProps) => {
return (
<button
{...stylex.props(
styles.base,
isHighlighted && styles.highlighted,
styles[variant],
)}
>
{text}
</button>
);
};
Here we created one more prop in our component which is called variant
. It is either string danger
or primary
. Inside our markup we write styles[variant]
which just applies different styles by selecting different key of the object. Now we can create different variants inside our styles.
As you can see our button is red when we provide a variant danger
to the component.
Overriding styles
Another important question is if we can override our styles from the outside. Sometimes it is not enough to style our component in a good way, we still want to override something from the parent.
// src/App.tsx
import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({
override: {
backgroundColor: "black",
color: "white",
},
});
const App = () => {
return (
<div>
<h1>Monsterlessons Academy</h1>
<Button
text="foo"
isHighlighted
variant="danger"
style={styles.override}
/>
</div>
);
};
In our parent component App
we created a bunch of styles in property override
with StyleX. After this we provided them to the component in the key style
.
interface ButtonProps {
text: string;
isHighlighted: boolean;
variant: "danger" | "primary";
style: stylex.StyleXStyles;
}
const Button = ({ text, isHighlighted, variant, style }: ButtonProps) => {
return (
<button
{...stylex.props(
styles.base,
isHighlighted && styles.highlighted,
styles[variant],
style
)}
>
{text}
</button>
);
};
Now inside our ButtonProps
we accept style
which is a stylex.StyleXStyles
and we apply them as a last parameter in our stylex.props
. It means that we will override all styles of our button.
As you can see in browser our override was applied and we successfully changed our styles of the button.
Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!
📚 Source code of what we've done