registyle
v2.1.1
Published
Write Tailwind utilities once. Use semantic class names everywhere.
Maintainers
Readme
registyle
A semantic styling library that compiles Tailwind CSS v4 utilities into named component classes, with a separate CSS-only runtime for plain declarations.
ESM only. This package requires Node.js 18+ and a bundler that supports ES Modules (Vite, webpack 5, Rollup, esbuild). CommonJS
require()is not supported.
See CHANGELOG for version history.
Try It Online
▶ Open in StackBlitz — no install needed, runs in your browser.
Features
- Compile Tailwind utilities onto semantic selectors such as
.action-buttonand.action-button-primary - Register component slots with
register.group() - Use utility arrays and grouped prefixes such as
max-sm:(w-full flex-col) - Compose conditional class names with
cx()or generate variant registrations withregistyle/variants - Choose a Vite plugin, a manual compiler step, or the CSS-only runtime
- Extract runtime CSS for server-side rendering
Install
npm install registyleHow It Works
For a React app, the Vite plugin is the shortest path:
registration files -> Vite collects styles -> Tailwind v4 compiles utilities -> virtual CSS -> semantic classes in JSXYou write register() calls in JavaScript modules. The plugin collects them from src/registyles/index.js and exposes compiled CSS as virtual:registyle.css. You do not create a manifest by hand. Registyle compiles only registered utilities; it does not scan JSX or HTML for class names, and its stylesheet does not include Tailwind Preflight.
For apps without Vite, use compileToFile() in a build script. If you do not need Tailwind, use the CSS-only runtime API instead.
Quick Start: React + Vite
Create a React app if you do not already have one:
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm install registyle
npm install -D @tailwindcss/postcss postcss postcss-selector-parserVite's React template already includes Vite and @vitejs/plugin-react. Add the Registyle plugin to vite.config.js:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { registyle } from 'registyle/vite';
export default defineConfig({
plugins: [react(), registyle()],
});Create src/registyles/button.js and register a component style:
import { register } from 'registyle/collector';
register('action-button', {
base: {
tw: [
'inline-flex items-center rounded-md',
'px-4 py-2 font-medium',
],
},
modifiers: {
primary: { tw: 'bg-blue-600 text-white hover:bg-blue-700' },
secondary: { tw: 'bg-gray-100 text-gray-900' },
},
});Create src/registyles/index.js to collect the styles. The Vite plugin uses this entry by default:
import { getManifest } from 'registyle/collector';
import './button.js';
export default getManifest();Import the virtual stylesheet once in src/main.jsx:
import 'virtual:registyle.css';Then use the semantic class in your React component:
import { cx } from 'registyle';
export function Button({ variant = 'primary', className, ...props }) {
return (
<button
className={cx('action-button', `action-button-${variant}`, className)}
{...props}
/>
);
}Run the app as usual:
npm run devThe CSS flow is handled by Vite: it watches src/registyles, recompiles when a registration changes, and emits CSS during npm run build. The component demo in examples/register-component-demo shows the complete setup.
Writing Utilities
A tw value can be a string or an array of strings. Use a string for a short set of utilities and an array to keep longer sets readable:
register('action-button', {
tw: [
'inline-flex items-center rounded-md',
'bg-blue-600 px-4 py-2 font-medium text-white',
'hover:(bg-blue-700 text-white)',
],
});Grouped prefixes expand at compile time:
max-sm:(w-full flex-col) -> max-sm:w-full max-sm:flex-col
border-(2 red-500) -> border-2 border-red-500Native Tailwind v4 single-value shorthand such as bg-(--brand) is passed through unchanged. Use Tailwind v4 slash notation for color alpha, for example bg-red-500/50.
Other Build Setups
For bundlers without a Registyle plugin, compile a manifest in a Node build script and import the generated CSS through your app's normal CSS pipeline:
// scripts/build-styles.mjs
import { compileToFile } from 'registyle/compile';
const manifest = {
classes: {
'action-button': {
tw: 'inline-flex rounded-md bg-blue-600 px-4 py-2 text-white',
},
},
};
await compileToFile(manifest, 'src/registyle.css');Run node scripts/build-styles.mjs before your framework's build command, then import src/registyle.css from your app entry. If you use a custom Tailwind theme or CSS-first plugins, pass an inputCss option that references your app stylesheet:
await compileToFile(manifest, 'src/registyle.css', {
inputCss: '@reference "./src/app.css"; @import "tailwindcss/utilities.css";',
baseDir: process.cwd(),
});See the integration guide for more build and server-rendering options.
CSS-Only Runtime
For plain CSS declarations without Tailwind, import register() from the package root:
import { register } from 'registyle';
register('notice', {
padding: '0.75rem 1rem',
color: '#1e3a8a',
backgroundColor: '#eff6ff',
});In the browser, the runtime injects a style tag. In Node.js, call register.extractCSS() after importing registration modules to get the registered CSS for server-side extraction. The runtime API does not compile tw utilities.
API and Guides
- Documentation guide — choose a workflow and find the right guide
- API reference — runtime, collector, compiler, and Vite APIs
- Integrations — Vite, other bundlers, and runtime CSS extraction
- CodeSandbox & Online IDEs — setup for browser-based development
- Advanced guide — themes, variants, CSS layers, and container queries
- Troubleshooting — common compiler, CSS, and Vite issues
- Migration guide — upgrade from v1 to v2
- Changelog — release history
- Examples — component library and todo app
Why Registyle
Most approaches make you choose: Tailwind's power or clean class names in HTML. Registyle gives you both.
<!-- ❌ CVA + Tailwind — utility strings sprawl in every JSX file -->
<button class="inline-flex items-center justify-center gap-2 font-medium rounded-md
bg-blue-600 text-white hover:bg-blue-700 focus-visible:outline-2
focus-visible:outline-blue-600 px-4 py-2 text-sm transition-colors">
<!-- ✅ registyle — semantic names, Tailwind compiled underneath -->
<button class="btn btn-primary btn-md">| | registyle | CVA + Tailwind | twin.macro | vanilla-extract |
|---|---|---|---|---|
| Semantic class names in HTML | ✅ .btn .btn-primary | ❌ utility strings | ❌ hashed | ❌ hashed |
| Tailwind v4 support | ✅ native | ✅ | ❌ | ❌ |
| Zero JS runtime | ✅ | ⚠️ +2 KB CVA | ❌ +8–13 KB | ✅ |
| Extend / inheritance | ✅ built-in | ❌ | ❌ | ❌ |
| CSS output (22 components) | ~7 KB gzip | ~10–15 KB | N/A | ~8–12 KB |
| Active in 2025 | ✅ | ✅ | ⚠️ stale | ✅ |
Bundle Size
The component demo — 22 full UI components — produces:
| | Raw | Gzip | |---|---|---| | Tailwind v4 base layer (fixed overhead) | 27.4 KB | ~5.5 KB | | Registyle semantic classes (200+ rules) | 9.8 KB | ~1.9 KB | | Keyframes + CSS variables | 1.7 KB | ~0.7 KB | | Total | 38.9 KB | 7.3 KB |
Zero KB of JavaScript runtime overhead. The base layer overhead is fixed — adding more components costs only ~0.09 KB gzip each.
When to Use Registyle
Registyle is for projects that want Tailwind v4 utilities compiled onto semantic class names from an explicit set of registrations. It is especially useful when component markup should stay independent of the utilities that style it. Choose the CSS-only runtime when you only need plain declarations.
Support This Project
If Registyle helps your project, consider buying me a coffee! Your support helps maintain and improve the library.
Contributing
Contributions are welcome. Report bugs and feature requests through GitHub Issues.
License
MIT
