Skip to content

Installation

Terminal window
npm install csszyx

The 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.

vite.config.ts
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(),
],
});
  1. Add @csszyx/runtime as a direct dependency. The transform injects a bare import { _szMerge } from '@csszyx/runtime', which a strict package manager (pnpm) does not resolve as a transitive dependency.

    Terminal window
    pnpm add @csszyx/runtime
  2. Wire the loader with the csszyxTurbopack() helper. It sets the *.tsx loader rule correctly (without as, which would otherwise self-match into ./X.tsx.tsx) and merges your existing turbopack config.

    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' },
    ),
    };
  3. Keep the Tailwind safelist fresh. Point @source at the safelist file and run the csszyx watcher/prebuild around Next. Production builds fail-closed (Next Turbopack production cache is not ready) until csszyx next prebuild has 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 watch maintains the safelist while next dev runs (the example uses concurrently; a second terminal works just as well)
    • build: csszyx next prebuild must finish before next build starts — running it as part of the build script keeps plain pnpm build working

CSSzyx requires Tailwind CSS v4. Create a CSS entry point:

src/index.css
@import "tailwindcss";

Import this in your app entry point:

src/main.tsx
import './index.css';
csszyx({
development: {
debug: true, // Enable debug logging
},
production: {
mangle: true, // Minify class names (z, y, x, ...)
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.

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.

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.

  1. Install the types so the reference resolves at the top level:

    Terminal window
    pnpm add -D @csszyx/types
  2. Add a csszyx-env.d.ts at your project root (kept in your tsconfig.json include):

    /// <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/types as a direct dev dependency (pnpm add -D @csszyx/types) — unlike the React augmentation, the Solid one is not picked up transitively through csszyx, so without the direct dependency astro check / tsc report Property 'sz' does not exist on 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' }} />

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-lockfile may 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 set build.parser or the CSSZYX_PARSER env var): CSSzyx automatically falls back to oxc and 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 options
    csszyx({ build: { parser: 'oxc' } }) // or 'babel'
    Terminal window
    # one-off, e.g. in CI
    CSSZYX_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 />