Posts

StyleX

StyleX is a new styling system designed by Meta.
According to their blog, every major product in Meta including Facebook, WhatsApp, Instagram and Threads, are using it.

Installation

We will use NextJs to demonstrate the StyleX usage in this blog.
Let's first create a new next-app and remember to choose no if bun ask if you prefer to use tailwindcss.

bun create next-app
✔ What is your project named? … my-app
✔ Would you like to use TypeScript with this project? … No / Yes
✔ Would you like to use ESLint with this project? … No / Yes
✔ Would you like to use `src/` directory with this project? … No / Yes
✔ Would you like to use experimental `app/` directory with this project? … No / Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /path/to/my-app.

To use StyleX in next-app, install StyleX plugins.

bun add @stylexjs/stylex
bun add --development @stylexjs/babel-plugin @stylexjs/eslint-plugin @stylexjs/nextjs-plugin
bun install

Configuration

Because NextJs use its own Next.js Compiler, we need to setup the babel-plugin ourselves.

// .babelrc.js
const path = require('path')

module.exports = {
  presets: ['next/babel'],
  plugins: [
    [
      '@stylexjs/babel-plugin',
      {
        dev: process.env.NODE_ENV === 'development',
        runtimeInjection: false,
        genConditionalClasses: true,
        treeshakeCompensation: true,
        unstable_moduleResolution: {
          type: 'commonJS',
          rootDir: path.join(__dirname, '../..'),
        },
      },
    ],
  ],
}

By the way, we need to tell Next to use the StyleX plugins.

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {}

const stylexPlugin = require('@stylexjs/nextjs-plugin')

module.exports = stylexPlugin({
  rootDir: __dirname,
})(nextConfig)

// module.exports = nextConfig

Modify the Existing Files

Adding Next ThemeProvider for Dark Mode

bun install next-themes

Under app/components, add ThemeProvider.tsx.

'use client'
import React from 'react'
import { ThemeProvider } from 'next-themes'

interface Props {
  children: React.ReactNode
}

const Providers = (props: Props) => {
  return (
    <ThemeProvider attribute="class" defaultTheme="system">
      {props.children}
    </ThemeProvider>
  )
}

export default Providers

then import it inside app/layout.tsx. We'll demo the code in the next section.

Layout.tsx

Due to the compiler StyleX plugin using, we can't use next/font anymore. Otherwise, compiler will give us the error.

 ⨯ ./app/layout.tsx:2:1
Syntax error: "next/font" requires SWC although Babel is being used due to a custom babel config being present.
Read more: https://nextjs.org/docs/messages/babel-font-loader-conflict

We need to comment or remove the following line from the initial layout.tsx.

import type { Metadata } from 'next'
-import { Inter } from 'next/font/google'
 import './globals.css'
-
-const inter = Inter({ subsets: ['latin'] })
+import ThemeProvider from './components/ThemeProvider'

 export const metadata: Metadata = {
   title: 'Create Next App',
@@ -15,8 +13,10 @@ export default function RootLayout({
   children: React.ReactNode
 }) {
   return (
-    <html lang="en">
-      <body className={inter.className}>{children}</body>
+    <html lang="en" suppressHydrationWarning>
+      <body>
+        <ThemeProvider>{children}</ThemeProvider>
+      </body>
     </html>
   )
 }

StyleX

Create StyleX component under app directory.

// app/Stylex.tsx
import * as stylex from '@stylexjs/stylex'

const styles = stylex.create({
  main: {
    backgroundColor: {
      default: 'var(--color-primary)',
    },
    color: 'white',
  },
  button: {
    color: 'blue',
    textColor: 'white',
  },
})

export default function Stylex() {
  return (
    <main {...stylex.props(styles.main)}>
      <p>main</p>
      <button {...stylex.props(styles.button)}>button</button>
    </main>
  )
}

Next, replace global.css with minimum config.

.light {
  --color-primary: #f9f9f9;
}

.dark {
  --color-primary: #1a1a1a;
}

Last, replace app/page.tsx to demonstrate the StyleX usage.

'use client'
import React from 'react'
import Stylex from './Stylex'
import { useTheme } from 'next-themes'

export default function Home() {
  const { setTheme } = useTheme()

  return (
    <main>
      <header>
        <button onClick={() => setTheme('light')}>light</button>
        <button onClick={() => setTheme('dark')}>dark</button>
        <button onClick={() => setTheme('system')}>system</button>
      </header>
      <Stylex />
    </main>
  )
}

Outro

StyleX is a CSS-in-JS library that simplifies styling and enables more dynamic and programmatic management. It offers flexibility and control over styling elements. However, integrating StyleX in Next.js results in the compiler disabling SWC, despite SWC being 17x faster than Babel. For this reason, I believe I'll continue using Tailwind CSS for the time being. I hope that these custom Babel transformations can be transferred to SWC's Rust-based transforms in the near future.