Every year in October, the biggest international react conference takes place in Goa, India. Yes, I am talking about React India. This year (2024), was even more special for me as I got a chance to speak at this magnificent conference. Here's the recording of my talk if you've missed to watch it live. If you prefer reading over watching videos, then this blog is just for you! Let's dive into it.
StyleX is Meta's new, scalable styling library that is now used as the primary system behind platforms like Facebook, Instagram, and WhatsApp. It addresses the pain points experienced with CSS-in-JS approaches, particularly in massive React applications. By offering a hybrid solution that blends the best features of both atomic CSS and static CSS, StyleX offers an efficient, modular, and scalable alternative.
Atomic CSS Generation: StyleX employs atomic CSS generation, which means it creates small, reusable classes for each style rule. This approach not only minimizes redundancy in the final CSS bundle but also improves performance by reducing the overall size of the stylesheets.
CSS Deduplication: By generating unique class identifiers for each style, StyleX effectively eliminates duplicate styles. This deduplication process ensures that each property-value pair is rendered only once, further contributing to a leaner CSS output.
“The Last Style Applied Always Wins!”: StyleX follows a predictable styling rule where the last style applied takes precedence. This feature simplifies debugging and enhances developer confidence, as it mitigates concerns about conflicting style rules.
Optimized for React: Designed specifically for React applications, StyleX integrates seamlessly into the React ecosystem. It allows developers to define styles directly within their components, fostering a more cohesive development workflow.
Flow and TypeScript Support: StyleX is written in "Flow" (created by Meta) and it also provides robust support for TypeScript, enabling type-safe APIs for styles and themes. This type safety enhances code reliability and maintainability, making it easier to manage complex styling scenarios.
Flexible Conditional Styling: With StyleX, developers can apply styles conditionally based on component states or props. This flexibility allows for dynamic styling that adapts to user interactions or changes in application state.
Scoped Styling: The scoped styling feature of StyleX ensures that styles are applied only to the components they are intended for. This prevents unintended side effects and specificity issues that often arise in larger codebases.
Fewer Runtime Calculations: StyleX minimizes runtime calculations by bundling all styles into a static CSS file at compile time. This optimization leads to faster rendering times and improved performance, especially in larger applications.
Better Code Maintainability: By co-locating styles with their respective components and utilizing atomic classes, StyleX promotes better code maintainability. Developers can easily understand and modify styles without sifting through extensive stylesheets.
Minimal CSS Output: The use of atomic CSS results in minimal CSS output, which is particularly beneficial for performance. As projects grow in size and complexity, StyleX ensures that the CSS bundle remains manageable without sacrificing functionality.
Works Well for Projects of All Sizes: While StyleX is suitable for projects of all sizes, it truly excels in larger applications. Its architecture is designed to handle the complexities of extensive styling needs without compromising on performance or maintainability.
The code examples in this article are written in React, and we will primarily work with two components, App.jsx and Button.jsx. Let's take a look at the basic structure of these components before we add styles.
import Button from "./components/Button"; const App = () => { return ( <div> <h1>StyleX by Meta</h1> <Button text="Get Started" /> </div> ); }; export default App;
// Button.jsx import PropTypes from "prop-types"; const Button = ({ text }) => { return <button>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
import PropTypes from "prop-types"; import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { fontSize: 18, backgroundColor: "black", color: "white", }, }); const Button = ({ text }) => { return <button {...stylex.props(styles.base)}>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
To use these styles, we need to import them from the styleX package and then define the styles using stylex.create method that takes an object as a parameter. We can then use the stylex.props method to apply the styles to the component.
In this example, base is the name of the style that we want to apply. We call them namespaces in StyleX. This is how our button component looks like now.
import PropTypes from "prop-types"; import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { fontSize: 18, backgroundColor: { default: "black", ":hover": "blue", }, color: "white", }, }); const Button = ({ text }) => { return <button {...stylex.props(styles.base)}>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
With StyleX, it's pretty simple to add styles to pseudo-classes. In the previous example, backgroundColor was a string. Here, we convert it to an object with the default value and a pseudo-class.
import PropTypes from "prop-types"; import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { fontSize: 18, backgroundColor: { default: "black", ":hover": "blue", }, color: "white", width: { default: "100px", "@media (max-width: 476px)": "100%", }, }, }); const Button = ({ text }) => { return <button {...stylex.props(styles.base)}>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
One thing that we do differently in StyleX when compared to other styling libraries is the media queries. Here, we apply media queries to every namespace based on requirements. In this example, we are defining the width of the button to be 100px for larger screens and 100% width for smaller screens or mobile devices.
Let's extend the previous example to see how we can create different variants of this button.
const styles = stylex.create({ base: { fontSize: 18, backgroundColor: { default: "teal", ":hover": "blue", }, color: "white", width: { default: "100px", "@media (max-width: 476px)": "100%", }, }, highlighted: { backgroundColor: "orange", }, danger: { backgroundColor: "red", }, primary: { backgroundColor: "green", }, }); const Button = ({ text, isHighlighted, variant }) => { return ( <button {...stylex.props( styles.base, isHighlighted && styles.highlighted, // conditional styling styles[variant] )} > {text} </button> ); }; Button.propTypes = { text: PropTypes.string.isRequired, isHighlighted: PropTypes.bool, variant: PropTypes.oneOf(["danger", "primary"]), };
Let's add a few more namespaces to stylex.create method and provide them with different background colors. Additionally, we are accepting 2 new props within our Button component. isHighlighted is a boolean prop that we use to apply the highlighted namespace. And variant is a prop that we use to apply the primary, danger or highlighted namespace.
// App.jsx import Button from "./components/Button"; const App = () => { return ( <div> <h1>StyleX by Meta</h1> <div {...stylex.props(styles.main)}> <Button text="Base Button" /> <Button text="Highlighted Button" isHighlighted /> <Button text="Danger Button" isHighlighted variant="danger" /> <Button text="Primary Button" variant="primary" /> </div> </div> ); }; export default App;
We create a few more copies of the Button component with different props being passed on. This is how our app looks like now.
Now, take a closer look at 'Danger Button'. Even though we have passed in isHighlighted as true, the highlighted namespace will not be applied. The danger variant is mentioned last and so it will be applied. Thus, the button will have a red background color.
We could override the style properties of this Button component from App.jsx directly.
import Button from "./components/Button"; const App = () => { return ( <div> <h1>StyleX by Meta</h1> <Button text="Get Started" /> </div> ); }; export default App;
In this example, the override namespace currently allows any properties. However, StyleX gives us the capability to limit which properties can be overridden. This feature becomes particularly useful when using TypeScript.
// Button.jsx import PropTypes from "prop-types"; const Button = ({ text }) => { return <button>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
This limitation ensures that only the backgroundColor and color properties can be overridden.
If you scroll up to the previous example code, you will see that we have added margin: "1rem" style to 3 different namespaces - main in App.jsx, highlighted and primary in Button.jsx. When we inspect the element using Devtools, we can see that the different components (main container, highlighted button, and primary button) are attached with the same class name and there is only 1 class x42y017 that holds margin: "1rem" style.
That's how StyleX significantly reduced its bundle size by employing atomic classes. After reaching a certain threshold, no new classes are generated; instead, they simply reuse the existing classes.
Being able to override styles at a granular level is great! However, any given design systems need to support design tokens and themeing. That's where StyleX comes in. The design of the theming APIs in StyleX are directly inspired by React's Context APIs. Variables are defined with default values similar to how React Contexts are created, and themes can be created to “provide” different values for these variables for UI sub-trees.
We can create global styles by creating a x.stylex.js file. Make sure to follow this naming convension. In this file, we make use of stylex.defineVars as shown below.
import PropTypes from "prop-types"; import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { fontSize: 18, backgroundColor: "black", color: "white", }, }); const Button = ({ text }) => { return <button {...stylex.props(styles.base)}>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
We are referring to the user's preferred theme and setting it to a constant value - DARK. Further, let's create a new theme using this colors variable.
import PropTypes from "prop-types"; import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { fontSize: 18, backgroundColor: { default: "black", ":hover": "blue", }, color: "white", }, }); const Button = ({ text }) => { return <button {...stylex.props(styles.base)}>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
Once the theme is created, it can be used just like any other style in StyleX.
import PropTypes from "prop-types"; import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { fontSize: 18, backgroundColor: { default: "black", ":hover": "blue", }, color: "white", width: { default: "100px", "@media (max-width: 476px)": "100%", }, }, }); const Button = ({ text }) => { return <button {...stylex.props(styles.base)}>{text}</button>; }; Button.propTypes = { text: PropTypes.string.isRequired, }; export default Button;
That's how we can see the same page with myCustomTheme in light and dark mode respectively.
Hurray! We have successfully got a gist of working with StyleX. Thank you for reading through this article. I hope it provided a good understanding of what is StyleX, how did Meta create it, and how to use it. Please share your thoughts/queries in the comments section or on Twitter. If this blog is interesting to you, I would appreciate it if you could give this post a like (with your favorite emoji ?).
Peace ✌
Connect with me on Topmate For Interview Preparation
The above is the detailed content of Decoding StyleX: Metas Cutting-Edge Styling System. For more information, please follow other related articles on the PHP Chinese website!