Skip to main content
Sabo ships with a rich set of UI components built with React 19, Tailwind CSS v4, and shadcn/ui + Magic UI primitives.
  • Import pattern: @/components/ui/...
  • Browse components from the sidebar on the left (Base, Forms, Overlays, Data Display, Feedback, Navigation, Magic UI).
  • For page-level sections (Hero, Pricing, etc.), see Marketing pages under Customization Guides → Marketing Pages.
Add components via shadcn/ui and Magic UI (preconfigured)
This boilerplate is set up to add components using the shadcn CLI. The Magic UI registry is also configured in components.json, so you can install effects/animations the same way. Beyond what’s included out-of-the-box, you can browse the registries and add the components you prefer.
  • shadcn/ui (base components)
    npx shadcn@latest add button
    
  • Magic UI (animated/visual effects)
    npx shadcn@latest add -r @magicui animated-beam
    
Files are generated in sabo/src/components/ui/. Import with @/components/ui/… and tweak styles to match your theme.

Global Setup (once)

Most components are import-and-use. For global behavior, set these once near the app root:
1

Theme & Styling

  • Already wired with next-themes and Tailwind v4 tokens (colors, radius, spacing).
  • Use className to extend/override per-component styles.
  • Learn more: Styling, Dark Mode, Responsive
2

Toast (Sonner)

  • A “toast” is a brief, non‑blocking notification that overlays the UI and disappears automatically.
  • You must render a single <Toaster /> near the app root; otherwise toast.*(...) calls won’t show.
sabo/src/app/layout.tsx
// Add Toaster once at the root so any component can display toasts
import { Toaster } from "sonner";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster richColors position="top-right" />
      </body>
    </html>
  );
}

Quick Start

Follow these basics with any UI component:
  • Import from @/components/ui/...
  • Pick a variant/size when available (preferred for most styling)
  • Use className only for small, last‑mile tweaks (spacing, minor radius, one‑off layout)
sabo/src/components/ui/button.tsx
import { Button } from "@/components/ui/button";

export function Example() {
  return (
    <>
      {/* Variants and sizes are the first choice for styling */}
      <Button /* default variant="default" size="default" */>Primary</Button>
      <Button
        variant="secondary"     // use built-in visual styles first
        size="sm"               // use size tokens before custom CSS
        className="ml-2"        // then do tiny tweaks via className (e.g., margin)
      >
        Secondary
      </Button>
    </>
  );
}

Conventions

Server vs Client Components

All components are Server Components by default (RSC). Add "use client" at the top of a file only when you need:
  • React hooks (useState, useEffect, useRef, etc.)
  • Browser APIs (window, document, localStorage, etc.)
  • Event handlers (onClick, onChange, onSubmit, etc.)
Prefer Server Components for static/async rendering and zero client JS. Isolate interactivity into small client components instead of marking large trees as client.
"use client"

import { Button } from "@/components/ui/button";
import { useState } from "react";

export function CounterButton() {
  const [count, setCount] = useState(0);
  return (
    <Button onClick={() => setCount((c) => c + 1)}>
      Clicked {count} times
    </Button>
  );
}