Installation

Scaffold a new rejoice app with a single command. The CLI will walk you through project name, router setup, default theme, and git initialization.

terminal
bunx create-rejoice-app@latest my-app
cd my-app
bun install
bun dev

Requires Bun installed. The scaffolder uses Bun as the default runtime and bundler.

CLI Flags

Every prompt can be skipped with a flag, making the CLI fully non-interactive for AI agents and CI:

terminal
bunx create-rejoice-app@latest my-app --router --theme dark --git
Flag Default Description
--router true Include React Router v6
--no-router Skip React Router
--theme light|dark "light" Sets the initial theme for RejoiceProvider
--git true Initialize a git repository
--no-git Skip git init

When all flags are provided along with the project name, no prompts are shown. Flags not provided will fall back to interactive prompts.

Quick Start

After scaffolding, your app is ready. Here's the entry point the CLI generates:

src/main.tsx
import { createRoot, RejoiceProvider } from "rejoice-js";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <RejoiceProvider defaultTheme="light">
    <App />
  </RejoiceProvider>
);

Everything is imported from rejoice-js — React, components, hooks, styling, and state management. No second import source needed.

src/App.tsx
import {
  useState,
  Button,
  Card,
  styled,
  useTheme,
} from "rejoice-js";

const Wrapper = styled.div`
  padding: ${({ theme }) => theme.spacing(3)};
  background: ${({ theme }) => theme.colors.background};
`;

export default function App() {
  const [count, setCount] = useState(0);
  const { isDarkMode, toggleTheme } = useTheme();

  return (
    <Wrapper>
      <Card title="Hello, rejoice">
        <Button onClick={() => setCount(c => c + 1)}>
          Clicked {count} times
        </Button>
        <Button onClick={toggleTheme}>
          {isDarkMode ? "Light" : "Dark"} Mode
        </Button>
      </Card>
    </Wrapper>
  );
}

Project Structure

The scaffolded app follows a clean, minimal layout:

directory
my-app/
  index.html           # Bun HTML entry point
  package.json
  tsconfig.json        # jsxImportSource: "rejoice-js"
  src/
    main.tsx           # App bootstrap with RejoiceProvider
    App.tsx            # Root component
    App.styles.ts      # styled-components
    styled.d.ts        # Theme type augmentation
    store/
      appStore.ts      # Zustand store example
    router.tsx         # React Router config (if opted in)
    pages/             # Route pages (if opted in)

The tsconfig.json sets jsxImportSource: "rejoice-js", so JSX works without importing React. The library delegates to react/jsx-runtime internally.

RejoiceProvider

The root provider that wires up Ant Design theming, styled-components theme context, and the Zustand theme store. Wrap your app with this once.

tsx
import { createRoot, RejoiceProvider } from "rejoice-js";

createRoot(document.getElementById("root")!).render(
  <RejoiceProvider defaultTheme="dark">
    <App />
  </RejoiceProvider>
);

Props

Prop Type Default Description
children React.ReactNode Required Your application tree
defaultTheme "light" | "dark" "light" Initial theme mode if no persisted preference exists

What it does

  • Wraps your app with Ant Design's ConfigProvider — switching between defaultAlgorithm and darkAlgorithm based on theme mode
  • Wraps with styled-components ThemeProvider — injecting theme tokens into all styled components
  • Syncs with the Zustand theme store — so useTheme() and useThemeStore() work anywhere in the tree

Theme System

Rejoice ships a unified theme system that synchronizes Ant Design, styled-components, and Zustand. Theme preference is automatically persisted to localStorage under the key rejoice-theme-storage.

RejoiceProvider
Ant Design
ConfigProvider
styled-components
ThemeProvider
Zustand
themeStore
localStorage persistence

useTheme

The primary hook for reading and controlling the theme. Returns the current mode and actions to change it.

tsx
import { useTheme, Button, Switch } from "rejoice-js";

function ThemeToggle() {
  const { mode, isDarkMode, toggleTheme, setTheme } = useTheme();

  return (
    <div>
      <p>Current: {mode}</p>
      <Switch
        checked={isDarkMode}
        onChange={toggleTheme}
      />
      <Button onClick={() => setTheme("dark")}>
        Force Dark
      </Button>
    </div>
  );
}

Return value

Property Type Description
mode "light" | "dark" Current theme mode
isDarkMode boolean Convenience boolean — true when dark
toggleTheme () => void Flips between light and dark
setTheme (mode: ThemeMode) => void Sets a specific theme mode

useThemeStore

Direct access to the underlying Zustand store. Useful for selecting specific slices or subscribing outside React.

tsx
import { useThemeStore } from "rejoice-js";

// Select only what you need
const mode = useThemeStore((s) => s.mode);
const toggle = useThemeStore((s) => s.toggleTheme);

// Subscribe outside React
useThemeStore.subscribe((state) => {
  console.log("Theme changed:", state.mode);
});

The store uses Zustand's persist middleware. Theme preference survives page reloads automatically.

Theme Tokens

The styled-components theme object injected by RejoiceProvider. Access these in any styled component via props.theme.

typescript
{
  mode: "light" | "dark",
  isDark: boolean,
  colors: {
    background: "#ffffff"  | "#141414",
    surface:    "#f5f5f5"  | "#1f1f1f",
    text:       "rgba(0,0,0,0.88)"   | "rgba(255,255,255,0.85)",
    primary:    "#1677ff",
    border:     "#d9d9d9"  | "#303030",
  },
  spacing: (n: number) => string  // returns `${n * 8}px`
}

Usage in styled-components

tsx
import { styled } from "rejoice-js";

const Panel = styled.div`
  background: ${({ theme }) => theme.colors.surface};
  padding: ${({ theme }) => theme.spacing(2)};
  border: 1px solid ${({ theme }) => theme.colors.border};
  color: ${({ theme }) => theme.colors.text};
  border-radius: 8px;
`;

Type augmentation

The scaffolded app includes src/styled.d.ts to give you full autocomplete on theme tokens:

src/styled.d.ts
import "styled-components";

declare module "styled-components" {
  export interface DefaultTheme {
    mode: "light" | "dark";
    isDark: boolean;
    colors: {
      background: string;
      surface: string;
      text: string;
      primary: string;
      border: string;
    };
    spacing: (n: number) => string;
  }
}

Hooks

All standard React hooks are re-exported from rejoice-js so you never need to import from react directly.

useState State management
useEffect Side effects
useLayoutEffect Synchronous effects
useReducer Complex state logic
useRef Mutable references
useContext Context consumption
useMemo Memoized values
useCallback Memoized callbacks
useId Unique IDs
useTransition Non-blocking updates
useDeferredValue Deferred rendering
useSyncExternalStore External store sync
tsx
// All from one import
import {
  useState,
  useEffect,
  useRef,
  useMemo,
  useCallback,
} from "rejoice-js";

Utilities

React utilities and component helpers available from the same import.

memo Component memoization
lazy Code splitting
Suspense Async boundaries
StrictMode Development checks
Fragment Invisible wrapper
createElement JSX alternative
createContext Context creation
forwardRef Ref forwarding

React DOM

DOM rendering methods — use createRoot for your app entry point.

createRoot Mount React app to DOM
hydrateRoot Hydrate server-rendered HTML

JSX Runtime

Rejoice provides its own JSX runtime that delegates to React's. This enables zero-config JSX — no import React from "react" needed anywhere.

tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "rejoice-js"
  }
}

With this config, TypeScript and Bun automatically resolve JSX transforms from rejoice-js/jsx-runtime. You write JSX as usual and it just works.

Components

39+ Ant Design 5 components are pre-exported. They're automatically themed by RejoiceProvider — dark mode, primary color, and all token overrides apply with zero configuration.

Data Entry

Button Actions & triggers
Input Text input
Select Dropdown selection
Form Form wrapper & validation
DatePicker Date selection
Upload File uploads
Slider Range input
Switch Toggle boolean
Checkbox Multi-select
Radio Single-select

Data Display

Table Tabular data
Card Content container
Tabs Tabbed content
Badge Status indicator
Tag Labels & tags
Tooltip Hover info
Typography Text & headings
Avatar User avatar
List List layout
Progress Progress indicators

Overlay

Modal Dialog windows
Drawer Side panels
Dropdown Contextual menus

Navigation

Menu Navigation menus
Breadcrumb Page hierarchy
Steps Step-by-step flow
tsx
import { Button, Card, Table, Modal, Form, Input } from "rejoice-js";

// Use exactly like Ant Design — same props, same API.
// Theme is automatically applied by RejoiceProvider.

Layout & Grid

Ant Design's layout primitives for building page structure.

Layout Page structure (Header, Sider, Content, Footer)
Space Inline spacing
Row Flex row
Col Grid column
Divider Content separator

Feedback & Status

Components for communicating state and results to users.

Spin Loading spinner
Alert Status messages
Result Operation outcomes
Empty Empty state placeholder
Skeleton Loading placeholder
ConfigProvider Global config overrides

Ant Design Theme

The Ant Design theme algorithms are available as antdTheme for advanced use cases.

tsx
import { antdTheme, ConfigProvider } from "rejoice-js";

// Access Ant Design's theme algorithms
const { defaultAlgorithm, darkAlgorithm } = antdTheme;

// Override locally if needed
<ConfigProvider theme={{ algorithm: darkAlgorithm }}>
  <MySection />
</ConfigProvider>

API

styled-components v6 is re-exported for CSS-in-JS with full theme token access.

styled Create themed components
css CSS helper for interpolation
keyframes Define animations
createGlobalStyle Global CSS injection
tsx
import { styled, css, keyframes, createGlobalStyle } from "rejoice-js";

const fadeIn = keyframes`
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
`;

const Card = styled.div`
  background: ${({ theme }) => theme.colors.surface};
  padding: ${({ theme }) => theme.spacing(3)};
  border-radius: 12px;
  animation: ${fadeIn} 0.3s ease;
`;

const GlobalStyle = createGlobalStyle`
  body {
    background: ${({ theme }) => theme.colors.background};
    color: ${({ theme }) => theme.colors.text};
  }
`;

Theming

All styled components inside RejoiceProvider automatically receive the theme object. Use it for dark-mode-aware styling without any additional setup.

tsx
import { styled } from "rejoice-js";

const Header = styled.header`
  background: ${({ theme }) =>
    theme.isDark ? "rgba(255,255,255,0.04)" : "rgba(0,0,0,0.02)"};
  border-bottom: 1px solid ${({ theme }) => theme.colors.border};
  padding: ${({ theme }) => theme.spacing(2)} ${({ theme }) => theme.spacing(3)};
`;

// theme.isDark is a boolean — use it for conditional styles
// theme.spacing(n) returns `${n * 8}px` — use it for consistent spacing

Store API

Zustand v4 is re-exported for lightweight state management. Create stores with zero boilerplate.

create Create a React-bound store
createStore Create a vanilla store
useShallow Shallow equality selector
store/counterStore.ts
import { create } from "rejoice-js";

interface CounterState {
  count: number;
  increment: () => void;
  decrement: () => void;
}

export const useCounter = create<CounterState>((set, get) => ({
  count: 0,
  increment: () => set({ count: get().count + 1 }),
  decrement: () => set({ count: get().count - 1 }),
}));
tsx
import { useShallow } from "rejoice-js";
import { useCounter } from "./store/counterStore";

// useShallow prevents re-renders when unrelated state changes
const { count, increment } = useCounter(
  useShallow((s) => ({ count: s.count, increment: s.increment }))
);

Middleware

Three Zustand middleware functions are pre-exported for common patterns.

persist Persist state to localStorage
devtools Redux DevTools integration
subscribeWithSelector Fine-grained subscriptions
store/settingsStore.ts
import { create, persist, devtools } from "rejoice-js";

interface Settings {
  language: string;
  notifications: boolean;
  setLanguage: (lang: string) => void;
  toggleNotifications: () => void;
}

export const useSettings = create<Settings>()(
  devtools(
    persist(
      (set, get) => ({
        language: "en",
        notifications: true,
        setLanguage: (lang) => set({ language: lang }),
        toggleNotifications: () =>
          set({ notifications: !get().notifications }),
      }),
      { name: "settings-storage" }
    )
  )
);

Type Exports

TypeScript types exported for use in your application code.

typescript
import type {
  ThemeMode,
  ThemeState,
  RejoiceProviderProps,
  DefaultTheme,
} from "rejoice-js";
Type Definition Description
ThemeMode "light" | "dark" Union type for theme modes
ThemeState { mode, isDarkMode, toggleTheme, setTheme } Shape of the theme store
RejoiceProviderProps { children, defaultTheme? } Props for RejoiceProvider
DefaultTheme styled-components theme interface Re-exported from styled-components for type augmentation