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