Installation

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

  1. Create an app with Tailwind v4
    pnpm create next-app@latest my-panel --typescript --tailwind --app --no-src-dir

    create-next-app sets up Tailwind v4 with @tailwindcss/postcss. In an existing app, check that tailwindcss is version 4 and globals.css starts with @import "tailwindcss".

  2. Install the kit
    pnpm add @vamos-ui/react

    The npm package is compiled ESM with type declarations and keeps the "use client" directives, so no transpilePackages entry is needed.

    Building with webpack (the Next.js 15 default, or next build --webpack)? Add experimental: { optimizePackageImports: ["@vamos-ui/react"] } to next.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.

  3. Import the styles
    app/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 needed
    Importing @vamos-ui/react/styles.css after tailwindcss is enough: Tailwind v4 also scans the package the stylesheet comes from, so the classes used inside the components are generated.
  4. Load the fonts

    data-font presets read --ff-* variables. Load the families of the presets you use with next/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(" ");
  5. 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 from localStorage (key vamos-theme) before the first paint. See Theming for 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>
      );
    }

    suppressHydrationWarning is needed because the boot script changes the attributes before React hydrates.

  6. Connect the router (recommended)

    The kit never imports next/*. Hand it Link, Image and the router once, so sidebars, breadcrumbs and command palettes navigate client-side. TooltipProvider makes icon buttons show real tooltips instead of a title.

    // 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>
      );
    }
  7. 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 badge

Components 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

Server components
Display components without "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.