Instant UI state updates. ZERO React re‑renders. <1 KB runtime.
Pre‑render your UI once, flip a data-* attribute to update — that's it.
| Example | Link | What it shows | Link to Code |
|---|---|---|---|
| Interactive menu with render tracker | Main Demo↗ | Compare Zero‑UI vs. React side‑by‑side while toggling a menu. | Github |
| React benchmark (10 000 nested nodes) | React 10k↗ | How long the traditional React render path takes. | Github |
| Zero‑UI benchmark (10 000 nested nodes) | Zero‑UI 10k↗ | Identical DOM, but powered by Zero‑UI's data-* switch. |
Github |
Every setState in React triggers the full VDOM → Diff → Reconciliation → Paint pipeline. For pure UI state (themes, menus, toggles) that work is wasted.
Zero‑UI introduces "PRE‑rendering":
- Tailwind variants for every state are generated at build‑time.
- The app pre‑renders once.
- Runtime state changes only flip a
data-*attribute on<body>.
Result → 5-10× faster visual updates with ZERO additional bundle cost.
| Nodes updated | React state | Zero‑UI | Speed‑up |
|---|---|---|---|
| 10,000 | ~50 ms | ~5 ms | 10× |
| 25,000 | ~180 ms | ~15 ms | 12× |
| 50,000 | ~300 ms | ~20 ms | 15× |
Re‑run these numbers yourself via the links above.
Prerequisite: Tailwind CSS v4 must already be initialized in your project.
# Inside an existing *Next.js (App Router)* or *Vite* repo
npx create-zero-uiThat's it — the CLI patch‑installs the required Babel & PostCSS plugins and updates configs for you.
npm install @react-zero-ui/coreThen follow Setup → for your bundler.
// vite.config.*
import { zeroUIPlugin } from '@react-zero-ui/core/vite';
export default {
// ❗️Remove the default `tailwindcss()` plugin — Zero‑UI extends it internally
plugins: [zeroUIPlugin()],
};-
Spread
bodyAttributeson<body>in your root layout.// app/layout.tsx import { bodyAttributes } from '@zero-ui/attributes'; // or: import { bodyAttributes } from '../.zero-ui/attributes'; export default function RootLayout({ children }) { return ( <html lang="en"> <body {...bodyAttributes}>{children}</body> </html> ); }
-
Add the PostCSS plugin (must come before Tailwind).
// postcss.config.js module.exports = { plugins: { '@react-zero-ui/core/postcss': {}, tailwindcss: {} } };
const [staleValue, setValue] = useUI<'open' | 'closed'>('sidebar', 'closed');key→ becomesdata-{key}on<body>.defaultValue→ SSR, prevents FOUC.- Note: the returned
staleValuedoes not update (useUIis write‑only).
<div className="sidebar-open:translate-x-0 sidebar-closed:-translate-x-full" />Any data-{key}="{value}" pair becomes a variant: {key}-{value}:.
useUI→ writes todata-*attributes on<body>.- Babel plugin → scans code, finds every
key/value, injects them into PostCSS. - PostCSS plugin → generates static Tailwind classes at build‑time.
- Runtime → changing state only touches the attribute — no VDOM, no reconciliation, ZERO re‑renders.
- Zero React re‑renders for UI‑only state.
- Global setters — call from any component or util.
- Tiny: < 391 Byte gzipped runtime.
- TypeScript‑first.
- SSR‑friendly (Next.js & Vite SSR).
- Framework‑agnostic CSS — generated classes work in plain HTML / Vue / Svelte as well with extra config.
- UI state only → themes, layout toggles, feature flags.
- Business logic stays in React → fetching, data mutation, etc.
- Kebab‑case keys → e.g.
sidebar-open. - Provide defaults to avoid Flash‑Of‑Unstyled‑Content.
PRs & issues welcome! Please read the Contributing Guide.
MIT © Austin Serb
Built with ❤️ for the React community. If Zero‑UI makes your app feel ZERO fast, please ⭐️ the repo!
