# @stylexjs/babel-plugin (/docs/api/configuration/babel-plugin) ## Configuration options ### `aliases` ```ts aliases: {[key: string]: string | Array} // Default: undefined ``` `aliases` option allows you to alias project directories to absolute paths, making it easier to import modules. Example: `'@/components/*': [path.join(__dirname, './src/components/*')]` *** ### `classNamePrefix` ```ts classNamePrefix: string; // Default: 'x' ``` Prefix to be applied to every generated className. *** ### `debug` ```ts debug: boolean; // Default: false ``` When `true`, StyleX will make compiled style keys easier to read and insert `data-style-src` props to help identify the source of the styles. Generated atomic class names remain hashed. *** ### `dev` ```ts dev: boolean; // Default: false ``` When `true`, StyleX will insert function calls to inject the CSS rules at runtime, making it possible to use StyleX without setting up CSS file extraction. It also enables the DOM metadata used by the StyleX Dev Tools Extension. *** ### `importSources` ```ts importSources: Array; // Default: ['@stylexjs/stylex'] ``` Override the package name where you can import stylex from. Used for setting up custom module aliases. Example: `importSources: ['stylex', { from: '@acme/ui', as: 'css' }]` *** ### `runtimeInjection` ```ts runtimeInjection: boolean; // Default: false ``` Should StyleX generate code to inject styles at runtime? This may be useful during development but should be disabled in production. *** ### `styleResolution` ```ts styleResolution: // Default: 'property-specificity' | 'application-order' | 'property-specificity' ``` Strategy to use for merging styles. * **application-order**: The last style applied wins. Consistent with how inline styles work on the web. * **property-specificity**: More specific styles will win over less specific styles. Consistent with React Native. (`margin-top` wins over `margin`) *** ### `sxPropName` ```ts sxPropName: string | false; // Default: 'sx' ``` Customize JSX prop name for applying styles on HTML elements. Defaults to `sx`. ```tsx
``` is transformed to: ```tsx
``` Set this option to another string to use a different prop name, such as `css`. Set it to `false` to disable the JSX shorthand entirely. *** ### `test` ```ts test: boolean; // Default: false ``` When `true`, StyleX will only output debug classNames identifying the source of the styles. It will *not* generate any styles or functional classNames. This can be useful for snapshot testing. *** ### `env` ```ts env: { [key: string]: mixed }; // Default: {} ``` An object of compile-time constants and functions available as [`stylex.env`](/docs/api/javascript/env). Values are substituted before other StyleX APIs are compiled. Supports strings, numbers, objects, and functions. *** ### `treeshakeCompensation` ```ts treeshakeCompensation: boolean; // Default: false ``` Named imports of StyleX variables are unused after compilation. Some bundlers may remove them as dead code. Causing variables to be undefined. Enable this option to prevent that by adding an import with no specifier. (e.g. `import './my-vars.stylex.js'`) *** ### `unstable_moduleResolution` ```ts unstable_moduleResolution: // Default: undefined | { // The module system to be used. // Use this value when using `ESModules`. type: 'commonJS', // The absolute path to the root directory of your project. // Only used as a fallback rootDir?: string, // Override `.stylex.js` with your own extension. themeFileExtension?: string, } | { // Use this when using the Haste module system // Where all files are imported by filename rather // than relative paths and all filenames must be unique. type: 'haste', // Override `.stylex.js` with your own extension. themeFileExtension?: string, } ``` Strategy to use for resolving variables defined with `defineVars`. This is required if you plan to use StyleX's theming APIs. **NOTE**: While theming APIs are stable, the shape of this configuration option may change in the future. *** ## `processStylexRules` ```ts import styleXTransform from '@stylexjs/babel-plugin'; const css: string = styleXTransform.processStylexRules(rules, config); ``` `processStylexRules` takes an array of CSS rules collected from the babel plugin and returns the final CSS string. The optional `config` parameter controls layer wrapping and RTL handling. ### `config` ```ts config?: | boolean | { useLayers?: boolean | { before?: ReadonlyArray; after?: ReadonlyArray; prefix?: string; }; enableLTRRTLComments?: boolean; legacyDisableLayers?: boolean; }; ``` Passing `true` is a shorthand for `{ useLayers: true }`. ### `useLayers` ```ts useLayers?: boolean | { before?: ReadonlyArray; after?: ReadonlyArray; prefix?: string; }; // Default: false ``` Controls whether generated CSS is wrapped in `@layer` blocks for cascade ordering. When `true`, each priority level is placed in its own layer. When your project also uses other CSS layers (e.g. a reset layer, a design-system layer, or a utility framework like Tailwind), pass an object to position StyleX's layers relative to yours in the [`@layer` ordering declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) and to namespace them to avoid collisions: * **`before`** — layer names placed *before* StyleX layers in the ordering header. * **`after`** — layer names placed *after* StyleX layers. * **`prefix`** — string prepended to every StyleX layer name (e.g. `'stylex'` → `stylex.priority1`). ```ts const css = styleXTransform.processStylexRules(rules, { useLayers: { before: ['reset', 'base'], after: ['utilities'], prefix: 'stylex', }, }); ``` The generated CSS begins with a layer-ordering header followed by wrapped rules: ```css @layer reset, base, stylex.priority1, stylex.priority2, utilities; @layer stylex.priority1 { /* low-priority rules */ } @layer stylex.priority2 { /* higher-priority rules */ } ``` Bundler plugins (`@stylexjs/unplugin`, `@stylexjs/postcss-plugin`) expose this as **`useCSSLayers`** and pass it through to `processStylexRules` as `useLayers`. # @stylexjs/eslint-plugin (/docs/api/configuration/eslint-plugin) ## Configuration options ### `@stylexjs/valid-styles` rule ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; // Custom limits for values of various properties propLimits?: PropLimits; // @deprecated // Allow At Rules and Pseudo Selectors outside of // style values. // // Default: false allowOuterPseudoAndMedia: boolean; // @deprecated // Disallow properties that are known to break // in 'legacy-expand-shorthands' style resolution mode. // // Default: false banPropsForLegacy: boolean; // Mirrors the compiler option and enables legacy-mode // shorthand autofixers when set to 'legacy-expand-shorthands'. styleResolution?: | 'application-order' | 'property-specificity' | 'legacy-expand-shorthands'; }; type PropLimits = { // The property name as a string or a glob pattern [propertyName: string]: { limit: // Disallow the property | null // Allow any string value | 'string' // Allow any number value | 'number' // Any string other 'string' or 'number' // will be considered to be a valid constant // e.g. 'red' or '100px'. | string // You can also provide numeric constants // e.g. 100 or 0.5 | number // You can also provide an array of valid // number or string constant values. | Array; // The error message to show when a value doesn't // conform to the provided restriction. reason: string; }; }; ``` #### Example ```json { "rules": { "@stylexjs/valid-styles": [ "error", { "propLimits": { "mask+([a-zA-Z])": { "limit": null, "reason": "Use the `mask` shorthand property instead." }, "fontSize": { "limit": "number", "reason": "Only numeric font values are allowed" }, "padding": { "limit": [0, 4, 8, 16, 32, 64], "reason": "Use a padding that conforms to the design system" } } } ] } } ``` ### `@stylexjs/sort-keys` rule ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; // Minimum number of keys required after which the rule is enforced // // Default: 2 minKeys: number; // Sort groups of keys that have a blank line between them separately // // Default: false allowLineSeparatedGroups: boolean; }; ``` #### Example ```json { "rules": { "@stylexjs/valid-styles": "error", "@stylexjs/sort-keys": [ "warn", { "minKeys": 3, "allowLineSeparatedGroups": true } ] } } ``` ### `@stylexjs/valid-shorthands` rule ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; // Whether `!important` is allowed // // Default: false allowImportant: boolean; // Whether the expansion uses logical direction properties over physical // // Default: false preferInline: boolean; }; ``` ### `@stylexjs/enforce-extension` rule ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; // The file extension to enforce for theme files // // Default: '.stylex.js' themeFileExtension: string; }; ``` ### `@stylexjs/no-unused` rule ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; }; ``` ### `@stylexjs/no-conflicting-props` rule Disallows using `className` or `style` props on elements that spread `stylex.props()`, which would cause conflicts. ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; }; ``` ### `@stylexjs/no-legacy-contextual-styles` rule ```ts type Options = { // Possible strings where you can import stylex from // // Default: ['stylex', '@stylexjs/stylex'] validImports: Array; }; ``` # @stylexjs/postcss-plugin (/docs/api/configuration/postcss-plugin) ## Configuration options ### babelConfig ```js babelConfig: object; // Default: {} ``` Options for Babel configuration. By default, the plugin reads the `babel.config.js` in your project. For custom configurations, set `babelrc: false` and specify desired options. Refer to [Babel Config Options](https://babeljs.io/docs/options) for available options. Unless you explicitly provide `babelConfig.cwd`, the plugin uses its `cwd` option as Babel's working directory to keep config resolution consistent. *** ### cwd ```js cwd: string; // Default: process.cwd() ``` Working directory for the plugin; defaults to `process.cwd()`. Dependency paths and Babel config resolution use this value. *** ### exclude ```js exclude: string[] // Default: [] ``` Array of paths or glob patterns to exclude from compilation. Paths in `exclude` take precedence over `include`. When `include` is omitted, the plugin automatically excludes common build and dependency folders (for example `node_modules`, `.next`, `dist`, `build`). *** ### importSources ```js importSources: Array; // Default: ['@stylexjs/stylex', 'stylex'] ``` Possible strings where you can import stylex from. Files that do not match the import sources may be skipped from being processed to speed up compilation. When omitted, the plugin infers `importSources` from your `@stylexjs/babel-plugin` options (if present), while still keeping the default StyleX sources. Precedence order is: 1. explicit PostCSS `importSources` 2. inferred from Babel config 3. built-in defaults *** ### include ```js include: string[] // Default: auto-discovered ``` Array of paths or glob patterns to compile. When omitted, the plugin auto-discovers source files under `cwd` and direct dependencies that use StyleX (checking `dependencies`, `peerDependencies`, and `optionalDependencies`). *** ### useCSSLayers ```js useCSSLayers: boolean | { before?: string[], after?: string[], prefix?: string, }; // Default: false ``` Enabling this option switches StyleX from using `:not(#\#)` to using `@layer` for handling CSS specificity. When set to `true`, each priority level is wrapped in its own `@layer` and a layer-ordering header is emitted. This gives StyleX CSS lower specificity than any unlayered CSS in the app (per the CSS cascade: layered styles \< unlayered styles). When your project also uses other CSS layers (e.g. a design-system layer, or a utility framework like Tailwind), pass an object to position StyleX's layers relative to yours in the [`@layer` ordering declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) and to namespace them to avoid collisions: * **`before`** — extra layer names placed *before* StyleX's priority layers in the ordering header. Use this to ensure base layers are ordered before StyleX. * **`after`** — extra layer names placed *after* StyleX's priority layers. Useful for giving a utilities layer higher precedence than StyleX. * **`prefix`** — string prepended to every StyleX layer name (joined with `.`). Use this to namespace StyleX layers and avoid collisions with other frameworks (e.g. `prefix: 'stylex'` → `stylex.priority1`). ```js // postcss.config.js module.exports = { plugins: { '@stylexjs/postcss-plugin': { useCSSLayers: { before: ['reset', 'base'], after: ['utilities'], prefix: 'stylex', }, // ...other options }, }, }; ``` The generated CSS begins with a layer-ordering header followed by wrapped rules: ```css @layer reset, base, stylex.priority1, stylex.priority2, utilities; @layer stylex.priority1 { /* low-priority rules */ } @layer stylex.priority2 { /* higher-priority rules */ } ``` *** ## Debugging auto-discovery Set `STYLEX_POSTCSS_DEBUG=1` to log the resolved `importSources`, final `include`/`exclude` globs, and discovered dependency directories. # @stylexjs/unplugin (/docs/api/configuration/unplugin) Universal bundler plugin for StyleX built on top of [`unplugin`](https://github.com/unjs/unplugin). It compiles StyleX modules, aggregates the generated CSS, and appends the result to an emitted CSS asset (or creates `stylex.css` as a fallback). Adapters are available for Vite/Rollup, Webpack/Rspack, esbuild, and Bun. ## Install ## Usage by bundler ### Vite ```ts title="vite.config.ts" import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import stylex from '@stylexjs/unplugin'; export default defineConfig({ plugins: [stylex.vite({ useCSSLayers: true }), react()], }); ``` * Keep `stylex.vite()` before framework plugins to preserve Fast Refresh. * Provide a CSS entry so Vite emits an asset for the plugin to append to. * Dev virtual modules: * `/virtual:stylex.css` — aggregated CSS endpoint. * `virtual:stylex:runtime` — JS runtime for hot CSS reloads. * `virtual:stylex:css-only` — JS shim that only triggers CSS reloads. Add `` and either a ` {#if import.meta.env.DEV} {/if} {@render children()} ``` # Vite + React (/docs/learn/installation/vite/vite-react) Both [`examples/example-vite`](https://github.com/facebook/stylex/tree/main/examples/example-vite) (JavaScript) and [`examples/example-vite-react`](https://github.com/facebook/stylex/tree/main/examples/example-vite-react) (TypeScript) demonstrate wiring StyleX into a Vite + React app via `@stylexjs/unplugin`. StyleX styles are compiled at build time and appended to the CSS emitted by Vite. ## Vite configuration ```ts title="vite.config.ts" import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import stylex from '@stylexjs/unplugin'; export default defineConfig({ plugins: [ stylex.vite({ useCSSLayers: true, // ... other StyleX configuration options }), react(), ], }); ``` Keep the StyleX plugin before `@vitejs/plugin-react` to preserve Fast Refresh. ## CSS entrypoint Import a CSS file from the app root (for example `src/index.css`) so Vite emits an asset. During `vite build` the StyleX plugin appends its aggregated output to that CSS file, avoiding extra requests in production. ESLint in the TypeScript example already includes the StyleX lint rules and `tsconfigRootDir` configuration. # Vite + React Server Components (/docs/learn/installation/vite/vite-rsc) [`examples/example-vite-rsc`](https://github.com/facebook/stylex/tree/main/examples/example-vite-rsc) layers `@vitejs/plugin-rsc` on top of React while `@stylexjs/unplugin` compiles StyleX for each environment (RSC, SSR, client). Every build output receives a single CSS asset with aggregated StyleX styles. ## Vite configuration ```ts title="vite.config.ts" import rsc from '@vitejs/plugin-rsc'; import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; import stylex from '@stylexjs/unplugin'; export default defineConfig({ plugins: [ rsc(), react(), stylex.vite({ // ... StyleX configuration options }), ], environments: { rsc: { build: { rollupOptions: { input: { index: './src/framework/entry.rsc.tsx' } }, }, }, ssr: { build: { rollupOptions: { input: { index: './src/framework/entry.ssr.tsx' } }, }, }, client: { build: { rollupOptions: { input: { index: './src/framework/entry.browser.tsx' }, }, }, }, }, }); ``` * `stylex.vite()` runs for every environment, so each bundle gets the correct CSS appended. * Add options such as `useCSSLayers` if desired. ## CSS entrypoint Ensure that there is one CSS file that is imported from your root layout component or JS entry point. ## CSS Link and Hot Reloading during development Ensure that the virtual CSS file and script for hot reloading styles are part of your bundle to enable hot reloading during development. The easiest way to do this is by using an encapsulated helper that can also be used to insert a link tag for your production CSS. ```tsx // src/DevStyleXInject.tsx 'use client'; import { useEffect } from 'react'; function DevStyleXInjectImpl() { useEffect(() => { if (import.meta.env.DEV) { import('virtual:stylex:runtime'); } }, []); return ; } export function DevStyleXInject({ cssHref }: { cssHref: string }) { return import.meta.env.DEV ? ( ) : ( cssHref && ); } ``` Render `` in your HTML shell component. ```tsx // src/root.tsx (excerpt) import { DevStyleXInject } from './DevStyleXInject'; export function Root(props: { url: URL }) { return ( ); } ``` # Waku (/docs/learn/installation/vite/waku) [`examples/example-waku`](https://github.com/facebook/stylex/tree/main/examples/example-waku) integrates StyleX into a Waku app through the Vite layer using `@stylexjs/unplugin`. The plugin compiles StyleX modules, aggregates the generated CSS, and appends it to the emitted CSS assets so the browser only downloads one stylesheet. ## Waku/Vite configuration ```ts title="waku.config.ts" import react from '@vitejs/plugin-react'; import stylex from '@stylexjs/unplugin'; import { defineConfig } from 'waku/config'; export default defineConfig({ vite: { plugins: [ stylex.vite({ useCSSLayers: true, devMode: 'css-only', devPersistToDisk: true, runtimeInjection: false, }), react({ babel: { plugins: ['babel-plugin-react-compiler'] } }), ], }, }); ``` * `devMode: 'css-only'` serves `/virtual:stylex.css` in dev without injecting the JS runtime. * `devPersistToDisk` lets multiple Waku environments share collected rules while developing. ## CSS entrypoint Ensure that there is one CSS file that is imported from your root layout component or JS entry point. ## CSS Link and Hot Reloading during development Ensure that the virtual CSS file and script for hot reloading styles are part of your bundle to enable hot reloading during development. The easiest way to do this is by using an encapsulated helper that can also be used to insert a link tag for your production CSS. ```tsx // src/DevStyleXInject.tsx 'use client'; import { useEffect } from 'react'; function DevStyleXInjectImpl() { useEffect(() => { if (import.meta.env.DEV) { import('virtual:stylex:runtime'); } }, []); return ; } export function DevStyleXInject({ cssHref }: { cssHref: string }) { return import.meta.env.DEV ? ( ) : ( cssHref && ); } ``` Render `` in your HTML shell component. ```tsx // src/pages/_layout.tsx (excerpt) import { DevStyleXInject } from '../components/DevStyleXInject'; export default function RootLayout({ children }: { children: ReactNode }) { return ( <>
{children}
); } ``` # Webpack (/docs/learn/installation/webpack) [`examples/example-webpack`](https://github.com/facebook/stylex/tree/main/examples/example-webpack) demonstrates using `@stylexjs/unplugin` with Webpack and `MiniCssExtractPlugin`. StyleX is compiled at build time and the aggregated CSS is appended to the extracted stylesheet. ## Install ## Webpack configuration ```js title="webpack.config.js" const fs = require('node:fs'); const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const stylex = require('@stylexjs/unplugin').default; const templatePath = path.resolve(__dirname, 'index.html'); module.exports = { // ... devServer: { watchFiles: [templatePath, path.resolve(__dirname, 'src/**/*')], }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: [{ loader: 'babel-loader' }], }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }, ], }, plugins: [ new HtmlWebpackPlugin({ inject: true, templateContent: () => fs.readFileSync(templatePath, 'utf-8'), }), stylex.webpack({ useCSSLayers: true, // ... other StyleX configuration }), new MiniCssExtractPlugin(), ], }; ``` ## CSS entrypoint Ensure that there is one CSS file that is imported from your root layout component or JS entry point. # Context-driven styles (/docs/learn/recipes/context-driven-styles) StyleX lets you apply styles conditionally. Any condition can be used to do so, `Props`, `State` or `Context`. The React Context API (and other similar APIs) can be used to avoid prop-drilling and provide conditions that child components can read and apply styles conditionally. Context can help reduce prop-drilling by sharing state across components. This can often be an alternative to using descendent selectors, as the same results can be achieved *without* "styling at a distance". For example, you can manage the open or closed state of a sidebar using React Context and conditionally apply styles: ## Defining context and styles This file sets up the `SidebarContext` and defines the styles for the sidebar in one place. The context provides a way to share the open/closed state, and the styles determine the appearance of the sidebar based on that state. ```tsx import { createContext } from 'react'; export default createContext(false); ``` ## Building the sidebar component The `Sidebar` component uses the `SidebarContext` to determine its open or closed state and conditionally applies the appropriate styles. ```tsx import { useContext } from 'react'; import * as stylex from '@stylexjs/stylex'; import { SidebarContext } from './SidebarContext'; export default function Sidebar({ children }) { const isOpen = useContext(SidebarContext); return (
{children}
); } const styles = stylex.create({ base: {...}, open: { width: 250, }, closed: { width: 50, }, }); ``` ## Using the sidebar in a parent component The `App` component manages the sidebar's open/closed state and provides it to child components through `SidebarContext.Provider`. A button toggles the sidebar state dynamically. ```tsx import { useState } from 'react'; import SidebarContext from './SidebarContext'; import Sidebar from './Sidebar'; export default function App() { const [isSidebarOpen, setIsSidebarOpen] = useState(false); return (

Sidebar Content

); } ``` # Variables for descendant styles (/docs/learn/recipes/descendant-styles) It is not uncommon to define styles on an element that are dependent on a parent element's state, such as applying some styles conditionally when the parent element is hovered. StyleX doesn't allow arbitrary selectors or "styling at a distance". However, variables can be used to achieve the same results in a safe and composable way. ## Example: Sidebar Consider the case where the content within a sidebar needs to have contextual styles applied when the sidebar as whole is hovered. Using CSS variables, you can style descendants based on a parent's state, such as `:hover`. ### Step 1 Define one or more variables using `defineVars`: ```tsx title="variables.stylex.ts" import * as stylex from '@stylexjs/stylex'; export const vars = stylex.defineVars({ childColor: 'black', }); ``` ### Step 2 Define contextual styles setting the value for the variable in the ancestor component. ```tsx title="Component.tsx" import * as stylex from '@stylexjs/stylex'; import { vars } from './variables.stylex'; const styles = stylex.create({ parent: { [vars.childColor]: { default: 'black', ':hover': 'blue', }, }, }); function ParentWithHover({ children }) { return
{children}
; } ``` ### Step 3 Use the variable to style the child component ```tsx title="Child.tsx" import * as stylex from '@stylexjs/stylex'; import { vars } from './variables.stylex'; const styles = stylex.create({ child: { color: vars.childColor, }, }); function Child() { return ( A Row ); } ``` This pattern makes it explicit what styles are being defined on an ancestor element, while leaving the child element in control to use those styles explicitly and to override it as needed. # Light and Dark Themes (/docs/learn/recipes/light-dark-themes) It is a common pattern to define separate `light`, `dark` and system themes to provide the ability to switch between different color schemes. This would typically be done by defining three separate `Theme`s: ```tsx const lightVars = { primaryColor: 'black', ... }; export const light = stylex.createTheme(vars, lightVars); const darkVars = { primaryColor: 'white', ... }; export const dark = stylex.createTheme(vars, darkVars); const systemVars = { primaryColor: { default: 'black', '@media (prefers-color-scheme: dark)': 'white', }, ... }; export const system = stylex.createTheme(vars, systemVars); ``` This pattern is well supported and will work in all browsers that support CSS variables. ## Using the `light-dark()` CSS function In modern browsers, we suggest using the [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark) CSS function instead which will simplify the code and remove the need to define themes. ```tsx export const vars = stylex.defineVars({ primaryColor: 'light-dark(black, white)', ... }); ``` You can now control the color scheme applied by using the `color-scheme` CSS property. ```tsx const styles = stylex.create({ light: { colorScheme: 'light', }, dark: { colorScheme: 'dark', }, system: { colorScheme: 'light dark', }, });
...
; ``` You *can* still define custom themes for other use-cases and use `light-dark()` within them. ### Limitations 1. The `light-dark()` CSS function can only be used for color values. 2. The `light-dark()` function is not supported in older browsers. # Merge Themes (/docs/learn/recipes/merge-themes) `Theme`s for the *same* `VarGroup` are mutually exclusive and do not merge. Any variable in a `VarGroup` that is not explicitly overridden in a `Theme` for that `VarGroup` is set to its default value. However, you can reuse common constants when defining multiple themes for a particular `VarGroup` and avoid excessive repetition. ## Example ```tsx import * as stylex from '@stylexjs/stylex'; import { vars } from './variables.stylex'; const themeBlueVars = { backgroundColor: 'blue', }; const themeBlue = stylex.createTheme(vars, themeBlueVars); const themeBigVars = { size: '128px', }; const themeBig = stylex.createTheme(vars, themeBigVars); const themeBigBlueVars = { ...themeBlueVars, ...themeBigVars }; const themeBigBlue = stylex.createTheme(vars, themeBigBlueVars); ``` The StyleX compiler is able to resolve local object constants and merge them. This is useful to be able to define a `Theme` that merges the values of two or more other `Theme`s without repetition. # Reset Theme (/docs/learn/recipes/reset-themes) The `defineVars` function is used to create a set of CSS variables, called `VarGroup`s. Further, the `createTheme` function can be used to create `Theme`s, that override the values of the variables defined within `VarGroup`s. Many `VarGroup`s can be defined which can then be independently overridden with `Theme`s. However, `Theme`s for the *same* `VarGroup` are mutually exclusive and do not merge. Any variable in a `VarGroup` that is not explicitly overridden in a `Theme` for that `VarGroup` is set to its default value. This characteristic of `Theme`s can be used to define a "empty" theme that resets all variables to their default values. ## Example ```tsx import * as stylex from '@stylexjs/stylex'; import { vars } from './variables.stylex'; export const resetVars = stylex.createTheme(vars, {}); ``` # Theme overrides (/docs/learn/recipes/shareable-tokens) StyleX provides several patterns for sharing design tokens across your codebase and creating themes that build on each other. ## Shareable tokens with `stylex.env` Use `stylex.env` to define design tokens once in your babel config and use them across your entire codebase. This avoids repeating token values in every file. ```js title="babel.config.js" module.exports = { plugins: [ ['@stylexjs/babel-plugin', { env: { tokens: { colors: { primary: 'blue', secondary: 'green', background: 'white', text: 'black', border: 'gray', }, spacing: { small: '4px', medium: '8px', large: '16px', }, radii: { small: '4px', medium: '8px', }, }, }, }], ], }; ``` These tokens can then be used in `defineVars`, `createTheme`, or `create` calls: ```tsx title="tokens.stylex.js" import * as stylex from '@stylexjs/stylex'; export const colors = stylex.defineVars(stylex.env.tokens.colors); export const spacing = stylex.defineVars(stylex.env.tokens.spacing); export const radii = stylex.defineVars(stylex.env.tokens.radii); ``` ## Partial overrides `createTheme` accepts partial overrides. Tokens you don't specify are not included in the generated CSS, so they inherit from the original `VarGroup`. ```tsx title="themes.js" import * as stylex from '@stylexjs/stylex'; import { colors } from './tokens.stylex'; // Dark theme — only override what changes export const darkTheme = stylex.createTheme(colors, { background: '#1a1a1a', text: 'white', }); // High contrast — only override what changes export const highContrastTheme = stylex.createTheme(colors, { primary: 'yellow', border: 'white', }); ``` Apply themes by nesting. Child themes inherit unspecified tokens from their parent: ```tsx title="App.js" import * as stylex from '@stylexjs/stylex'; import { darkTheme, highContrastTheme } from './themes'; // High contrast inherits background and text from dark
{children}
``` ## Composing theme overrides with `stylex.env` functions For themes that share a common base, define a shareable `env` function to merge overrides. This avoids repeating the full token set in every theme. ```js title="babel.config.js" module.exports = { plugins: [ ['@stylexjs/babel-plugin', { env: { tokens: { colors: { primary: 'blue', secondary: 'green', background: 'white', text: 'black', border: 'gray', }, }, override: (base, overrides) => ({ ...base, ...overrides }), }, }], ], }; ``` ```tsx title="themes.js" import * as stylex from '@stylexjs/stylex'; import { colors } from './tokens.stylex'; // Each theme specifies only its changes export const darkTheme = stylex.createTheme(colors, stylex.env.override(stylex.env.tokens.colors, { background: '#1a1a1a', text: 'white', }) ); export const draculaTheme = stylex.createTheme(colors, stylex.env.override(stylex.env.tokens.colors, { primary: 'purple', secondary: 'pink', background: '#282a36', text: '#f8f8f2', }) ); ``` The `override` function merges the base tokens with your changes through the function set in your config. # Variants (/docs/learn/recipes/variants) The "variants" pattern allows you to conditionally apply one of several predefined styles based on a value. This is especially useful for theming or dynamic component behavior. It is common to have different styles for different "variants" of a component. Some other styling solutions provide an explicit API for defining variants. In StyleX, you can define variants with a simple pattern instead. ## Example: Button Variants Here’s how you can create a button component with different visual styles based on `variant` props: ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ base: { appearance: 'none', borderWidth: 0, }, }); const colorVariants = stylex.create({ primary: { backgroundColor: { default: 'blue', ':hover': 'darkblue', }, color: 'white', }, secondary: { backgroundColor: { default: 'gray', ':hover': 'darkgray', }, color: 'white', }, }); const sizeVariants = stylex.create({ small: { fontSize: '1rem', paddingBlock: 4, paddingInline: 8 }, medium: { fontSize: '1.2rem', paddingBlock: 8, paddingInline: 16 }, }); type Props = { color: keyof typeof colorVariants, size: keyof typeof sizeVariants, ... }; function Button({ color = 'primary', size = 'small', ...props }: Props) { return ( ``` ## Compound Variants Sometimes variants are dependent on a combination of variants props. In most cases, it's simpler to leverage StyleX’s deterministic style merging to simplify this behaviour. ### Example: A `disabled` prop ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ base: {...}, disabled: { backgroundColor: 'grey', color: 'rgb(204, 204, 204)', cursor: 'not-allowed', }, }); const colorVariants = stylex.create({ primary: { backgroundColor: { default: 'blue', ':hover': 'darkblue', }, color: 'white', }, secondary: { backgroundColor: { default: 'gray', ':hover': 'darkgray', }, color: 'white', }, }); const sizeVariants = stylex.create({...}); type Props = { color?: keyof typeof colorVariants, size?: keyof typeof sizeVariants, disabled?: boolean, ... }; function Button({ color = 'primary', size = 'small', disabled = false, ...props, }: Props) { return ( ``` There may be other scenarios where you need to be more explicit about the styles that should be applied under various condition. You can do this by declaring multiple style definitions for a particular variant. ### Example: Two definitions for `color` variant styles ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ base: {...}, }); const colorVariantsEnabled = stylex.create({ primary: { backgroundColor: { default: 'blue', ':hover': 'darkblue', }, color: 'white', }, secondary: { backgroundColor: { default: 'gray', ':hover': 'darkgray', }, color: 'white', }, }); const colorVariantsDisabled = stylex.create({ primary: { backgroundColor: 'blue', color: 'white', }, secondary: { backgroundColor: 'gray', color: 'white', }, }); const sizeVariants = stylex.create({...}); type Props = { color?: keyof (typeof colorVariantsEnabled | typeof colorVariantsDisabled), size?: keyof typeof sizeVariants, disabled?: boolean, ... }; function Button({ color = 'primary', size = 'small', disabled = false, ...props, }: Props) { const colorVariants = disabled ? colorVariantsDisabled : colorVariantsEnabled; return ( ``` # Static types (/docs/learn/static-types) ## Types for style props StyleX comes with full support for Static Types. The most common utility type is `StyleXStyles` which is used to accept any arbitrary StyleX styles. ```tsx import type { StyleXStyles } from '@stylexjs/stylex'; import * as stylex from '@stylexjs/stylex'; type Props = { ... style?: StyleXStyles, }; function MyComponent({style, ...otherProps}: Props) { return (
{/* ... */}
); } ```
```tsx import type { StyleXStyles } from '@stylexjs/stylex'; import * as stylex from '@stylexjs/stylex'; type Props = $ReadOnly<{ ... style?: StyleXStyles<>, }>; function MyComponent({style, ...otherProps}: Props) { return (
{/* ... */}
); } ```
`StaticStyles` can be used instead of `StyleXStyles` to accept arbitrary static styles, but disallow dynamic styles. ## Constraining accepted styles Type arguments can be used with `StyleXStyles<{...}>` to limit the styles that are accepted. ### Accepting from a set of style properties To limit the accepted style properties to a given set, an object type with the allowed properties can be used: ```tsx import type { StyleXStyles } from '@stylexjs/stylex'; type Props = { // ... style?: StyleXStyles<{ color?: string; backgroundColor?: string; borderColor?: string; borderTopColor?: string; borderEndColor?: string; borderBottomColor?: string; borderStartColor?: string; }>; }; ``` ```tsx import type { StyleXStyles } from '@stylexjs/stylex'; type Props = $ReadOnly<{ // ... style?: StyleXStyles<{ color?: string; backgroundColor?: string; borderColor?: string; borderTopColor?: string; borderEndColor?: string; borderBottomColor?: string; borderStartColor?: string; }>; }>; ``` The `style` prop will now accept only the properties defined but disallow anything else. It is a good practice to make the keys of the style types optional and have baseline styles in the component itself. TypeScript object types don’t error when given objects with extra properties. We’ve taken steps to mitigate this issue, but there may be edge-cases where you’ll be able to pass in extra, disallowed styles without a type error. ### Limiting the possible values for styles In addition to the accepted style properties, the values for those properties can be constrained too. ```tsx import type { StyleXStyles } from '@stylexjs/stylex'; type Props = { ... // Only accept styles for marginTop and nothing else. // The value for marginTop can only be 0, 4, 8 or 16. style?: StyleXStyles<{ marginTop: 0 | 4 | 8 | 16 }>, }; ``` ```tsx import type { StyleXStyles } from '@stylexjs/stylex'; type Props = $ReadOnly<{ ... // Only accept styles for marginTop and nothing else. // The value for marginTop can only be 0, 4, 8 or 16. style?: StyleXStyles<{ marginTop: 0 | 4 | 8 | 16 }>, }>; ``` Now, this component only accepts styles that have a `marginTop` property and no other properties. The value for `marginTop` can only be one of `0`, `4`, `8`, or `16`. ## Disallowing properties It is sometimes more convenient to define a blocklist instead of an allowlist. ```tsx import type { StyleXStylesWithout } from '@stylexjs/stylex'; import * as stylex from '@stylexjs/stylex'; type NoLayout = StyleXStylesWithout<{ position: unknown, display: unknown, top: unknown, start: unknown, end: unknown, bottom: unknown, border: unknown, borderWidth: unknown, borderBottomWidth: unknown, borderEndWidth: unknown, borderStartWidth: unknown, borderTopWidth: unknown, margin: unknown, marginBottom: unknown, marginEnd: unknown, marginStart: unknown, marginTop: unknown, padding: unknown, paddingBottom: unknown, paddingEnd: unknown, paddingStart: unknown, paddingTop: unknown, width: unknown, height: unknown, flexBasis: unknown, overflow: unknown, overflowX: unknown, overflowY: unknown, }>; type Props = { // ... style?: NoLayout, }; function MyComponent({style, ...otherProps}: Props) { return (
{/* ... */}
); } ```
```tsx import type { StyleXStylesWithout } from '@stylexjs/stylex'; import * as stylex from '@stylexjs/stylex'; type NoLayout = StyleXStylesWithout<{ position: mixed, display: mixed, top: mixed, start: mixed, end: mixed, bottom: mixed, border: mixed, borderWidth: mixed, borderBottomWidth: mixed, borderEndWidth: mixed, borderStartWidth: mixed, borderTopWidth: mixed, margin: mixed, marginBottom: mixed, marginEnd: mixed, marginStart: mixed, marginTop: mixed, padding: mixed, paddingBottom: mixed, paddingEnd: mixed, paddingStart: mixed, paddingTop: mixed, width: mixed, height: mixed, flexBasis: mixed, overflow: mixed, overflowX: mixed, overflowY: mixed, }>; type Props = $ReadOnly<{ // ... style?: NoLayout, }>; function MyComponent({style, ...otherProps }: Props) { return (
{/* ... */}
); } ```
Here the listed properties in the object type will be disallowed, but all other styles will still be accepted. # Defining styles (/docs/learn/styling-ui/defining-styles) StyleX uses an expressive JavaScript API that is similar to working with inline styles in React DOM, or styles in React Native. ## Constraints Since StyleX depends on ahead-of-time compilation, it is important for all your styles to be statically analyzable. This means that every "Raw Style Object" must only contain: * Plain Object Literals * String Literals * Number Literals * Array Literals * `null` or `undefined` * Constants, simple expressions, and built-in methods (e.g., `.toString()`) that resolve to one of the above. * And arrow functions for dynamic styles The following are **not** allowed: * Function calls (except StyleX functions) * Values imported from other modules (except for CSS Variables created using StyleX from a `.stylex.js` file.) * Object spreads (e.g., `{...style}`) ## Creating styles Styles must be created with the `create` function. You can define one or more "namespaces", or objects of styles. In the example below, there are 2 "namespaces" - one called `base` and the other `highlighted`. The names are arbitrary and represent the constant used to capture the result of the `create()` function call. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ base: { fontSize: 16, lineHeight: 1.5, color: 'rgb(60,60,60)', }, highlighted: { color: 'rebeccapurple', }, }); ``` ## Pseudo-classes Pseudo-classes represent different states of an element. In StyleX, declarations for pseudo-classes are nested within properties. For example, let's say we have a button that currently has a `lightblue` background. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ button: { backgroundColor: 'lightblue', }, }); ``` If we want to add pseudo-classes to change the background color for different states, we replace the `lightblue` string literal with an object of pseudo-states. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ button: { backgroundColor: { default: 'lightblue', ':hover': 'blue', ':active': 'darkblue', }, }, }); ``` ## Pseudo-elements We recommend avoiding pseudo-elements when possible and relying on actual HTML elements instead, i.e., replace `::before` and `::after` with elements like `div` or `span`. This helps reduce the size of your CSS bundle. Pseudo-elements are a way of targeting shadow DOM elements contained within the native HTML elements provided by user agents. For example, `::placeholder` references the element that contains placeholder text within an `input` or `textarea` element. To target pseudo-elements in StyleX, they must be defined as a top-level key within a namespace. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ input: { // pseudo-element '::placeholder': { color: '#999', }, color: { default: '#333', // pseudo-class ':invalid': 'red', }, }, }); ``` ## Media queries (and other `@` rules) Media Queries can, similarly, be as "conditions" within style values. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ base: { width: { default: 800, '@media (max-width: 800px)': '100%', '@media (min-width: 1540px)': 1366, }, }, }); ``` ## Combining conditions Your Style Values can be nested more than one level deep when you need to combine Media Queries and Pseudo Selectors ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ button: { color: { default: 'var(--blue-link)', ':hover': { default: null, '@media (hover: hover)': 'scale(1.1)', }, ':active': 'scale(0.9)', }, }, }); ``` The `default` case is required when authoring contextual styles. If you don't want any style to be applied in the default case, you can use `null` as the value. Using `null` for a non-`default` condition has no effect and should be considered invalid. ## Fallback styles There are situations in StyleX where, when you need fallback styles for browsers that don't support a certain new style property. In CSS you may do something like this: ```css .header { position: fixed; position: -webkit-sticky; position: sticky; } ``` This kind of syntax is not possible when using JavaScript objects. So in StyleX you can use the `firstThatWorks` function to achieve the same thing. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ header: { position: stylex.firstThatWorks('sticky', '-webkit-sticky', 'fixed'), }, }); ``` ## Keyframe animations You can use the `keyframes` function to define keyframe animations. ```tsx import * as stylex from '@stylexjs/stylex'; const fadeIn = stylex.keyframes({ from: { opacity: 0 }, to: { opacity: 1 }, }); const styles = stylex.create({ base: { animationName: fadeIn, animationDuration: '1s', }, }); ``` ## Dynamic styles Dynamic styles are an advanced feature and should be used sparingly. For the majority of use-cases, [conditional styles](/docs/learn/styling-ui/using-styles#conditional-styles) should be sufficient. StyleX generates all styles at compile-time which means you need to *know* all those styles ahead of time as well. But sometimes you just don't know what you will need until runtime. For such situations, you can define your styles as a function instead of an object and pass in the dynamic components of the needed styles as parameters. **NOTE**: The function body *must* be an object literal. You cannot use a function body with multiple statements. ```tsx import { useState } from 'react'; import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ // Function arguments must be simple identifiers // -- No destructuring or default values bar: (height) => ({ height, // The function body must be an object literal // -- { return {} } is not allowed }), }); function MyComponent() { // The value of `height` cannot be known at compile time. const [height, setHeight] = useState(10); return
; } ``` Behind the scenes, StyleX will generate static styles that depend on a CSS variable and set the value of that variable at runtime. This means, that any part of your styles can be dynamic, including within Media Queries and pseudo-classes. # Using styles (/docs/learn/styling-ui/using-styles) Once styles have been defined, they must be converted to `className` and `styles` props that can be spread on HTML elements using the [`props`](/docs/api/javascript/props) function. ```tsx
``` While this is the simplest case, it is trivial to merge multiple style objects, use them conditionally, or even compose styles across module boundaries. ## Merging styles The `props` function can take a list of styles and merge them in a deterministic way. When two styles set the same property, the last style applied always wins. The order in which the styles are defined does not affect the resulting styles, only the order in which they are applied to the HTML element. Shorthand and longhand properties are different keys (`margin` vs `marginTop`). By default (`styleResolution: 'property-specificity'`), the longhand wins even if the shorthand is applied later. See [`styleResolution`](/docs/api/configuration/babel-plugin/#styleresolution). Using `props` is only required when styles are set on React DOM host elements like `
`. Consider styles that are defined as follows: ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ base: { fontSize: 16, lineHeight: 1.5, color: 'grey', }, highlighted: { color: 'rebeccapurple', }, });
; ``` The resulting HTML element will have purple text, because that style was applied last. If instead the order of the styles were reversed, the text would be gray. ```tsx
``` A simple way to think about the `props` function is that it merges many objects and the later objects have precedence over previous objects. Each individual style object can be passed to `props` as a separate argument, or passed in as an array of styles. ```tsx
``` ## Conditional styles Styles can be applied conditionally at runtime using common JavaScript patterns such as ternary expressions and the `&&` operator. `props` ignores falsy values such as `null`, `undefined`, or `false`. ```tsx
``` ## Inline atomic styles For small one-off styles, `@stylexjs/atoms` can be used directly inside `stylex.props` without creating a separate `stylex.create` namespace. ```tsx import * as stylex from '@stylexjs/stylex'; import x from '@stylexjs/atoms';
; ``` Atoms are compiled away by the StyleX Babel plugin and produce the same output as equivalent `stylex.create` styles. Use [`@stylexjs/atoms`](/docs/api/javascript/atoms) for inline styles that are easier to read at the callsite; use `stylex.create` when a set of styles is reused or benefits from a descriptive local name. ## Style variants A common styling pattern called "variants" lets you apply styles based on the value of a specific prop, e.g., `variant`. StyleX supports this pattern without an additional API. Instead, an object property lookup can be used to achieve the same result. First, each variant can be defined with the appropriate variant name for the style object. ```tsx import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ violet: { backgroundColor: { default: 'blueviolet', ':hover': 'darkviolet', }, color: 'white', }, gray: { backgroundColor: { default: 'gainsboro', ':hover': 'lightgray', }, }, // ... more variants here ... }); ``` The appropriate styles can then be applied by using the `variant` prop as a key on the `styles` object. ```tsx function Button({ variant, ...props }) { return