Proper UI

Vite integration

Set up a Vite + React project with Proper UI in minutes — the Tailwind Vite plugin, path aliases, and provider wiring included.

Vite has no framework-specific router or server/client split to work around, which makes it the simplest target to wire up by hand — but the CLI still saves you the boilerplate.

Installation

Run this from the root of your Vite project:

npx @properui/cli@latest init --vite

It detects your Tailwind version, src/ layout, and package manager, then writes components.json, the theme token file, utils/cx.ts, adds the Tailwind imports and @source line to your CSS file, and wraps your app entry in ThemeProvider. It doesn't install npm packages, touch vite.config.ts, or add RouterProvider — set those up yourself as shown below.

Manual Installation

1. Install the package and the Tailwind Vite plugin

npm install @properui/ui react-aria-components next-themes
npm install -D @tailwindcss/vite

2. Register the Tailwind plugin and the @/ alias

// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import path from "node:path";
import { defineConfig } from "vite";

export default defineConfig({
    plugins: [react(), tailwindcss()],
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "./src"),
        },
    },
});

Match the alias in tsconfig.json so your editor and type-checker resolve it too:

{
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "@/*": ["./src/*"]
        }
    }
}

3. Import the stylesheet

@properui/ui/styles/globals.css already bundles the Tailwind import, the design tokens, typography, and every plugin the components need — you only add one @source line so Tailwind's scanner also reads the package's own class names:

/* src/index.css */
@import "@properui/ui/styles/globals.css";

@source "../node_modules/@properui/ui/src/**/*.{ts,tsx}";

4. Wire up the providers

@properui/ui/providers only exports a Next.js-flavored RouterProvider (it reads useRouter from next/navigation internally), so it doesn't work in Vite. Use React Aria's own RouterProvider from react-aria-components instead, and hand it your router's navigate function — useNavigate if you're using react-router:

// src/providers.tsx
import type { ReactNode } from "react";
import { RouterProvider } from "react-aria-components";
import { useNavigate } from "react-router-dom";
import { ThemeProvider } from "@properui/ui/providers";

export const Providers = ({ children }: { children: ReactNode }) => {
    const navigate = useNavigate();

    return (
        <ThemeProvider>
            <RouterProvider navigate={navigate}>{children}</RouterProvider>
        </ThemeProvider>
    );
};
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { App } from "./app";
import "./index.css";
import { Providers } from "./providers";

createRoot(document.getElementById("root")!).render(
    <StrictMode>
        <BrowserRouter>
            <Providers>
                <App />
            </Providers>
        </BrowserRouter>
    </StrictMode>,
);

No router at all? Drop RouterProvider entirely. Every component still renders and behaves correctly — href links just do a full page load instead of a client-side transition.

Using Proper UI components

Import a component from its subpath, the same one the CLI writes when you run add:

import { Button } from "@properui/ui/components/base/buttons/button";

export const App = () => <Button size="md">Get started</Button>;

Theme toggle

next-themes works outside of Next.js too — it only needs localStorage and a document, both of which a Vite SPA has:

// src/components/theme-toggle-button.tsx
import { useTheme } from "next-themes";
import { Button } from "@properui/ui/components/base/buttons/button";

export const ThemeToggleButton = () => {
    const { resolvedTheme, setTheme } = useTheme();

    return <Button onPress={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}>Toggle theme</Button>;
};

FAQs