Installation
Install
Section titled “Install”npm install csszyxpnpm add csszyxyarn add csszyxThe csszyx umbrella package includes the compiler, runtime, types, and
build plugin. The command-line tools (migrate, next, type generation)
ship separately as @csszyx/cli — run them with npx @csszyx/cli <command>
or install with pnpm add -D @csszyx/cli.
For portable sz autocomplete through compatible TypeScript editor hosts,
install the preview @csszyx/ts-plugin separately and follow the
TypeScript Autocomplete Plugin guide. It is an
authoring tool, not a build dependency.
Setup by Platform
Section titled “Setup by Platform”import { defineConfig } from 'vite';import csszyx from 'csszyx/vite';import tailwindcss from '@tailwindcss/vite';import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [ // Order matters: csszyx → tailwindcss → react ...csszyx(), tailwindcss(), react(), ],});import type { NextConfig } from 'next';
const nextConfig: NextConfig = { reactStrictMode: true, webpack: (config) => { const csszyxWebpack = require('@csszyx/unplugin/webpack').default; config.plugins.push(csszyxWebpack()); return config; },};
export default nextConfig;const csszyxWebpack = require('@csszyx/unplugin/webpack').default;
module.exports = { plugins: [ csszyxWebpack(), // ... other plugins ],};What the Turbopack lane can and cannot use
Section titled “What the Turbopack lane can and cannot use”csszyx’s Turbopack support is a webpack-compatible loader wired through
turbopack.rules, so it is bounded by how much of the webpack loader API
Turbopack implements. This is the state csszyx builds on, taken from the
Next.js 16 documentation and validated against Next.js 16.2.x, the version
the csszyx playground pins and CI runs.
| Capability | Status | What csszyx does with it |
|---|---|---|
turbopack config key | Named turbopack from 15.3; was experimental.turbo in 13.0.0–15.2.x, removed in 16 | csszyxTurbopack() writes this block |
turbopack.rules loaders | Supported | Runs the sz transform |
File dependencies (this.addDependency) | Supported — Turbopack reports a loader’s file dependencies back to its watcher | Watches your @theme stylesheets so szcn groups refresh live |
resolveAlias | Supported | Not used — the theme registration imports a real relative path, so no app config is needed |
this.emitFile | Not supported | Why the generated registration is written to .csszyx/ directly instead of emitted |
this.mode | Not supported | csszyx falls back to NODE_ENV, so dev/production detection is unaffected |
this.importModule / this.loadModule | Not supported | Unused |
this.fs | Partial — readFile only | Unused |
| Deep bundle rewrite hook | Not exposed | Why production mangling stays Webpack-only |
Next.js Turbopack setup
Section titled “Next.js Turbopack setup”-
Add
@csszyx/runtimeas a direct dependency. The transform injects a bareimport { _szMerge } from '@csszyx/runtime', which a strict package manager (pnpm) does not resolve as a transitive dependency.Terminal window pnpm add @csszyx/runtime -
Wire the loader with the
csszyxTurbopack()helper. It sets the*.tsxloader rule correctly (withoutas, which would otherwise self-match into./X.tsx.tsx) and merges your existingturbopackconfig.next.config.mjs import { csszyxTurbopack } from '@csszyx/unplugin/next';export default {turbopack: csszyxTurbopack({}, // your existing turbopack config (resolveAlias, other rules) is merged{ safelistOutputFile: '.csszyx/next-loader-classes.html' },),}; -
Keep the Tailwind safelist fresh. Point
@sourceat the safelist file and run the csszyx watcher/prebuild around Next. Production builds fail-closed (Next Turbopack production cache is not ready) untilcsszyx next prebuildhas seeded the safelist and generation manifest, so wire both flows into your scripts:package.json {"scripts": {"dev": "concurrently \"csszyx next watch 'app/**/*.tsx'\" \"next dev --turbopack\"","build": "csszyx next prebuild 'app/**/*.tsx' && next build --turbopack"}}- dev:
csszyx next watchmaintains the safelist whilenext devruns (the example uses concurrently; a second terminal works just as well) - build:
csszyx next prebuildmust finish beforenext buildstarts — running it as part of thebuildscript keeps plainpnpm buildworking
- dev:
Tailwind CSS v4 Entry Point
Section titled “Tailwind CSS v4 Entry Point”CSSzyx requires Tailwind CSS v4. Create a CSS entry point:
@import "tailwindcss";Import this in your app entry point:
import './index.css';Plugin Options
Section titled “Plugin Options”csszyx({ development: { debug: true, // Enable debug logging }, production: { mangle: true, // Obfuscate class names (z, y, x, ...) — opt-in, off by default injectChecksum: true, // Inject SSR hydration checksum }, build: { astBudgetLimit: 50_000, // Per-file AST node cap; file skipped (warned) past it scanCss: 'src/index.css', // CSS file(s) to scan for @theme tokens },});For per-element hydration recovery (szRecover="csr" / szRecover="dev-only")
see SSR & Hydration → Recovery Tokens.
Optional: Initialize Runtime
Section titled “Optional: Initialize Runtime”For SSR hydration safety, initialize the runtime in your app entry:
import { initRuntime } from '@csszyx/runtime';
initRuntime({ development: process.env.NODE_ENV === 'development', strictHydration: true,});This is optional — CSSzyx works without it, but SSR hydration guards require
it to be called before first render. Per-element CSR recovery is opted in
via the szRecover JSX attribute on individual elements; see
SSR & Hydration.
TypeScript
Section titled “TypeScript”The sz prop comes from a JSX type augmentation in @csszyx/types. It is
picked up automatically when you import from csszyx in a hoisting package
manager. With a strict package manager (pnpm), or if your app uses sz
without importing csszyx directly, add a one-line reference so TypeScript
loads the augmentation.
-
Install the types so the reference resolves at the top level:
Terminal window pnpm add -D @csszyx/types -
Add a
csszyx-env.d.tsat your project root (kept in yourtsconfig.jsoninclude):/// <reference types="@csszyx/types/jsx" />SolidJS keeps its own JSX namespace, so reference the Solid augmentation instead:
/// <reference types="@csszyx/types/jsx-solid" />Solid consumers must add
@csszyx/typesas a direct dev dependency (pnpm add -D @csszyx/types) — unlike the React augmentation, the Solid one is not picked up transitively throughcsszyx, so without the direct dependencyastro check/tscreportProperty 'sz' does not existon Solid elements.
Then the sz prop is typed on every element:
// ✅ TypeScript knows this is valid<div sz={{ p: 4, bg: 'blue-500', hover: { bg: 'blue-700' } }} />
// ❌ TypeScript error: 'red-999' is not a valid color<div sz={{ bg: 'red-999' }} />Troubleshooting
Section titled “Troubleshooting”Classes not applying — make sure your CSS entry point uses the full Tailwind v4 bundle:
/* ✅ correct — includes theme + preflight + utilities */@import "tailwindcss";
/* ❌ wrong — utilities only, theme variables undefined */@import "tailwindcss/utilities";Partial imports like tailwindcss/utilities only generate static-value utilities
(.border-0, .m-px). Scale-dependent utilities like p-4, rounded-sm, text-xs
require the theme layer (--spacing, --radius-sm, --text-xs) and will silently
produce no CSS without it.
Monorepo with both Tailwind v3 and v4 — if your workspace has other packages
that depend on Tailwind v3, your package’s CSS resolver may accidentally pick up v3
instead of v4, causing @import "tailwindcss" to fail or generate no theme utilities.
Fix: explicitly declare tailwindcss as a dependency in each package that uses CSSzyx:
{ "dependencies": { "csszyx": "^0.4.0", "tailwindcss": "^4.0.0" }}Plugin order warnings — CSSzyx must be before Tailwind and React in the plugins array.
“No prebuilt native binary” warning, or a native engine unavailable build
error — CSSzyx parses your source with three interchangeable engines. The
default is rust (a native addon) because it is the fastest; oxc and babel
are pure-JavaScript and need no binary. All three emit the exact same classes
(this is gate-tested for parity), so the engine only affects build speed, never
your output.
The native engine ships as per-platform optional dependencies
(@csszyx/core-<platform>) — one is auto-selected for your OS/CPU/libc during a
normal npm install. You do not run any extra command or compile anything;
it is just a prebuilt download. It can be absent in three situations:
- Unsupported platform — your OS/arch/libc is outside the prebuilt set (the targets are macOS arm64/x64, Linux gnu+musl arm64/x64, Windows arm64/x64). Anything else (FreeBSD, 32-bit ARM, riscv64, Alpine on an odd arch, …) has no matching package.
- Optional dependencies were skipped — installing with
--omit=optional/--no-optional, or in a minimal/sandboxed CI or Docker image that strips optional deps, removes the binary even on a supported platform. - A cross-platform lockfile — a lockfile generated on one OS (say macOS) then
installed on another (say Linux CI) with
--frozen-lockfilemay not have the other platform’s optional entry resolved, so the binary is missing.
What happens, and what to do:
-
You only inherited the default
rust(you did not setbuild.parseror theCSSZYX_PARSERenv var): CSSzyx automatically falls back tooxcand prints the warning once. Your build succeeds and your classes are identical — nothing to fix. To silence the warning, set the parser explicitly (next bullet) or install the matching@csszyx/core-<platform>package / stop omitting optional deps. -
You explicitly chose
rust(via config or env): this stays a hard error on purpose — an explicit choice is never silently swapped. Either install the native binary for your platform, or switch to a pure-JS engine:// vite.config.ts / csszyx plugin optionscsszyx({ build: { parser: 'oxc' } }) // or 'babel'Terminal window # one-off, e.g. in CICSSZYX_PARSER=oxc npm run build
Prefer oxc over babel when choosing manually — it is the faster pure-JS
engine; babel is the most compatible last resort. Effective fallback order is
rust → oxc → babel.
TypeScript errors with sz prop (Property 'sz' does not exist on type 'DetailedHTMLProps<...>') — the JSX augmentation is not loaded. Add
@csszyx/types and a csszyx-env.d.ts with
/// <reference types="@csszyx/types/jsx" />; see TypeScript
above. This is common under pnpm and when csszyx is never imported directly.
Hydration warnings in Next.js — initialize the runtime in your root layout
and opt in to per-element recovery with <section szRecover="csr">…</section>
where mismatches are expected. See SSR & Hydration.
sz props not working in Astro MDX — the MDX Vite plugin compiles JSX to
runtime calls before csszyx runs, so sz={{ ... }} props written directly in
.mdx files are never transformed and render as sz="[object Object]".
Fix: put sz props in .tsx components and import them in MDX:
// src/components/MyDemo.tsx ← compiled by csszyx ✅export function MyDemo() { return <div sz={{ p: 4, bg: 'blue-500' }}>Hello</div>;}{/* src/pages/guide.mdx */}import { MyDemo } from '../../components/MyDemo.tsx';
<MyDemo />