Installation

React Router

Add Vamos UI to a React Router v7 app in framework mode. Remix v2 apps follow the same steps after the upgrade to React Router v7.

Requirements: React 19, Tailwind CSS v4, Node 20 or newer.

Steps

  1. Create the app
    pnpm create react-router@latest my-panel
    cd my-panel

    The default template already uses Tailwind v4 through @tailwindcss/vite and has app/app.css. In an existing app, add it with pnpm add -D tailwindcss @tailwindcss/vite and tailwindcss() in vite.config.ts.

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

    Replace the template's app/app.css (its @theme font and the html, body background rules would override the tokens):

    app/app.css
    @import "tailwindcss";
    @import "@vamos-ui/react/styles.css";
    
    /* The data-font="editorial" preset reads these variables. */
    :root {
      --ff-onest: "Onest";
      --ff-instrument: "Instrument Serif";
      --ff-jetbrains: "JetBrains Mono";
    }
    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. Link adapter for the router

    The kit renders plain <a href> by default. This adapter maps its link props onto React Router's Link and gives it a navigate():

    // app/vamos-router.tsx
    import * as React from "react";
    import { Link, useNavigate } from "react-router";
    import type { VamosLinkProps, VamosProviderProps } from "@vamos-ui/react";
    
    /** Kit links (sidebar, breadcrumb, pagination…) become React Router links. */
    export const RouterLink = React.forwardRef<HTMLAnchorElement, VamosLinkProps>(function RouterLink(
      { href, prefetch, replace, ...props },
      ref,
    ) {
      return <Link ref={ref} to={href} replace={replace} prefetch={prefetch ? "intent" : "none"} {...props} />;
    });
    
    /** navigate() for the kit (command palette, row clicks). */
    export function useVamosNavigate(): NonNullable<VamosProviderProps["navigate"]> {
      const navigate = useNavigate();
      return React.useCallback(
        (href, options) => navigate(href, { replace: options?.replace, preventScrollReset: options?.scroll === false }),
        [navigate],
      );
    }
  5. Theme attributes, boot script, fonts and providers in root.tsx

    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.

    The page is server-rendered, so the boot script goes in Layout's <head> and suppressHydrationWarning keeps React quiet about the attributes it sets. The providers wrap the Outlet, because Layout also renders the error boundary.

    // app/root.tsx
    import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
    import type { Route } from "./+types/root";
    import { TooltipProvider, VamosProvider } from "@vamos-ui/react";
    import { RouterLink, useVamosNavigate } from "./vamos-router";
    import "./app.css";
    
    export const links: Route.LinksFunction = () => [
      { rel: "preconnect", href: "https://fonts.googleapis.com" },
      { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" },
      { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Onest:wght@100..900&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@100..800&display=swap" },
    ];
    
    // Restore the saved theme axes before the first paint.
    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 function Layout({ children }: { children: React.ReactNode }) {
      return (
        <html
          lang="en"
          data-material="glass"
          data-accent="lime"
          data-density="comfortable"
          data-font="editorial"
          suppressHydrationWarning
        >
          <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <script dangerouslySetInnerHTML={{ __html: boot }} />
            <Meta />
            <Links />
          </head>
          <body className="min-h-dvh font-sans">
            {children}
            <ScrollRestoration />
            <Scripts />
          </body>
        </html>
      );
    }
    
    export default function App() {
      const navigate = useVamosNavigate();
      return (
        <VamosProvider locale="en-US" Link={RouterLink} navigate={navigate}>
          <TooltipProvider>
            <Outlet />
          </TooltipProvider>
        </VamosProvider>
      );
    }
  6. Use a component
    // app/routes/home.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/app.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.

Set the paths for the app/ folder
React Router keeps code in app/ with the ~/* alias. After init, set "uiDir": "app/components/vamos", "blocksDir": "app/components/blocks" and "importAlias": "~/components/vamos" in vamos.json before the first add.

Remix v2

Remix v2 continues as React Router v7. Upgrade with the official Remix upgrade guide first, then follow the steps above: the imports change from @remix-run/react to react-router, and everything kit-related is identical.