stylex-webpack
v0.3.2
Published
The another Webpack Plugin for Facebook's StyleX
Downloads
10,381
Maintainers
Readme
stylex-webpack
First introduced by Frank Yan at React Conf 2020, StyleX framework agnostic CSS-in-JS system with near-zero runtime, ahead-of-time compiler, atomic CSS extraction that powers Facebook and Instagram.
Motivation
stylex offers a CSS-in-JS compiler, allowing you to write CSS in your JavaScript/JSX/TSX. However, unlike other CSS-in-JS solutions that gather and process styles within the browser, stylex will read your source code, collect your style and transform your JS/JSX/TSX, stripping runtime calls as much as possible (making the value of className
a static string literal), and output CSS elsewhere.
StyleX does provide a webpack plugin. Under the hood, it will traverse through the source code, collect styles, and emit a new CSS asset during the webpack compilation. However, it does come with some limitations:
- StyleX's official Next.js setup requires a
.babelrc
file, which disables Next.js' built-in SWC compiler. - StyleX's official Next.js plugin requires a CSS asset to pre-exist so that it can append the extracted CSS to it.
I start this project as a Proof of Concept, to see if it is possible to make a webpack plugin for ststylex that doesn't disable Next.js' SWC compiler. I have already made a similar webpack plugin for style9, which is also an AoT atomic CSS-in-JS system that is inspired by StyleX.
Unlike stylex's official webpack plugin, stylex-webpack
requires you have setup css-loader
and MiniCssExtractPlugin
in your webpack configuration, just like your normal CSS based webpack project. stylex-webpack
's built-in loader will generate a virtual CSS import containing a dummy CSS rule. This allows the MiniCssExtractPlugin
to collect those virtual CSS imports and emit a CSS asset, which stylex-webpack
will later inject the actual extracted CSS into at the processAssets
stage.
Installation
# npm
npm i stylex-webpack
# Yarn
yarn add stylex-webpack
# pnpm
pnpm add stylex-webpack
Usage
Webpack
// webpack.config.js
const { StyleXPlugin } = require('stylex-webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
// Just like your normal CSS setup, a css-loader and MiniCssExtractPlugin.loader
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new StyleXPlugin({
// stylex-webpack options goes here, see the following section for more details
}),
new MiniCssExtractPlugin(),
new CssMinimizerPlugin()
// You can also use `LightningCssMinifyPlugin` from `lightningcss-loader`
// to replace CssMinimizerPlugin for faster CSS minification
// https://github.com/fz6m/lightningcss-loader
]
};
Next.js
// next.config.js
const { withStyleX } = require('stylex-webpack/next');
module.exports = withStyleX({
// stylex-webpack options goes here, see the following section for more details
})({
// Your Next.js config goes here.
reactStrictMode: true
});
Options
webpack
new StyleXPlugin({
// stylex-webpack options
/**
* stylex options passed to stylex babel plugin
*
* @see https://stylexjs.com/docs/api/configuration/babel-plugin/
*/
stylexOption: {
dev: process.env.NODE_ENV === 'development',
test: process.env.NODE_ENV === 'test'
// Check the stylex documentation for more options
},
/* Specify where stylex will be imported from
* This overrides `importSources` in the `stylexOption` above
*
* @default ['stylex', '@stylexjs/stylex']
*/
stylexImports: ['stylex', '@stylexjs/stylex'],
/**
* Whether to use CSS layers
*
* @default false
*/
useCSSLayers?: boolean,
/**
* Enable other CSS transformation
*
* Since stylex-webpack's loader only emit virtual CSS imports with dummy rules,
* while the actual CSS is injected by the plugin after all loaders, you can not
* use postcss-loader + PostCSS plugins. You can manually transform the CSS here.
*/
transformCss(css) {
const postcss = require('postcss');
const autoprefixer = require('autoprefixer');
/**
* It is a known issue that stylex won't sort your at-rules and media queries.
*
* https://github.com/facebook/stylex/issues/455
* https://github.com/facebook/stylex/issues/517
*
* For now, it is recommended to use postcss-sort-media-queries as a workaround.
*/
const sortMediaQueries = require('postcss-sort-media-queries');
return postcss([
autoprefixer({
// autoprefixer options
}),
sortMediaQueries({
sort: 'mobile-first'
})
]).process(css, { from: undefined }).css;
// If you don't use custom PostCSS plugins (like `postcss-sort-media-queries`
// mentioned above), only downleveling CSS syntax using autoprefixer, you can
// also use LightningCSS. It is a Rust-based CSS transformer and minifier that
// has built-in downleveling support.
const browserslist = require('browserslist');
const { transform, browserslistToTargets } = require('lightningcss');
return transform({
code: Buffer.from(css),
targets: browserslistToTargets(browserslist('>= 0.25%'))
}).code;
// If you don't need to transform CSS at all, you can just return the input as-is as well.
return css;
}
});
Next.js
withStyleX({
// The same options as the webpack plugin, but with a few differences
stylexOption: {
/**
* You don't have to specify `dev` here. `stylex-webpack` will automatically read
* Next.js building mode and set `dev` accordingly.
*/
// dev: process.env.NODE_ENV === 'development',
},
/**
* You don't have to specify `transformCss` here. `stylex-webpack` will automatically
* read your PostCSS configuration and apply it here, just like how Next.js does.
*
* Under the hood, `withStyleX` uses Next.js built-in PostCSS config reader to
* maintain the consistency with Next.js' built-in PostCSS support.
*/
// transformCss(css) {}
})
It is recommended to use postcss-sort-media-queries
as a workaround for stylex's known issue with sorting at-rules and media queries. You can configure it in your PostCSS configuration file, and stylex-webpack
will automatically apply your PostCSS configuration to the extracted CSS just like Next.js' built-in PostCSS support.
// postcss.config.js
/** @type {Record<'plugins', import('postcss').AcceptedPlugin[]>} */
module.exports = {
plugins: [
[
require.resolve('postcss-sort-media-queries'),
{
sort: 'mobile-first' // default value
}
],
// Next.js will disable its built-in default PostCSS configuration you
// create `postcss.config.js`, which you can add it back:
/* --- Start of Next.js built-in default PostCSS configuration --- */
require.resolve('next/dist/compiled/postcss-flexbugs-fixes'),
[
require.resolve('next/dist/compiled/postcss-preset-env'),
{
browsers: ['defaults'],
autoprefixer: {
// Disable legacy flexbox support
flexbox: 'no-2009'
},
// Enable CSS features that have shipped to the
// web platform, i.e. in 2+ browsers unflagged.
stage: 3,
features: {
'custom-properties': false
}
}
]
/* --- End of Next.js built-in default PostCSS configuration --- */
]
};
Author
stylex-webpack © Sukka, Released under the MIT License. Authored and maintained by Sukka with help from contributors (list).
Personal Website · Blog · GitHub @SukkaW · Telegram Channel @SukkaChannel · Twitter @isukkaw · Mastodon @[email protected] · Keybase @sukka