Next.js
Add Vamos UI to a Next.js App Router project. About five minutes.
Requirements: React 19, Tailwind CSS v4, Node 20 or newer. Next.js 15 is what the docs site itself runs on.
Steps
- Create an app with Tailwind v4
pnpm create next-app@latest my-panel --typescript --tailwind --app --no-src-dircreate-next-appsets up Tailwind v4 with@tailwindcss/postcss. In an existing app, check thattailwindcssis version 4 andglobals.cssstarts with@import "tailwindcss". - Install the kit
pnpm add @vamos-ui/reactThe npm package is compiled ESM with type declarations and keeps the
"use client"directives, so notranspilePackagesentry is needed.Building with webpack (the Next.js 15 default, or
next build --webpack)? Addexperimental: { optimizePackageImports: ["@vamos-ui/react"] }tonext.config.ts. Without it, a Server Component that imports one component from the package root ships every client component of the kit. Turbopack builds only ship what you import. - Import the stylesapp/globals.css
@import "tailwindcss"; @import "@vamos-ui/react/styles.css";Keep the order: Tailwind first, then the kit tokens. Your own CSS goes after both.
No @source line neededImporting@vamos-ui/react/styles.cssaftertailwindcssis enough: Tailwind v4 also scans the package the stylesheet comes from, so the classes used inside the components are generated. - Load the fonts
data-fontpresets read--ff-*variables. Load the families of the presets you use withnext/font; this is the default editorial preset:// app/fonts.ts import { Instrument_Serif, JetBrains_Mono, Onest } from "next/font/google"; // The "editorial" preset (the default). Each font exposes the --ff-* variable the tokens read. const onest = Onest({ subsets: ["latin", "latin-ext"], variable: "--ff-onest" }); const instrument = Instrument_Serif({ subsets: ["latin", "latin-ext"], weight: "400", style: ["normal", "italic"], variable: "--ff-instrument" }); const jetbrains = JetBrains_Mono({ subsets: ["latin", "latin-ext"], variable: "--ff-jetbrains" }); export const fontVariables = [onest, instrument, jetbrains].map((f) => f.variable).join(" "); - Set the theme axes on <html>
The six axes are attributes on
<html>(or any element):class="dark",data-material="glass|solid",data-accent="lime|ocean|ember|rose|graphite",data-density="comfortable|compact",data-font="editorial|grotesk|corporate|system"and, optionally,data-radius="sharp|round"for corners. The boot script reads the user's saved choice fromlocalStorage(keyvamos-theme) before the first paint. SeeThemingfor every token.// app/layout.tsx import "./globals.css"; import { fontVariables } from "./fonts"; import { Providers } from "./providers"; const boot = `try{var s=JSON.parse(localStorage.getItem("vamos-theme")||"{}");var h=document.documentElement;if(s.dark)h.classList.add("dark");h.dataset.material=s.material||"glass";h.dataset.accent=s.accent||"lime";h.dataset.density=s.density||"comfortable";h.dataset.font=s.font||"editorial"}catch(e){}`; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en" data-material="glass" data-accent="lime" data-density="comfortable" data-font="editorial" className={fontVariables} suppressHydrationWarning > <head> <script dangerouslySetInnerHTML={{ __html: boot }} /> </head> <body className="min-h-dvh font-sans"> <Providers>{children}</Providers> </body> </html> ); }suppressHydrationWarningis needed because the boot script changes the attributes before React hydrates. - Connect the router (recommended)
The kit never imports
next/*. Hand itLink,Imageand the router once, so sidebars, breadcrumbs and command palettes navigate client-side.TooltipProvidermakes icon buttons show real tooltips instead of atitle.// app/providers.tsx "use client"; import Image from "next/image"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import { TooltipProvider } from "@vamos-ui/react"; import { createVamosNextProvider } from "@vamos-ui/react/next"; // Kit links, images and navigate() use the Next.js router. const VamosNext = createVamosNextProvider({ Link, Image, useRouter, usePathname }); export function Providers({ children }: { children: React.ReactNode }) { return ( <VamosNext locale="en-US"> <TooltipProvider>{children}</TooltipProvider> </VamosNext> ); } - Use a component
// app/page.tsx import { Badge, Button, Card, CardBody, CardHeader } from "@vamos-ui/react"; export default function Welcome() { return ( <main className="mx-auto grid max-w-xl gap-4 p-8"> <Card> <CardHeader title="Spring campaign" description="Newsletter · 12,480 recipients" action={<Badge tone="success" dot>Scheduled</Badge>} /> <CardBody className="flex gap-2"> <Button variant="primary">Publish</Button> <Button>Preview</Button> </CardBody> </Card> </main> ); }pnpm dev
With the CLI instead
Prefer owning the source? Instead of the npm package, let the CLI copy the kit into your project. It detects the framework, Tailwind v4 and your package manager, writes vamos.json, adds the stylesheet import to app/globals.css and prints the <html> attributes:
npx vamos-ui init
npx vamos-ui add button card badgeComponents land in components/vamos (or src/components/vamos) and are imported from your alias, for example @/components/vamos. Every written file is recorded in .vamos-lock.json, so npx vamos-ui diff and upgrade keep your edits apart from kit updates.
Notes
"use client" render on the server. Interactive ones are client components already. Don't pass functions (event handlers, render props) from a server component into a client part: wrap that wiring in your own "use client" file.Other fonts: see Fonts for the grotesk, corporate and system presets and Google Fonts pairings. Turkish and other locales: Localization.