-
-**SSR-safe runtime logic** for handling interactivity in React server components without using
-
-```diff
-- use client
-```
-
-
-
-Designed to be tiny(~300 bytes), deterministic, and fully compatible with
-
-React Zero-UI's pre-rendered data-attribute model.
-
-
-
----
-
-### Why This Approach?
-
-**The Problem:** A single `onClick` event forces your entire component tree to become client-rendered. In Next.js, this means shipping extra JavaScript, losing SSR benefits, and adding hydration overhead, all for basic interactivity.
-
-**The Solution:** This design creates the perfect bridge between **static HTML** and **interactive UX**, while maintaining:
-
-- Server-rendered performance
-- Zero JavaScript bundle overhead
-- Instant visual feedback
-
-_Why sacrifice server-side rendering for a simple click handler when 300 bytes of runtime can handle all the clicks in your app?_
-
----
-
-## Core Functionality
-
-### `activateZeroUiRuntime()`
-
-The core runtime entrypoint that enables client-side interactivity in server components:
-
-**How it works:**
-
-1. **Single Global Listener** - Registers one click event listener on `document`
-2. **Smart Detection** - Listens for clicks on elements with `data-ui` attributes
-3. **Directive Parsing** - Interprets `data-ui` directives in this format.
-
-```diff
-+ data-ui="global:key(val1,val2,...)" -> flips data-key on document.body
-+ data-ui="scoped:key(val1,val2,...)" -> flips data-key on closest ancestor/self
-```
-
-4. **Round-Robin Cycling** - Cycles through values in sequence
-5. **Instant DOM Updates** - Updates DOM immediately for Tailwind responsiveness
-
-> **Note:** Guards against duplicate initialization using `window.__zero` flag.
-
----
-
-## Helper Functions
-
-### `zeroSSR.onClick()` & `scopedZeroSSR.onClick()`
-
-Utility functions that generate valid `data-ui` attributes for JSX/TSX:
-
-**Global Example:**
-
-```tsx
-zeroSSR.onClick("theme", ["dark", "light"]);
-// Returns: { 'data-ui': 'global:theme(dark,light)' }
-```
-
-**Scoped Example:**
-
-```tsx
-scopedZeroSSR.onClick("modal", ["open", "closed"]);
-// Returns: { 'data-ui': 'scoped:modal(open,closed)' }
-```
-
-**Development Validation:**
-
-- Ensures keys are kebab-case
-- Validates at least one value is provided
-
----
-
-## Installation & Setup
-
-### Step 1: Install Package
-
-```bash
-npm install @react-zero-ui/core@0.3.1-beta.2
-```
-
-### Step 2: Generate Variants
-
-Run your development server to generate the required variant map:
-
-```bash
-npm run dev
-```
-
-This creates `.zero-ui/attributes.ts` containing the variant map needed for runtime activation.
-
-### Step 3: Create `` Component
-
-```tsx
-"use client";
-
-import { variantKeyMap } from "path/to/.zero-ui/attributes";
-import { activateZeroUiRuntime } from "@react-zero-ui/core/experimental/runtime";
-
-activateZeroUiRuntime(variantKeyMap);
-
-export const InitZeroUI = () => null;
-```
-
-### Step 4: Add to Root Layout
-
-```tsx
-import { InitZeroUI } from "path/to/InitZeroUI";
-
-export default function RootLayout({ children }) {
- return (
-
-
-
- {children}
-
-
- );
-}
-```
-
----
-
-## Usage Examples
-
-### Global Theme Toggle
-
-```tsx
-import { zeroSSR } from "@react-zero-ui/core/experimental";
-
-
Click me to cycle themes!
;
-```
-
-**Pair with Tailwind variants:**
-
-```html
-
Interactive Server Component!
-```
-
-### Scoped Modal Toggle
-
-```tsx
-import { scopedZeroSSR } from "@react-zero-ui/core/experimental";
-
-// Scopes based on matching data-* attribute (e.g. data-modal)
-
-
-
;
-```
-
----
-
-## Design Philosophy
-
-### Core Principles
-
-- **No React State** - Zero re-renders involved
-- **Pure DOM Mutations** - Works entirely via `data-*` attribute changes
-- **Server Component Compatible** - Full compatibility with all server components
-- **Tailwind-First** - Designed for conditional CSS classes
-
----
-
-## Summary
-
-| Feature | Description |
-| ------------------------------- | --------------------------------------------------------- |
-| **`activateZeroUiRuntime()`** | Enables click handling on static components via `data-ui` |
-| **`zeroSSR` / `scopedZeroSSR`** | Generate valid click handlers as JSX props |
-| **Runtime Overhead** | ~300 bytes total |
-| **React Re-renders** | Zero |
-| **Server Component Support** | Full compatibility |
-
-> **Source Code:** See [experimental](/packages/core/src/experimental) for implementation details.
-
----
-
-
-
-**The bridge between static HTML and interactive UX**
-
-_No state. No runtime overhead. Works in server components. ZERO re-renders._
-
-[**Get Started in less than 5 minutes**](/#quick-start)
-
-
-
-# Frequently Asked Questions
-
-**Common questions and answers about React Zero-UI**
-
-Everything you need to know to get the most out of Zero-UI.
-
-
-
----
-
-## General Questions
-
-### What exactly is React Zero-UI?
-
-React Zero-UI is a state management library that eliminates React re-renders for UI state by using CSS and data attributes instead of component state. It "pre-renders" all possible UI states at **build time** and switches between them by flipping data attributes.
-
-### How is this different from regular React state?
-
-**Traditional React:**
-
-```tsx
-const [theme, setTheme] = useState("light");
-// Every setState() triggers re-render of component tree
-```
-
-**React Zero-UI:**
-
-```tsx
-const [, setTheme] = useUI("theme", "light");
-// No re-renders, just flips data-theme="dark" on
-```
-
-### Do I need to learn anything new?
-
-Not really! If you know React hooks and Tailwind CSS, you already know 95% of what you need:
-
-1. Replace `useState` with `useUI`
-2. Replace conditional classes with Tailwind variants
-3. Everything else works the same
-
----
-
-## Performance Questions
-
-### How much faster is it really?
-
-**Benchmarks** (10,000 DOM nodes):
-
-- React state changes: ~50ms
-- Zero-UI state changes: ~5ms
-- **Result: 10× faster updates**
-
-The more complex your UI, the bigger the performance gain.
-
-### Does it increase my bundle size?
-
-**Zero-UI core: ~350 bytes** in production (10x smaller than a single svg icon)
-
-Compare that to:
-
-- Redux: ~5KB
-- SVG Icon: ~4KB
- So about 10x smaller than a single SVG icon
-
-### What about CSS file size?
-
-CSS variants are only generated for the states you actually use. If you use 3 theme values, you get CSS for 3 variants—not hundreds.
-
----
-
-Here’s a tighter, sharper version with your key points, a nod to Prepack’s failure, and a forward-looking note about Turbopack:
-
----
-
-## Technical Questions
-
-### Why can't I use imported variables in the state key?
-
-Zero-UI uses a custom Babel-based resolver that only analyzes **top-level `const` values in the same file**. Imported variables are **not supported**, even if re-assigned to a local `const`.
-
-Why? Because resolving cross-file imports requires a **full module graph**, accounting for:
-
-- ESM vs CJS interop
-- TypeScript vs JavaScript
-- Re-exports, namespace imports, aliased paths, dynamic values
-- Recursive constant folding across files
-
-This problem is **deceptively deep** — even Facebook's [Prepack](https://prepack.io/) abandoned the attempt after years of effort.
-
-Until a future plugin system for **Turbopack** emerges, we've chosen the simpler, safer route:
-**Only local `const` string literals are supported.**
-
-We may ship a resolver plugin for Turbopack once it's open to third-party hooks.
-
----
-
-Want it even shorter or more opinionated?
-
-### Can I use it with existing state management?
-
-Absolutely! Zero-UI is designed for **UI state only**. Use it alongside:
-
-- Redux/Zustand for business logic
-- React Query for server state
-- Regular `useState` for component-specific data
-
-```tsx
-// Great combination
-const [data, setData] = useState(null); // Component state
-const { user } = useQuery("user"); // Server state
-const [, setTheme] = useUI("theme", "light"); // UI state
-```
-
-### Does it work with SSR/Next.js?
-
-Yes! Zero-UI is designed with SSR in mind:
-
-- No hydration mismatches
-- Perfect FOUC prevention
-- Works with Next.js App Router
-- Experimental server component support see [experimental](./experimental.md)
-
-### Can I persist state across page reloads?
-
-Zero-UI state is DOM-based, so it doesn't persist automatically. For persistence you handle it the same as you would with regular React state:
-
-```tsx
-// Save to localStorage
-const [, setTheme] = useUI("theme", "light");
-
-const persistentSetTheme = (value) => {
- setTheme(value);
- localStorage.setItem("theme", value);
-};
-
-// Load on mount
-useEffect(() => {
- const saved = localStorage.getItem("theme");
- if (saved) setTheme(saved);
-}, []);
-```
-
----
-
-## Styling Questions
-
-### Do I have to use Tailwind?
-
-**For the best experience, yes.** Zero-UI generates Tailwind variants automatically.
-
-and tailwind variants are generated by tailwindCSS.
-
-```css
-[data-theme="dark"] {
- background: black;
-}
-```
-
-### Can I use CSS variables?
-
-Yes! Pass the `CssVar` flag:
-
-```tsx
-import { useUI, CssVar } from "@react-zero-ui/core";
-
-const [, setColor] = useUI("primary", "#blue", CssVar);
-// Result:
-```
-
----
-
-## Migration Questions
-
-### How hard is it to migrate from useState?
-
-**Very easy** for UI state:
-
-```tsx
-// Before
-const [theme, setTheme] = useState("light");
-
-// After
-const [, setTheme] = useUI("theme", "light");
-// Note: Don't use the first return value for logic
-```
-
-### What about Context API?
-
-If context is used for UI state, it's even easier! Just remove the provider:
-
-```tsx
-// Before: Need provider, useContext, prop drilling
-
-
-;
-
-// After: State works everywhere automatically
-function App() {
- const [, setTheme] = useUI("theme", "light");
- // Theme accessible anywhere via CSS classes
-}
-```
-
-### Should I migrate everything at once?
-
-**No!** Migrate incrementally:
-
-1. Start with global UI state (theme, modals)
-2. Move component-specific UI state
-3. Leave business logic in existing solutions
-
----
-
-## Experimental Features
-
-### What's the experimental SSR runtime?
-
-It allows interactivity in **server components** without `'use client'` see [experimental](./experimental.md):
-
-```tsx
-// This is a SERVER COMPONENT!
-import { zeroSSR } from "@react-zero-ui/core/experimental";
-
-function ServerThemeToggle() {
- return ;
-}
-```
-
-Only ~300 bytes of runtime for unlimited server component interactivity.
-
-### Is the experimental API stable?
-
-It's **experimental** but used in production by early adopters. The API may change in minor versions, but we'll provide migration guides.
-
-### Should I use it in production?
-
-I would not recommend using it in production until the API is stable. Because the API is still in development and the API may change in minor versions, but we'll provide migration guides.
-
----
-
-## Troubleshooting
-
-### My Tailwind variants aren't working
-
-**Check these in order:**
-
-1. **PostCSS plugin configured? (Next.js)**
-
- ```js
- // postcss.config.js
- module.exports = {
- plugins: {
- "@react-zero-ui/core/postcss": {}, // Before Tailwind!
- tailwindcss: {},
- },
- };
- ```
-
-2. **Tailwind V4 Configured and imported?**
- ```css
- @import "tailwindcss";
- ```
-
-### I get "Multiple ref attachments" error
-
-Each `useScopedUI` can only attach to one element:
-
-```tsx
-// Wrong
-const [, setState] = useScopedUI('state', 'default');
-
- // Error!
-
-// Right
-const [, setState1] = useScopedUI('state-1', 'default');
-const [, setState2] = useScopedUI('state-2', 'default');
-
-
-```
-
----
-
-## Best Practices
-
-### When should I use Zero-UI?
-
-**Perfect for:**
-
-- Theme switching
-- Modal/drawer states
-- Navigation states
-- UI toggles and animations
-- Any visual state that doesn't affect business logic
-
-**Not ideal for:**
-
-- Form data
-- API responses
-- Complex business logic
-- State that needs to trigger side effects
-
-### Should I use global or scoped state?
-
-**Global (`useUI`)** for:
-
-- App-wide state (theme, language)
-- State that affects multiple components
-- State you want accessible everywhere
-
-**Scoped (`useScopedUI`)** for:
-
-- Component-specific state
-- State that doesn't affect other components
-- Better performance for isolated changes
-
----
-
-## Future & Roadmap
-
-### What's coming next?
-
-- Enhanced TypeScript support
-- More framework integrations (Vue, Svelte)
-- Better DevTools integration
-- Performance monitoring tools
-
-### How can I influence the roadmap?
-
-- Vote on feature requests in GitHub Discussions
-- Report bugs and use cases
-- Share your usage patterns
-- Contribute code or documentation
-
----
-
-## Getting Help
-
-### Where can I ask questions?
-
-1. **Documentation:** Check the guides first
-2. **Discussions:** [GitHub Discussions](https://github.com/react-zero-ui/core/discussions) for questions
-3. **Issues:** [GitHub Issues](https://github.com/react-zero-ui/core/issues) for bugs
-
-### How do I report a bug?
-
-1. Check existing issues first
-2. Create a minimal reproduction
-3. Include your configuration (postcss.config.js, etc.)
-4. Include browser and framework versions
-
-### Can I contribute?
-
-**Absolutely!** We welcome:
-
-- Documentation improvements
-- Bug fixes
-- Feature implementations
-- Examples and demos
-
-See our [Contributing Guide](./CONTRIBUTING.md) to get started.
-
----
-
-
-
-### Still have questions?
-
-[**Ask in Discussions**](https://github.com/react-zero-ui/core/discussions) | [**Read the Docs**](../README.md) | [**Try the Demo**](https://zero-ui.dev)
-
-The community is here to help!
-
-
diff --git a/docs/installation-next.md b/docs/installation-next.md
deleted file mode 100644
index 197a9b2..0000000
--- a/docs/installation-next.md
+++ /dev/null
@@ -1,76 +0,0 @@
-### Next.js (App Router) Setup
-
-1. **Install the dependencies**
-
-```bash
-npm install @react-zero-ui/core
-```
-
-```bash
-npm install @tailwindcss/postcss
-```
-
----
-
-2. **Add the PostCSS plugin (must come _before_ Tailwind).**
-
-```js
-// postcss.config.* ESM Syntax
-const config = {
- // Zero-UI must come before Tailwind
- plugins: ["@react-zero-ui/core/postcss", "@tailwindcss/postcss"],
-};
-export default config;
-```
-
-```js
-// postcss.config.* Common Module Syntax
-module.exports = {
- // Zero-UI must come before Tailwind
- plugins: { "@react-zero-ui/core/postcss": {}, tailwindcss: {} },
-};
-```
-
-3. **Import Tailwind CSS**
-
-```css
-// global.css
-@import "tailwindcss";
-```
-
----
-
-4. **Start the App**
-
-```bash
-npm run dev
-```
-
-> Zero-UI will generate a .zero-ui folder in your project root. and generate the attributes.ts and type definitions for it.
-
----
-
-5. **Preventing FOUC (Flash Of Unstyled Content)**
-
-Spread `bodyAttributes` on `` in your root layout.
-
-```tsx
-// app/layout.tsx
-import { bodyAttributes } from "./.zero-ui/attributes";
-
-export default function RootLayout({ children }) {
- return (
-
- // Spread the bodyAttributes on the body tag
- {children}
-
- );
-}
-```
-
-**Thats it.**
-Zero-UI will now add used data-\* attributes to the body tag and the CSS will be injected and transformed by tailwind.
-
-**Checkout our Experimental SSR Safe OnClick Handler**
-
-[**Zero UI OnClick**](/docs/experimental.md)
diff --git a/docs/installation-vite.md b/docs/installation-vite.md
deleted file mode 100644
index dffedf6..0000000
--- a/docs/installation-vite.md
+++ /dev/null
@@ -1,45 +0,0 @@
-### Vite Setup
-
-1. **Install the dependencies**
-
-```bash
-npm install @react-zero-ui/core
-```
-
-```bash
-npm install @tailwindcss/postcss
-```
-
----
-
-## Setup
-
-### Vite
-
-2. **Add the plugin to your vite.config.ts**
-
-```js
-// vite.config.*
-import zeroUI from "@react-zero-ui/core/vite";
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
-import tailwindCss from "@tailwindcss/postcss";
-
-export default defineConfig({
- // Remove the default `tailwindcss()` plugin - and pass it into the `zeroUI` plugin
- plugins: [zeroUI({ tailwind: tailwindCss }), react()],
-});
-```
-
-3. **Import Tailwind CSS**
-
-```css
-// global.css
-@import "tailwindcss";
-```
-
-**Thats it.**
-
-The plugin will add the data-\* attributes to the body tag (no FOUC) and the CSS will be injected and transformed by tailwind.
-
-
diff --git a/docs/migration-guide.md b/docs/migration-guide.md
deleted file mode 100644
index 5003459..0000000
--- a/docs/migration-guide.md
+++ /dev/null
@@ -1,443 +0,0 @@
-# Migration Guide
-
-
-
-**Migrate to React Zero-UI from existing state management solutions**
-
-Step-by-step guides for common migration scenarios.
-
-
- );
-}
-```
-
----
-
-## From Component State to Global State
-
-### Converting Local State to Global
-
-**Before (Local component state):**
-
-```tsx
-function Sidebar() {
- const [isOpen, setIsOpen] = useState(false);
-
- return (
-
-
-### Migration Complete!
-
-Your app should now be faster, simpler, and more maintainable.
-
-[**Next: Usage Examples**](./usage-examples.md) | [**API Reference**](../README.md#api-reference)
-
-
diff --git a/docs/rules.md b/docs/rules.md
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/size.md b/docs/size.md
deleted file mode 100644
index ce11131..0000000
--- a/docs/size.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Bundle Size Proof
-
-This repo measures bundle size from the built `@react-zero-ui/core` entry.
-
-## Command
-
-```bash
-pnpm build
-pnpm size
-pnpm size:badge
-```
-
-The `size` script in [`package.json`](../package.json) runs:
-
-```bash
-npx esbuild ./packages/core/dist/index.js --bundle --minify --format=esm --external:react --define:process.env.NODE_ENV='"production"' | gzip -c | wc -c
-```
-
-That produces the gzipped byte count used for the README badge.
-
-## Badge File
-
-Run `pnpm size:badge` after a build when you want to refresh the badge JSON.
-
-That writes:
-
-- [`core-size.json`](../.github/badges/core-size.json)
-
-The README badge reads from that committed file through a Shields endpoint.
diff --git a/docs/usage-examples.md b/docs/usage-examples.md
deleted file mode 100644
index 82b5341..0000000
--- a/docs/usage-examples.md
+++ /dev/null
@@ -1,477 +0,0 @@
-# Usage Examples & Patterns
-
-
-
-**Comprehensive examples and patterns for React Zero-UI**
-
-Learn through practical, real-world use cases and best practices.
-
-
-
-### Ready to build?
-
-These patterns cover 95% of real-world use cases. Mix and match them to create powerful, performant UIs.
-
-[**View Live Demo**](https://zero-ui.dev) | [**API Reference**](../README.md#api-reference)
-
-
diff --git a/examples/demo/.gitignore b/examples/demo/.gitignore
new file mode 100644
index 0000000..55a12ae
--- /dev/null
+++ b/examples/demo/.gitignore
@@ -0,0 +1,28 @@
+# deps
+/node_modules
+
+# generated content
+.contentlayer
+.content-collections
+.source
+
+# test & build
+/coverage
+/.next/
+/out/
+/build
+*.tsbuildinfo
+
+# misc
+.DS_Store
+*.pem
+/.pnp
+.pnp.js
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# others
+.env*.local
+.vercel
+next-env.d.ts
\ No newline at end of file
diff --git a/examples/demo/.zero-ui/attributes.d.ts b/examples/demo/.zero-ui/attributes.d.ts
index a2af8b3..a1476b5 100644
--- a/examples/demo/.zero-ui/attributes.d.ts
+++ b/examples/demo/.zero-ui/attributes.d.ts
@@ -1,15 +1,12 @@
/* AUTO-GENERATED - DO NOT EDIT */
export declare const bodyAttributes: {
- "data-accent": "amber" | "emerald" | "violet";
- "data-active": "react" | "zero";
- "data-menu-open": "false" | "true";
- "data-mobile-menu": "closed" | "open";
- "data-scrolled": "down" | "up";
- "data-theme": "dark" | "light";
- "data-theme-test": "dark" | "light";
- "data-theme-test-ssr": "dark" | "light";
+ "data-demo-search-status": "loading";
+ "data-perf-accent": "amber" | "emerald" | "violet";
+ "data-perf-active": "react" | "zero";
+ "data-perf-menu-open": "false" | "true";
+ "data-perf-theme": "dark" | "light";
};
export declare const variantKeyMap: {
- [key: string]: true;
+ [key: string]: true | string[] | '*';
};
diff --git a/examples/demo/.zero-ui/attributes.js b/examples/demo/.zero-ui/attributes.js
index cd87cdd..daa2e62 100644
--- a/examples/demo/.zero-ui/attributes.js
+++ b/examples/demo/.zero-ui/attributes.js
@@ -1,21 +1,15 @@
/* AUTO-GENERATED - DO NOT EDIT */
export const bodyAttributes = {
- "data-accent": "violet",
- "data-active": "zero",
- "data-menu-open": "false",
- "data-mobile-menu": "closed",
- "data-scrolled": "up",
- "data-theme": "light",
- "data-theme-test": "light",
- "data-theme-test-ssr": "dark"
+ "data-demo-search-status": "idle",
+ "data-perf-accent": "violet",
+ "data-perf-active": "zero",
+ "data-perf-menu-open": "false",
+ "data-perf-theme": "light"
};
-export const variantKeyMap = {
- "data-accent": true,
- "data-active": true,
- "data-menu-open": true,
- "data-mobile-menu": true,
- "data-scrolled": true,
- "data-theme": true,
- "data-theme-test": true,
- "data-theme-test-ssr": true
+export const variantKeyMap = {
+ "data-demo-search-status": true,
+ "data-perf-accent": true,
+ "data-perf-active": true,
+ "data-perf-menu-open": true,
+ "data-perf-theme": true
};
diff --git a/examples/demo/README.md b/examples/demo/README.md
new file mode 100644
index 0000000..9b7bba9
--- /dev/null
+++ b/examples/demo/README.md
@@ -0,0 +1,45 @@
+# docs
+
+This is a Next.js application generated with
+[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
+
+Run development server:
+
+```bash
+npm run dev
+# or
+pnpm dev
+# or
+yarn dev
+```
+
+Open http://localhost:3000 with your browser to see the result.
+
+## Explore
+
+In the project, you can see:
+
+- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.
+- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep.
+
+| Route | Description |
+| ------------------------- | ------------------------------------------------------ |
+| `app/(home)` | The route group for your landing page and other pages. |
+| `app/docs` | The documentation layout and pages. |
+| `app/api/search/route.ts` | The Route Handler for search. |
+
+### Fumadocs MDX
+
+A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
+
+Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details.
+
+## Learn More
+
+To learn more about Next.js and Fumadocs, take a look at the following
+resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
+ features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs
diff --git a/examples/demo/app/(home)/_components/Comparison.tsx b/examples/demo/app/(home)/_components/Comparison.tsx
new file mode 100644
index 0000000..9cc7af6
--- /dev/null
+++ b/examples/demo/app/(home)/_components/Comparison.tsx
@@ -0,0 +1,39 @@
+'use client';
+
+import { Atom, Zap } from 'lucide-react';
+import { useUI } from '@react-zero-ui/core';
+import { ReactState } from './ReactState';
+import { ZeroState } from './ZeroState';
+
+export function Comparison() {
+ const [, setActive] = useUI<'zero' | 'react'>('perf-active', 'zero');
+
+ return (
+
+ );
+}
diff --git a/examples/demo/app/(home)/demo/real-world/_components.tsx b/examples/demo/app/(home)/demo/real-world/_components.tsx
new file mode 100644
index 0000000..a4d6176
--- /dev/null
+++ b/examples/demo/app/(home)/demo/real-world/_components.tsx
@@ -0,0 +1,181 @@
+'use client';
+
+import { useEffect, useRef, useState } from 'react';
+import { useUI } from '@react-zero-ui/core';
+import { categories, fetchWithDelay, type Category, type Product } from './_data';
+
+type CategoryFilter = Category | 'all';
+
+export function RealWorldDemo() {
+ const [query, setQuery] = useState('');
+ const [category, setCategory] = useState('all');
+
+ return (
+
+
+
+
+
+
+
+ Type in the search or change the filter. Watch the render counters — both panes update on keystroke (the search input drives both),
+ but only the React pane re-renders during the loading transition.
+
+ Use useState for what changes (the product data) and useUI{' '}
+ for how it looks (the loading skeleton). Both panes below fetch the same mock data with a 650 ms delay — only the React
+ version re-renders when the skeleton appears and disappears.
+
— Presentation states (loading, expanded, focused) flip a data-attribute.
+
— Actual data (fetched items, form values) stays in React state.
+
— The expensive tree (the list) only re-renders when the data changes.
+
— The skeleton appears instantly via CSS, not via React reconciliation.
+
+
+
+
+ );
+}
diff --git a/examples/demo/app/(home)/layout.tsx b/examples/demo/app/(home)/layout.tsx
new file mode 100644
index 0000000..77379fa
--- /dev/null
+++ b/examples/demo/app/(home)/layout.tsx
@@ -0,0 +1,6 @@
+import { HomeLayout } from 'fumadocs-ui/layouts/home';
+import { baseOptions } from '@/lib/layout.shared';
+
+export default function Layout({ children }: LayoutProps<'/'>) {
+ return {children};
+}
diff --git a/examples/demo/app/(home)/page.tsx b/examples/demo/app/(home)/page.tsx
new file mode 100644
index 0000000..44a2174
--- /dev/null
+++ b/examples/demo/app/(home)/page.tsx
@@ -0,0 +1,217 @@
+import Link from 'next/link';
+import { ArrowRight, Zap, Layers, Feather, Github } from 'lucide-react';
+import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
+import { Comparison } from './_components/Comparison';
+
+export default function HomePage() {
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+function Hero() {
+ return (
+
+
+
+ Zero runtime · Zero re-renders · ~350 bytes
+
+
+ Ultra-fast React UI state,
+ powered by CSS.
+
+
+ React Zero-UI pre-renders every UI state at build time and flips data-* attributes on the
+ fly — giving you global state without providers, re-renders, or hydration headaches.
+
+ Themes, modals, sidebars, accents — none of that needs to live in React. Zero-UI moves presentation state into the DOM where it
+ belongs, while you keep React for the things React is good at.
+
+ Same UI, built twice. The Zero-UI tab flips data-* attributes on{' '}
+ <body>; the React tab holds the same state in{' '}
+ useState. Watch the render counters as you click around.
+
+
+
+
+
+
+
+
The hybrid pattern
+
Search + skeleton loading, useState for data, useUI for presentation.
+
+
+
+
+
+ );
+}
+
+function WhyFast() {
+ const cards = [
+ {
+ icon: ,
+ title: 'Zero re-renders',
+ body: 'State changes flip DOM attributes. React stays completely out of the loop — no reconciliation, no render cycles.',
+ },
+ {
+ icon: ,
+ title: '~350 bytes',
+ body: 'Smaller than a single SVG icon. An order of magnitude leaner than Redux or Zustand for UI state.',
+ },
+ {
+ icon: ,
+ title: 'Build-time CSS',
+ body: 'Tailwind variants are generated for every possible state at build time. Switching states is just changing a selector match.',
+ },
+ ];
+
+ return (
+
+