Installation
Next.js
From an empty folder to WAVEINNO components on screen, in the App Router. Every step is needed once per project; after that, import and use.
Requirements
Create the app
Answer “Yes” to TypeScript, Tailwind CSS and the App Router. Tailwind CSS 4 is what create-next-app sets up today.
$ npx create-next-app@latest my-app --ts --tailwind --app --eslintInstall the package
$ npm install @waveinno/uiIt brings its own Radix primitives, icons (
lucide-react), charts and table engine.react,react-dom,nextandtailwindcssare yours, so there is only ever one copy of each.Add the styles
Replace the contents of
app/globals.css. The package's stylesheet holds the design tokens (colours, radius, density, motion) for light and dark; the@sourceline lets Tailwind generate the classes the components use.app/globals.css@import "tailwindcss"; @import "@waveinno/ui/styles.css"; /* Tailwind only generates classes it can see. Point it at the package, relative to this file: "../node_modules" for app/globals.css, "../../node_modules" for src/app/globals.css. */ @source "../node_modules/@waveinno/ui/dist"; /* The fonts loaded in layout.tsx, in the package's font slots. Noto Sans Bengali carries Bangla and the Taka sign (৳), which Inter lacks. */ :root { --font-inter: var(--font-body), var(--font-bangla); --font-jakarta: var(--font-display), var(--font-bangla); --font-jetbrains: var(--font-code); }Add the providers
One client file, used once in the layout.
NextRouterProvidermakes every link in the package (the shell's rail, breadcrumbs, the command palette) a Next.js link, with prefetching and no full page loads.app/providers.tsx"use client"; import { Toaster, TooltipProvider } from "@waveinno/ui"; import { NextRouterProvider } from "@waveinno/ui/next"; /** Once, around the whole app: Next.js routing for the package, tooltips, toasts. */ export function Providers({ children }: { children: React.ReactNode }) { return ( <NextRouterProvider> <TooltipProvider delayDuration={300}> {children} <Toaster /> </TooltipProvider> </NextRouterProvider> ); }Set up the layout: fonts, theme, providers
next/fontself-hosts the four families the system uses (see Typography).themeBootScript()runs before the first paint, so a dark-mode user never sees a white flash.app/layout.tsximport type { Metadata } from "next"; import { Inter, JetBrains_Mono, Noto_Sans_Bengali, Plus_Jakarta_Sans } from "next/font/google"; import { themeBootScript } from "@waveinno/ui"; import "./globals.css"; import { Providers } from "./providers"; const body = Inter({ subsets: ["latin"], variable: "--font-body" }); const display = Plus_Jakarta_Sans({ subsets: ["latin"], weight: ["500", "600", "700"], variable: "--font-display" }); const code = JetBrains_Mono({ subsets: ["latin"], variable: "--font-code" }); const bangla = Noto_Sans_Bengali({ subsets: ["bengali"], variable: "--font-bangla" }); export const metadata: Metadata = { title: "My app" }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( // The boot script sets data-theme before React runs, so the attribute // differs from the server's HTML on purpose. <html lang="en" suppressHydrationWarning className={[body.variable, display.variable, code.variable, bangla.variable].join(" ")}> <head> {/* Applies the saved light/dark theme and density before first paint: no flash. */} <script dangerouslySetInnerHTML={{ __html: themeBootScript() }} /> </head> <body className="bg-canvas text-body antialiased"> <Providers>{children}</Providers> </body> </html> ); }Use a component
Components are client components already (they carry
"use client"), so they work straight from a server page.app/page.tsximport { Alert, Button, StatCard } from "@waveinno/ui"; export default function Home() { return ( <main className="mx-auto max-w-3xl space-y-6 p-8"> <h1 className="disp text-3xl font-bold">Hello, WAVEINNO</h1> <div className="grid gap-4 sm:grid-cols-2"> <StatCard label="Revenue today" value="48,250" currency delta={12.4} variant="gradient" /> <StatCard label="Cars inside" value="312" delta={6.2} /> </div> <Alert tone="success" title="It works">The components, the tokens and the fonts are in.</Alert> <Button variant="primary">Get started</Button> </main> ); }$ npx next dev
Optional: the application shell#
Products built on WAVEINNO (ParkingOS, the Admin portal) run inside AppShell: the icon rail, the module panel, the top bar, ⌘K and the record drawer. It needs the providers from step 4 and a config describing your apps and modules.
"use client";
import { AppShell, type ShellConfig } from "@waveinno/ui";
// Apps, modules and their screens: the rail, the panel and ⌘K are drawn from this.
const config: ShellConfig = {
apps: [/* { id, name, icon, tint, href, … } */],
modules: [/* { key, app, title, label, icon, href, nav: [ … ] } */],
user: { name: "Karim Ahmed", email: "karim@example.com", role: "Owner" },
};
export function Shell({ children }: { children: React.ReactNode }) {
return <AppShell config={config}>{children}</AppShell>;
}Troubleshooting#
- Components look unstyled or half-styled. The
@sourcepath is wrong for where your CSS file lives. It is relative to the CSS file, and must reachnode_modules/@waveinno/ui/dist. - A white flash before dark mode. The boot script is missing from
<head>, orsuppressHydrationWarningis missing from<html>. - “৳” shows as a box, or Bangla falls back to a system font. Noto Sans Bengali is not loaded, or not in the font slots in globals.css.
- Tooltips never open.
TooltipProvideris missing; it lives in the providers file. - Shell links reload the whole page.
NextRouterProvideris missing; without it the package falls back to plain links.