Installation
Vite
Wavi in a Vite + React app: no Next.js needed. This guide is the one we test against: Vite 8, React 19, Tailwind CSS 4.
Requirements
Create the app
$ npx create-vite@latest my-app --template react-tsInstall the package and Tailwind CSS
$ npm install @waveinno/ui$ npm install -D tailwindcss @tailwindcss/viteThe package does not need Next.js: its only Next.js part lives in
@waveinno/ui/next, which a Vite app never imports.Add Tailwind to Vite
vite.config.tsimport tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [react(), tailwindcss()], });Add the styles
Replace everything in
src/index.css(Vite's starter styles fight the tokens), and deletesrc/App.css.src/index.css@import "tailwindcss"; @import "@waveinno/ui/styles.css"; /* Tailwind only generates classes it can see: point it at the package. */ @source "../node_modules/@waveinno/ui/dist"; /* Noto Sans Bengali after each family: it carries Bangla and the Taka sign (৳). */ :root { --font-inter: "Inter", "Noto Sans Bengali"; --font-jakarta: "Plus Jakarta Sans", "Noto Sans Bengali"; --font-jetbrains: "JetBrains Mono"; }Fonts and the theme script
In
index.html: the font families, and the script that applies a saved dark theme before the first paint. Prefer self-hosting? Install@fontsource-variable/interand friends and import them inmain.tsxinstead of the links.index.html<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My app</title> <!-- The four families of the design system. --> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@500;600;700&family=JetBrains+Mono:wght@400;500&family=Noto+Sans+Bengali:wght@400;500;600;700&display=swap" /> <!-- Applies the saved light/dark theme and density before first paint: no flash. The same script as themeBootScript() from "@waveinno/ui". --> <script>(function(){try{var t=localStorage.getItem("waveinno.theme");if(t!=="light"&&t!=="dark")t=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";var d=localStorage.getItem("waveinno.density")||"default";var e=document.documentElement;e.setAttribute("data-theme",t);e.setAttribute("data-density",d);e.style.colorScheme=t;if(localStorage.getItem("waveinno.panel")==="1")e.setAttribute("data-panel-collapsed","");}catch(_){}})();</script> </head> <body class="bg-canvas text-body antialiased"> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> </body> </html>Add the providers
src/main.tsximport { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { Toaster, TooltipProvider } from "@waveinno/ui"; import App from "./App"; import "./index.css"; createRoot(document.getElementById("root")!).render( <StrictMode> <TooltipProvider delayDuration={300}> <App /> <Toaster /> </TooltipProvider> </StrictMode>, );Use the components
src/App.tsximport { useState } from "react"; import { Alert, Button, DatePicker, Menu, MenuItem, ThemeToggle, toast } from "@waveinno/ui"; export default function App() { const [day, setDay] = useState(""); // an ISO day, "" for none return ( <div className="flex min-h-dvh gap-8 p-8"> <Menu variant="raised" defaultValue="overview" className="w-60"> <MenuItem value="overview">Overview</MenuItem> <MenuItem value="sessions" count={12}>Sessions</MenuItem> <MenuItem value="settings">Settings</MenuItem> </Menu> <main className="max-w-md flex-1 space-y-4"> <div className="flex items-center justify-between"> <h1 className="disp text-2xl font-bold">Hello, WAVEINNO</h1> <ThemeToggle /> </div> <Alert tone="success" title="Running on Vite" /> <DatePicker value={day} onChange={setDay} /> <Button variant="primary" onClick={() => toast.success("Saved")}>Save</Button> </main> </div> ); }$ npx vite
With React Router#
Without a router adapter, links in the package are plain <a> tags: they work, with a full page load. With React Router, hand the package its Link, location and navigate once, and the shell, breadcrumbs and command palette navigate in place. See Routing for the details.
import { Link, useLocation, useNavigate } from "react-router";
import { RouterProvider, type RouterAdapter } from "@waveinno/ui";
// Tell the package to link and navigate through React Router.
const reactRouter: RouterAdapter = {
Link: ({ href, ...props }) => <Link to={href} {...props} />,
usePathname: () => useLocation().pathname,
useNavigate: () => {
const navigate = useNavigate();
return (href) => navigate(href);
},
};
// Inside <BrowserRouter> (or your data router's root route):
export function UiRouter({ children }: { children: React.ReactNode }) {
return <RouterProvider adapter={reactRouter}>{children}</RouterProvider>;
}Troubleshooting#
- Components look unstyled. The
@sourcepath must be relative tosrc/index.cssand reachnode_modules/@waveinno/ui/dist; in a monorepo it may need more../. - Odd margins, centred text, purple links. Vite's starter
index.cssorApp.cssis still loaded. - “Could not resolve next/link”. Something imports
@waveinno/ui/next; in Vite, useRouterProviderinstead. - A white flash before dark mode. The theme script is missing from
index.html, or runs after the stylesheet.