# 9UI Documentation Beautiful, customizable components built with Base UI and Tailwind CSS. This is a curated set of components that you can customize to fit your style. You won't install it through npm - instead, you simply select the components you need and add them directly to your project. ## Getting Started ### Astro Setting up dark mode in your Astro project. **Create script to handle theme.** ```astro {5-28} title="src/pages/index.astro" --- import '../styles/global.css'; --- {/* content */} ``` **Create theme utilies.** Create a utility to handle theme management. ```ts title="src/utils/theme.ts" export type Theme = "light" | "dark" | "system" export function getTheme(): Theme { if (typeof localStorage !== "undefined" && localStorage.getItem("theme")) { return localStorage.getItem("theme") as Theme } return "system" } export function setTheme(theme: Theme) { const isDark = theme === "dark" || (theme === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches) document.documentElement.classList[isDark ? "add" : "remove"]("dark") } ``` **Add theme toggle component.** ```tsx title="src/components/theme-toggle.tsx" import * as React from "react" import { MoonIcon, SunIcon } from "lucide-react" import { getTheme, setTheme, type Theme } from "../utils/theme" import { Button } from "./ui/button" export function ThemeToggle() { const [theme, setCurrentTheme] = React.useState(() => getTheme()) const toggleTheme = () => { const newTheme = theme === "dark" ? "light" : "dark" setTheme(newTheme) setCurrentTheme(newTheme) } return ( ) } ``` **Use the theme toggle in your app** You can now use the theme toggle component anywhere in your app. ```astro title="src/components/Header.astro" --- import { ThemeToggle } from "./theme-toggle" ---
``` --- ### Changelog Latest Updates ## 27/12/2025 - **Important:** All components now use Base UI v1.0.0. - Component refactoring and cleanup - Fixed some issues with the component states (like disabled, hovered, focused, etc.) ## 16/12/2025 - Updated Base UI to 1.0.0-beta.7 - Updated \[`Button`] component. It uses native Base UI button component. - Code style refactoring ## 11/11/2025 - Updated [`Combobox`](/docs/components/combobox) component. It uses native Base UI combobox component. - Added [`Autocomplete`](/docs/components/autocomplete) component - Updated Base UI to 1.0.0-beta.3 ## 09/08/2025 - Added [`multiple select example`](/docs/components/select#multiple) - Added [`LLMs`](/docs/getting-started/llms) documentation page with AI-friendly formats - Added search functionality with command dialog (⌘K) - Added manual installation documentation - Updated Base UI to latest version (1.0.0-beta.2) - Fixed registry build to support shadcn monorepo setup ## 22/07/2025 - Added [`Navigation Menu`](/docs/components/navigation-menu) component - Added [`Number Field`](/docs/components/number-field) component - Added [`Pagination`](/docs/components/pagination) component - Added [`Toast`](/docs/components/toast) component ## 30/06/2025 - New version! 🎉 - Compatible with Tailwind 4 and React 19 ## 02/05/2025 - Added [`Checkbox Group`](/docs/components/checkbox-group) component - Added [`Meter`](/docs/components/meter) component ## 21/03/2025 - Added [`Toolbar`](/docs/components/toolbar) component - Added [`useRender`](https://base-ui.com/react/utils/use-render) hook to button component that overrides the default rendered element ## 23/02/2025 - Added examples for compose components [`Combobox`](/docs/components/combobox) and [`Date Picker`](/docs/components/date-picker) ## 18/02/2025 - Added [`Chart`](/docs/components/chart) component ## 08/02/2025 - Initial components release ## 31/12/2024 - Project started 🎉 --- ### Dark Mode Setting up dark mode in your project. [Next.js](/docs/getting-started/dark-mode/next) [Vite](/docs/getting-started/dark-mode/vite) [Remix](/docs/getting-started/dark-mode/remix) [Astro](/docs/getting-started/dark-mode/astro) --- ### Installation Get started with 9ui by installing dependencies and setting up your project. ### Quick Setup **Create a new project** ```bash title="Terminal" npx shadcn@latest init https://9ui.dev/r/init.json ``` or you can follow the [shadcn installation guide](https://ui.shadcn.com/docs/installation) if your framework doesn't support `shadcn init`. **Add 9ui registry to your ** ```json title="components.json" { // existing configuration "registries": { "@9ui": "https://9ui.dev/r/{name}.json" } } ``` **Initialize MCP** This step adds the necessary configuration to allow you to use the components directly in your code with AI assistance. The AI will have full context of the entire 9ui registry. ```bash title="Terminal" npx shadcn@latest mcp init ``` **Add the root wrapper to your layout** Add the `root` class to your app layout to ensure proper styling isolation and theme application. ```tsx {2} title="layout.tsx"
{children}
``` **Start using 9ui components** Your project is now ready! You can start installing and using 9ui components in your application. ### Manual Setup **Install required dependencies** Install the core dependencies needed for 9ui components to function properly. ```bash title="Terminal" npm install tw-animate-css @base-ui/react tailwind-merge clsx lucide-react class-variance-authority ``` **Set up utility functions** Create a utilities file that provides essential helper functions for component styling and class management. ```ts title="src/lib/utils.ts" import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } ``` **Configure your global styles** Add the 9ui theme configuration and styling to your global CSS file. This includes color variables, custom properties, and base styles. ```css title="globals.css" @import "tailwindcss"; @import "tw-animate-css"; @custom-variant dark (&:is(.dark *)); @theme inline { --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --font-mono: "Geist Mono", "ui-monospace", "SFMono-Regular", "Menlo", "Monaco", "Consolas", '"Liberation Mono"', '"Courier New"', "monospace"; --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 2px); --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); --color-danger: var(--danger); --color-danger-foreground: var(--danger-foreground); --color-danger-border: var(--danger-border); --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); --color-warning-border: var(--warning-border); --color-info: var(--info); --color-info-foreground: var(--info-foreground); --color-info-border: var(--info-border); --color-success: var(--success); --color-success-foreground: var(--success-foreground); --color-success-border: var(--success-border); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); } :root { --background: oklch(98.5% 0 0); --foreground: oklch(14.5% 0 0); --card: oklch(97% 0 0); --card-foreground: oklch(14.5% 0 0); --popover: oklch(97% 0 0); --popover-foreground: oklch(14.5% 0 0); --primary: oklch(14.5% 0 0); --primary-foreground: oklch(98.5% 0 0); --secondary: oklch(92.2% 0 0); --secondary-foreground: oklch(20.5% 0 0); --muted: oklch(92.2% 0 0); --muted-foreground: oklch(43.9% 0 0); --accent: oklch(92.2% 0 0); --accent-foreground: oklch(14.5% 0 0); --destructive: oklch(50.5% 0.213 27.518); --destructive-foreground: oklch(98.5% 0 0); --border: oklch(87% 0 0); --input: oklch(97% 0 0); --ring: oklch(70.8% 0 0); --danger: oklch(96.6% 0.016 16.278); --danger-foreground: oklch(57.9% 0.237 29.233); --danger-border: oklch(93.3% 0.033 16.63); --warning: oklch(99% 0.016 96.37); --warning-foreground: oklch(66.9% 0.16 56.73); --warning-border: oklch(94.4% 0.078 96.97); --info: oklch(97.4% 0.012 244.25); --info-foreground: oklch(56.2% 0.182 255.12); --info-border: oklch(92.7% 0.032 265.82); --success: oklch(97.9% 0.023 158.94); --success-foreground: oklch(55% 0.165 146.61); --success-border: oklch(94.1% 0.078 158.88); --chart-1: oklch(48.8% 0.243 264.376); --chart-2: oklch(52.7% 0.154 150.069); --chart-3: oklch(49.6% 0.265 301.924); --chart-4: oklch(55.3% 0.195 38.402); --chart-5: oklch(51.4% 0.222 16.935); --radius: 0.625rem; } .dark { --background: oklch(14.5% 0 0); --foreground: oklch(98.5% 0 0); --card: oklch(20.5% 0 0); --card-foreground: oklch(98.5% 0 0); --popover: oklch(20.5% 0 0); --popover-foreground: oklch(98.5% 0 0); --primary: oklch(98.5% 0 0); --primary-foreground: oklch(14.5% 0 0); --secondary: oklch(26.9% 0 0); --secondary-foreground: oklch(97% 0 0); --muted: oklch(26.9% 0 0); --muted-foreground: oklch(70.8% 0 0); --accent: oklch(37.1% 0 0); --accent-foreground: oklch(98.5% 0 0); --destructive: oklch(63.7% 0.237 25.331); --destructive-foreground: oklch(98.5% 0 0); --border: oklch(37.1% 0 0); --input: oklch(26.9% 0 0); --ring: oklch(55.6% 0 0); --danger: oklch(19.9% 0.063 23.01); --danger-foreground: oklch(79.8% 0.115 17.83); --danger-border: oklch(26.9% 0.102 25.45); --warning: oklch(22.7% 0.05 113.29); --warning-foreground: oklch(86.4% 0.141 92.19); --warning-border: oklch(29.1% 0.063 109.77); --info: oklch(15.6% 0.045 250.71); --info-foreground: oklch(67.5% 0.152 258.33); --info-border: oklch(26.2% 0.052 266.51); --success: oklch(20.9% 0.0487 158.25); --success-foreground: oklch(86.2% 0.169 157.764); --success-border: oklch(31.6% 0.082 152.3); --chart-1: oklch(62.3% 0.214 259.815); --chart-2: oklch(72.3% 0.219 149.579); --chart-3: oklch(62.7% 0.265 303.9); --chart-4: oklch(70.5% 0.213 47.604); --chart-5: oklch(64.5% 0.246 16.439); } @layer base { * { border-color: var(--border); outline-color: oklch(from var(--ring) l c h / 0.5); scrollbar-width: thin; scrollbar-color: var(--border) transparent; } html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-tap-highlight-color: transparent; } html[data-base-ui-scroll-locked] { scroll-behavior: auto; } body { background-color: var(--background); color: var(--foreground); overscroll-behavior: none; } .root { isolation: isolate; min-height: 100vh; } ::-webkit-scrollbar { width: 4px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } } ``` **Set up import aliases** Configure TypeScript path mapping to enable clean `@/*` imports throughout your project. ```json title="tsconfig.json" { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./*"] } } } ``` **Add the root wrapper to your layout** Wrap your application content with the `root` class to ensure proper styling isolation and theme application. ```tsx {2} title="layout.tsx"
{children}
``` **Configure components.json (optional)** Optionally create a `components.json` file to enable shadcn CLI component installation with the correct paths and settings. ```json title="components.json" { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "rsc": false, "tsx": true, "tailwind": { "config": "", "css": "src/styles/globals.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" }, "iconLibrary": "lucide", "registries": { "@9ui": "https://9ui.dev/r/{name}.json" } } ``` **Start using 9ui components** Your project is now fully configured! You can start installing and using 9ui components in your application. ## Icon Libraries 9ui components use [`lucide-react`](https://lucide.dev/) as the default icon library, which provides a comprehensive set of beautiful, consistent icons. ### Alternative Icon Libraries For additional icon options, consider these popular alternatives: - **[Monicon](https://monicon-docs.vercel.app/)** - Access over 200,000 icons from various popular libraries in a unified interface - **[lucide-animated](https://lucide-animated.com/)** - A curated collection of animated icons for enhanced user interactions These libraries can be used alongside or instead of lucide-react depending on your project's specific needs. --- ### Introduction Beautiful, customizable components built with Base UI and Tailwind CSS This isn’t a standard component library. It’s a curated set of components that you can customize to fit your style. You won’t install it through npm. Instead, you simply select the components you need and add them directly to your project. Once included, each component is fully adaptable, letting you fine-tune it to meet your exact requirements. ## Why Base UI? [Base UI](https://base-ui.com) offers unstyled, headless components with built-in accessibility. This makes it perfect for 9ui, allowing us to integrate Tailwind CSS without forcing any default design. It’s a clean foundation that stays lightweight, letting you adapt every detail to fit your exact needs. ## Why it's called 9ui? The number **"9"** looks like the letter **"g"**. If you read **"9"** as **"g"**, it becomes **"gui"**, which stands for **"Graphical User Interface"**. ## Thank you First of all, I would like to thank [shadcn](https://ui.shadcn.com) for the inspiration. I am grateful to everyone who uses 9ui, provides feedback, and helps me improve it. Thank you! ## FAQ Yes. It is free and open source. You can use it in your projects without any costs. I would love to see your projects using it. Yes. It is a design system that allows you to create beautiful and customizable components. You can use it with all the frameworks that support React. Such as Next.js, Remix, Astro, Gatsby, etc. --- ### LLMs AI-friendly documentation for 9ui This page provides AI-friendly documentation formats that can be used by large language models (LLMs) to better understand and work with 11ui components. ## Available Formats We provide two different formats of documentation optimized for AI consumption: ### llms.txt A concise index format with links to individual component documentation. This format is ideal for: - Quick reference and component discovery - Integration with AI tools that need structured navigation - Systems that work better with linked content [See llms.txt](/llms.txt) ### llms-full.txt A comprehensive format containing the complete documentation for all components in a single file. This format is ideal for: - AI models that need full context in a single document - Local processing and analysis - Systems that work better with embedded content [See llms-full.txt](/llms-full.txt) ## Usage These files are automatically generated from the same MDX source files used for the website documentation, ensuring consistency and accuracy. The documentation includes: - Component descriptions and use cases - Installation instructions - Code examples and demos Both formats are updated automatically when the documentation is built, so they always reflect the latest component information. --- ### Next.js Setting up dark mode in your Next.js project. ### References - [Docs](https://github.com/pacocoursey/next-themes) **Install .** ```bash title="Terminal" npm install next-themes ``` **Create .** ```tsx title="providers/theme-provider.tsx" "use client" import { ThemeProvider as NextThemesProvider } from "next-themes" export function ThemeProvider({ children, ...props }: React.ComponentProps) { return {children} } ``` **Wrap your root layout with the provider.** ```tsx {1,12-19} title="app/layout.tsx" import { ThemeProvider } from "@/providers/theme-provider" export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( {children} ) } ``` **Add theme toggle component.** --- ### Remix Setting up dark mode in your Remix project. ### References - [Docs](https://github.com/abereghici/remix-themes) **Update tailwind.css file.** This will allow you to use `dark` class to apply dark mode styles. ```css {1-2} title="tailwind.css" .dark, :root[class~="dark"] { /* Your dark mode styles */ } ``` **Install .** ```bash title="Terminal" npm install remix-themes ``` **Create session storage.** ```tsx title="sessions.server.tsx" import { createCookieSessionStorage } from "@remix-run/node" import { createThemeSessionResolver } from "remix-themes" const sessionStorage = createCookieSessionStorage({ cookie: { name: "__remix-themes", // domain: 'remix.run', path: "/", httpOnly: true, sameSite: "lax", secrets: ["s3cr3t"], // secure: true, }, }) export const themeSessionResolver = createThemeSessionResolver(sessionStorage) ``` **Create action to set theme.** ```tsx title="routes/action.set-theme.tsx" import { createThemeAction } from "remix-themes" import { themeSessionResolver } from "../sessions.server" export const action = createThemeAction(themeSessionResolver) ``` **Add theme provider to root layout.** ```tsx {18-23, 25-32, 35, 36, 38, 42, 43} title="root.tsx" import "./tailwind.css" import { LoaderFunctionArgs } from "@remix-run/node" import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData, } from "@remix-run/react" import clsx from "clsx" import { PreventFlashOnWrongTheme, ThemeProvider, useTheme } from "remix-themes" import { themeSessionResolver } from "./sessions.server" export async function loader({ request }: LoaderFunctionArgs) { const { getTheme } = await themeSessionResolver(request) return { theme: getTheme(), } } export default function AppWithProviders() { const data = useLoaderData() return ( ) } export function App() { const data = useLoaderData() const [theme] = useTheme() return ( ) } ``` **Add theme toggle component.** ```tsx title="components/theme-toggle.tsx" import * as React from "react" import { MoonIcon, SunIcon } from "lucide-react" import { Theme, useTheme } from "remix-themes" import { Button } from "./ui/button" export function ModeToggle() { const [theme, setTheme] = useTheme() const toggleTheme = React.useCallback(() => { setTheme(theme === Theme.LIGHT ? Theme.DARK : Theme.LIGHT) }, [theme, setTheme]) return ( ) } ``` --- ### Roadmap What's coming next We plan to add more components and features as Base UI continues to evolve, and 9ui will grow right alongside it. Development isn’t just about adding new components. We’ll also improve existing ones and enhance their usability over time. User feedback is incredibly valuable to us. To share your ideas or issues, feel free to open an issue on [GitHub](https://github.com/borabaloglu/9ui/issues) or reach out to [@borabalogluu](https://x.com/borabalogluu) on X. There’s no strict deadline, but we do have features in the pipeline. To follow our active development, check out [roadmap](https://9ui.featurebase.app/roadmap) for real-time updates. --- ### Theming Using CSS variables to customize the theme. ## Color Tokens The theme system is built with semantic color tokens that represent specific use cases rather than literal colors. Here are the main color tokens and their use cases: ### Base Colors - `background` / `foreground`: Primary background and text colors - `card` / `card-foreground`: Used for card components and their content - `popover` / `popover-foreground`: For popover, dropdown, dialog, etc. ### Interactive Elements - `primary` / `primary-foreground`: Main brand color, used for primary actions - `secondary` / `secondary-foreground`: Less prominent actions and elements - `muted` / `muted-foreground`: Subdued elements like secondary text - `accent` / `accent-foreground`: Highlighted or featured elements - `destructive` / `destructive-foreground`: Dangerous or destructive actions ### Status Colors - `danger` / `danger-foreground` / `danger-border`: Error states and critical alerts - `warning` / `warning-foreground` / `warning-border`: Warning messages and alerts - `info` / `info-foreground` / `info-border`: Informational messages - `success` / `success-foreground` / `success-border`: Success states and confirmations ### Utility Colors - `border`: Default border color - `input`: Form input borders - `ring`: Focus ring color for interactive elements ### Chart Colors - `chart-1` through `chart-5`: Predefined colors for data visualizations ## Theme Color Mapping The theme system uses semantic color tokens that map to specific shade values from the [Tailwind CSS color palette](https://tailwindcss.com/docs/customizing-colors). Each shade (neutral, gray, slate, stone, zinc) follows the same mapping pattern: ### Light Mode Mapping - `background`: shade-50 (lightest) - `foreground`: shade-950 (darkest) - `card`: shade-100 - `card-foreground`: shade-950 - `popover`: shade-100 - `popover-foreground`: shade-950 - `primary`: shade-950 - `primary-foreground`: shade-50 - `secondary`: shade-200 - `secondary-foreground`: shade-900 - `muted`: shade-200 - `muted-foreground`: shade-600 - `accent`: shade-200 - `accent-foreground`: shade-950 - `destructive`: red-700 - `destructive-foreground`: shade-50 - `border`: shade-300 - `input`: shade-100 - `ring`: shade-400 - `chart-1`: blue-700 - `chart-2`: green-700 - `chart-3`: purple-700 - `chart-4`: orange-700 - `chart-5`: rose-700 ### Dark Mode Mapping - `background`: shade-950 (darkest) - `foreground`: shade-50 (lightest) - `card`: shade-900 - `card-foreground`: shade-50 - `popover`: shade-900 - `popover-foreground`: shade-50 - `primary`: shade-50 - `primary-foreground`: shade-950 - `secondary`: shade-800 - `secondary-foreground`: shade-100 - `muted`: shade-800 - `muted-foreground`: shade-400 - `accent`: shade-700 - `accent-foreground`: shade-50 - `destructive`: red-500 - `destructive-foreground`: shade-50 - `border`: shade-700 - `input`: shade-800 - `ring`: shade-500 - `chart-1`: blue-500 - `chart-2`: green-500 - `chart-3`: purple-500 - `chart-4`: orange-500 - `chart-5`: rose-500 Where `shade` represents the selected shade (e.g., if using neutral shade: neutral-50, neutral-100, etc.). The status color system is inspired by [Emil Kowalski's Sonner](https://sonner.emilkowal.ski/) toast library, providing consistent visual feedback across different states. These status colors remain consistent across all theme shades, ensuring users can always recognize the semantic meaning regardless of the selected color theme. ## Why OKLCH? 1. **Wider Color Gamut**: OKLCH can represent a broader range of colors 2. **Better Color Interpolation**: Smoother transitions and animations 3. **Perceptual Uniformity**: More natural-looking color variations You can read more about OKLCH colors in [What are OKLCH colors?](https://jakub.kr/components/oklch-colors) by [Jakub Krehel](https://x.com/jakubkrehel). ## Customizing Colors ### Adding and Using New Colors To add new colors to your theme: **Add the CSS variables in ** ```css title="globals.css" :root { /* Existing colors */ --custom-color: oklch(62.7% 0.265 303.9); --custom-color-foreground: oklch(97.7% 0.014 308.299); } .dark { /* Existing dark mode colors */ --custom-color: oklch(62.7% 0.265 303.9); --custom-color-foreground: oklch(97.7% 0.014 308.299); } @theme inline { /* Existing colors */ --color-custom-color: var(--custom-color); --color-custom-color-foreground: var(--custom-color-foreground); } ``` **Use the new colors in your components** ```jsx title="component.tsx"
Custom colored content
``` --- ### Vite Setting up dark mode in your Vite project. **Create a theme provider component.** ```tsx title="providers/theme-provider.tsx" import { createContext, useContext, useEffect, useState } from "react" type Theme = "dark" | "light" | "system" type ThemeProviderProps = { children: React.ReactNode defaultTheme?: Theme storageKey?: string } type ThemeProviderState = { theme: Theme setTheme: (theme: Theme) => void } const ThemeProviderContext = createContext( undefined ) export function ThemeProvider({ children, defaultTheme = "system", storageKey = "vite-ui-theme", ...props }: ThemeProviderProps) { const [theme, setTheme] = useState( () => (localStorage.getItem(storageKey) as Theme) || defaultTheme ) useEffect(() => { const root = window.document.documentElement root.classList.remove("light", "dark") if (theme === "system") { const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") .matches ? "dark" : "light" root.classList.add(systemTheme) return } root.classList.add(theme) }, [theme]) useEffect(() => { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") // Add listener for system theme changes const handleChange = () => { if (theme === "system") { const root = window.document.documentElement root.classList.remove("light", "dark") root.classList.add(mediaQuery.matches ? "dark" : "light") } } mediaQuery.addEventListener("change", handleChange) return () => mediaQuery.removeEventListener("change", handleChange) }, [theme]) const value = { theme, setTheme: (theme: Theme) => { localStorage.setItem(storageKey, theme) setTheme(theme) }, } return ( {children} ) } export const useTheme = () => { const context = useContext(ThemeProviderContext) if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider") return context } ``` **Wrap your app with the provider.** ```tsx {1,5-7} title="main.tsx" import { ThemeProvider } from "@/providers/theme-provider" function App() { return ( {/* Your app content */} ) } export default App ``` **Add theme toggle component.** ```tsx title="components/theme-toggle.tsx" import * as React from "react" import { MoonIcon, SunIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { useTheme } from "@/providers/theme-provider" export default function ThemeToggle() { const { setTheme, theme } = useTheme() const toggleTheme = React.useCallback(() => { setTheme(theme === "dark" ? "light" : "dark") }, [theme, setTheme]) return ( ) } ``` --- ## Components ### Accordion A collapsible section to show or hide content. ### References - [Docs](https://base-ui.com/react/components/accordion) ### Example ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" export default function AccordionDemo() { return ( What is accordion? A collapsible component that organizes content into expandable sections. Is it customizable? Yes. You can style it with tailwind classes to match your design system. Is it animated? Yes. Smooth expand and collapse transitions are built in. Can it be disabled? ) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/accordion ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" ``` ```tsx title="Anatomy" ``` ### Multiple ### Example ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" export default function AccordionMultipleDemo() { return ( Can it be multiple? Yes. You can open multiple items at the same time. Try to open multiple items at the same time. You can open multiple items at the same time. Add multiple prop to the accordion. You can add the multiple prop to the accordion to allow multiple items to be opened at the same time. ) } ``` --- ### Alert Used to highlight important messages. ### Example ```tsx import { AlertTriangleIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertDemo() { return ( No Internet Connection Please check your internet connection and try again. ) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/alert ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" ``` ```tsx title="Anatomy" ``` ## Examples ### Success ### Example ```tsx import { CircleCheckIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertSuccessDemo() { return ( Your account has been created You can now sign in with your new account credentials. ) } ``` --- ### Info ### Example ```tsx import { InfoIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertInfoDemo() { return ( Browser Update Available A new version of your browser is available. Updating your browser ensures better security and performance. ) } ``` --- ### Warning ### Example ```tsx import { AlertTriangleIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertWarningDemo() { return ( Your session is about to expire You will be logged out in 5 minutes. Please save your work and refresh the page. ) } ``` --- ### Danger ### Example ```tsx import { XCircleIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertDangerDemo() { return ( Your subscription has been canceled Your access to premium features will end in 30 days. You can reactivate your subscription anytime. ) } ``` --- ### With action ### Example ```tsx import { AlertTriangleIcon } from "lucide-react" import { Alert, AlertTitle } from "@/components/ui/alert" import { Button } from "@/components/ui/button" export default function AlertWithActionDemo() { return ( No Internet Connection ) } ``` --- ### Alert Dialog A modal dialog for critical messages or confirmation actions. ### References - [Docs](https://base-ui.com/react/components/alert-dialog) ### Example ```tsx import { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" export default function AlertDialogDemo() { return ( ( )} /> Are you sure? This action cannot be undone. Your post will be permanently deleted. ( )} /> ) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/alert-dialog ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" ``` ```tsx title="Anatomy" ``` --- ### Aspect Ratio Allows you to display an element at a specific aspect ratio. ### Example ```tsx import { AspectRatio } from "@/components/ui/aspect-ratio" export default function AspectRatioDemo() { return (
Content
) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/aspect-ratio ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { AspectRatio } from "@/components/ui/aspect-ratio" ``` ```tsx title="Anatomy" ``` --- ### Autocomplete A component that suggests options as the user types. ### References - [Docs](https://base-ui.com/react/components/autocomplete) ### Example ```tsx import * as React from "react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteDemo() { return (
No tags found. {(tag: Tag) => ( {tag.value} )}
) } interface Tag { id: string value: string } const tags: Tag[] = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" }, { id: "t5", value: "internal" }, { id: "t6", value: "mobile" }, { id: "c-accordion", value: "component: accordion" }, { id: "c-alert-dialog", value: "component: alert dialog" }, { id: "c-autocomplete", value: "component: autocomplete" }, { id: "c-avatar", value: "component: avatar" }, { id: "c-checkbox", value: "component: checkbox" }, { id: "c-checkbox-group", value: "component: checkbox group" }, { id: "c-collapsible", value: "component: collapsible" }, { id: "c-combobox", value: "component: combobox" }, { id: "c-context-menu", value: "component: context menu" }, { id: "c-dialog", value: "component: dialog" }, { id: "c-field", value: "component: field" }, { id: "c-form", value: "component: form" }, { id: "c-input", value: "component: input" }, { id: "c-popover", value: "component: popover" }, { id: "c-select", value: "component: select" }, { id: "c-switch", value: "component: switch" }, { id: "c-tabs", value: "component: tabs" }, { id: "c-tooltip", value: "component: tooltip" }, ] ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/autocomplete ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/autocomplete" ``` ```tsx title="Anatomy" ``` ## Examples ### Groupped ### Example ```tsx import * as React from "react" import { Autocomplete, AutocompleteCollection, AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteSeparator, AutocompleteValue, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteGrouppedDemo() { // Filter and group items by category const groupedItems = React.useMemo(() => { const groups: { [key: string]: Item[] } = items.reduce( (acc, item) => { acc[item.category] = acc[item.category] || [] acc[item.category].push(item) return acc }, {} as { [key: string]: Item[] } ) const order = [ "Frontend Frameworks", "Backend Runtime", "Backend Frameworks", "Meta Frameworks", ] return order.map((value) => ({ value, items: groups[value] ?? [] })) }, []) return (
No technologies found for " " {(group: Group, index: number) => ( {index > 0 && } {group.value} {(item: Item) => ( {item.value} )} )}
) } interface Group { value: string items: Item[] } interface Item { id: string value: string category: string } const items: Item[] = [ { id: "react", value: "React", category: "Frontend Frameworks" }, { id: "vue", value: "Vue.js", category: "Frontend Frameworks" }, { id: "angular", value: "Angular", category: "Frontend Frameworks" }, { id: "svelte", value: "Svelte", category: "Frontend Frameworks" }, { id: "nodejs", value: "Node.js", category: "Backend Runtime" }, { id: "deno", value: "Deno", category: "Backend Runtime" }, { id: "bun", value: "Bun", category: "Backend Runtime" }, { id: "express", value: "Express.js", category: "Backend Frameworks" }, { id: "fastify", value: "Fastify", category: "Backend Frameworks" }, { id: "nestjs", value: "NestJS", category: "Backend Frameworks" }, { id: "nextjs", value: "Next.js", category: "Meta Frameworks" }, { id: "nuxt", value: "Nuxt.js", category: "Meta Frameworks" }, { id: "remix", value: "Remix", category: "Meta Frameworks" }, ] ``` --- ### Async ### Example ```tsx "use client" import * as React from "react" import { Loader2Icon } from "lucide-react" import { Autocomplete, AutocompleteContent, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteStatus, useFilter, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteAsyncDemo() { const [searchValue, setSearchValue] = React.useState("") const [isLoading, setIsLoading] = React.useState(false) const [searchResults, setSearchResults] = React.useState([]) const [error, setError] = React.useState(null) const { contains } = useFilter({ sensitivity: "base" }) React.useEffect(() => { if (!searchValue) { setSearchResults([]) setIsLoading(false) return undefined } setIsLoading(true) setError(null) let ignore = false async function fetchBooks() { try { const results = await searchBooks(searchValue, contains) if (!ignore) { setSearchResults(results) } } catch { if (!ignore) { setError("Failed to find books. Please try again.") setSearchResults([]) } } finally { if (!ignore) { setIsLoading(false) } } } const timeoutId = setTimeout(fetchBooks, 300) return () => { clearTimeout(timeoutId) ignore = true } }, [searchValue, contains]) let status: React.ReactNode = `${searchResults.length} book${ searchResults.length === 1 ? "" : "s" } found` if (isLoading) { status = ( Searching books... ) } else if (error) { status = error } else if (searchResults.length === 0 && searchValue) { status = `No books found for "${searchValue}"` } const shouldRenderPopup = searchValue !== "" return (
(item as Book).title} filter={null} >
{shouldRenderPopup && ( {status} {(book: Book) => (
{book.title}
by {book.author}, {book.publishedYear}
)}
)}
) } interface Book { id: string title: string author: string publishedYear: number } const books: Book[] = [ { id: "1", title: "Pride and Prejudice", author: "Jane Austen", publishedYear: 1813, }, { id: "2", title: "To Kill a Mockingbird", author: "Harper Lee", publishedYear: 1960, }, { id: "3", title: "1984", author: "George Orwell", publishedYear: 1949 }, { id: "4", title: "The Great Gatsby", author: "F. Scott Fitzgerald", publishedYear: 1925, }, { id: "5", title: "Jane Eyre", author: "Charlotte Brontë", publishedYear: 1847, }, { id: "6", title: "Wuthering Heights", author: "Emily Brontë", publishedYear: 1847, }, { id: "7", title: "The Catcher in the Rye", author: "J.D. Salinger", publishedYear: 1951, }, { id: "8", title: "Lord of the Flies", author: "William Golding", publishedYear: 1954, }, { id: "9", title: "Of Mice and Men", author: "John Steinbeck", publishedYear: 1937, }, { id: "10", title: "Romeo and Juliet", author: "William Shakespeare", publishedYear: 1597, }, { id: "11", title: "The Adventures of Huckleberry Finn", author: "Mark Twain", publishedYear: 1884, }, { id: "12", title: "The Lord of the Rings", author: "J.R.R. Tolkien", publishedYear: 1954, }, { id: "13", title: "Animal Farm", author: "George Orwell", publishedYear: 1945, }, { id: "14", title: "Brave New World", author: "Aldous Huxley", publishedYear: 1932, }, { id: "15", title: "The Picture of Dorian Gray", author: "Oscar Wilde", publishedYear: 1890, }, { id: "16", title: "Crime and Punishment", author: "Fyodor Dostoevsky", publishedYear: 1866, }, { id: "17", title: "The Brothers Karamazov", author: "Fyodor Dostoevsky", publishedYear: 1880, }, { id: "18", title: "War and Peace", author: "Leo Tolstoy", publishedYear: 1869, }, { id: "19", title: "Anna Karenina", author: "Leo Tolstoy", publishedYear: 1877, }, { id: "20", title: "The Odyssey", author: "Homer", publishedYear: -800 }, { id: "21", title: "The Iliad", author: "Homer", publishedYear: -750 }, { id: "22", title: "Hamlet", author: "William Shakespeare", publishedYear: 1603, }, { id: "23", title: "Macbeth", author: "William Shakespeare", publishedYear: 1623, }, { id: "24", title: "One Hundred Years of Solitude", author: "Gabriel García Márquez", publishedYear: 1967, }, { id: "25", title: "The Divine Comedy", author: "Dante Alighieri", publishedYear: 1320, }, { id: "26", title: "Don Quixote", author: "Miguel de Cervantes", publishedYear: 1605, }, { id: "27", title: "Moby Dick", author: "Herman Melville", publishedYear: 1851, }, { id: "28", title: "The Scarlet Letter", author: "Nathaniel Hawthorne", publishedYear: 1850, }, { id: "29", title: "The Canterbury Tales", author: "Geoffrey Chaucer", publishedYear: 1400, }, { id: "30", title: "Great Expectations", author: "Charles Dickens", publishedYear: 1861, }, { id: "31", title: "A Tale of Two Cities", author: "Charles Dickens", publishedYear: 1859, }, { id: "32", title: "Oliver Twist", author: "Charles Dickens", publishedYear: 1838, }, { id: "33", title: "David Copperfield", author: "Charles Dickens", publishedYear: 1850, }, { id: "34", title: "Little Women", author: "Louisa May Alcott", publishedYear: 1868, }, { id: "35", title: "The Count of Monte Cristo", author: "Alexandre Dumas", publishedYear: 1844, }, { id: "36", title: "Les Misérables", author: "Victor Hugo", publishedYear: 1862, }, { id: "37", title: "The Hunchback of Notre-Dame", author: "Victor Hugo", publishedYear: 1831, }, { id: "38", title: "Madame Bovary", author: "Gustave Flaubert", publishedYear: 1857, }, { id: "39", title: "The Stranger", author: "Albert Camus", publishedYear: 1942, }, { id: "40", title: "The Metamorphosis", author: "Franz Kafka", publishedYear: 1915, }, ] async function searchBooks( query: string, filter: (item: string, query: string) => boolean ): Promise { // Simulate network delay await new Promise((resolve) => { setTimeout(resolve, Math.random() * 400 + 200) }) // Simulate occasional network errors (1% chance) if (Math.random() < 0.01 || query === "will_error") { throw new Error("Network error") } return books.filter( (book) => filter(book.title, query) || filter(book.author, query) || filter(book.publishedYear.toString(), query) ) } ``` ### Inline Autocomplete ### Example ```tsx import * as React from "react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteInlineDemo() { return (
No tags found. {(tag: Tag) => ( {tag.value} )}
) } interface Tag { id: string value: string } const tags: Tag[] = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" }, { id: "t5", value: "internal" }, { id: "t6", value: "mobile" }, { id: "c-accordion", value: "component: accordion" }, { id: "c-alert-dialog", value: "component: alert dialog" }, { id: "c-autocomplete", value: "component: autocomplete" }, { id: "c-avatar", value: "component: avatar" }, { id: "c-checkbox", value: "component: checkbox" }, { id: "c-checkbox-group", value: "component: checkbox group" }, { id: "c-collapsible", value: "component: collapsible" }, { id: "c-combobox", value: "component: combobox" }, { id: "c-context-menu", value: "component: context menu" }, { id: "c-dialog", value: "component: dialog" }, { id: "c-field", value: "component: field" }, { id: "c-form", value: "component: form" }, { id: "c-input", value: "component: input" }, { id: "c-popover", value: "component: popover" }, { id: "c-select", value: "component: select" }, { id: "c-switch", value: "component: switch" }, { id: "c-tabs", value: "component: tabs" }, { id: "c-tooltip", value: "component: tooltip" }, ] ``` --- ### Fuzzy Matcher ### Example ```tsx "use client" import * as React from "react" import { matchSorter } from "match-sorter" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteValue, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteFuzzyMatcherDemo() { const fuzzyFilter = React.useCallback( (item: unknown, query: string): boolean => { const doc = item as DevDoc if (!query) { return true } const results = matchSorter([doc], query, { keys: ["title", "description"], threshold: matchSorter.rankings.MATCHES, }) return results.length > 0 }, [] ) return (
(item as DevDoc).title} >
No docs found for " " {(doc: DevDoc) => ( {(value) => (
{highlightText(doc.title, value)}
{highlightText(doc.description, value)}
)}
)}
) } function highlightText(text: string, query: string): React.ReactNode { const trimmed = query.trim() if (!trimmed) { return text } const limited = trimmed.slice(0, 100) const escaped = limited.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") const regex = new RegExp(`(${escaped})`, "gi") return text.split(regex).map((part, idx) => regex.test(part) ? ( {part} ) : ( part ) ) } interface DevDoc { id: string title: string description: string } const devDocs: DevDoc[] = [ { id: "1", title: "React Hooks Guide", description: "Learn how to use React Hooks like useState, useEffect, and custom hooks to manage state and side effects in functional components.", }, { id: "2", title: "JavaScript Array Methods", description: "Master array methods like map, filter, reduce, and forEach for functional programming in JavaScript.", }, { id: "3", title: "CSS Flexbox Layout", description: "Complete guide to CSS Flexbox for creating responsive and flexible layouts with ease.", }, { id: "4", title: "TypeScript Interfaces", description: "Understanding TypeScript interfaces and type definitions for better code safety and documentation.", }, { id: "5", title: "API Design Best Practices", description: "Learn how to design RESTful APIs that are intuitive, scalable, and maintainable.", }, { id: "6", title: "React Performance Optimization", description: "Tips and techniques for optimizing React application performance using memoization and lazy loading.", }, { id: "7", title: "Git Workflow Strategies", description: "Understanding different Git workflows like GitFlow, GitHub Flow, and trunk-based development.", }, { id: "8", title: "Node.js Express Server", description: "Building RESTful APIs with Node.js and Express framework for scalable backend applications.", }, { id: "9", title: "Database Indexing", description: "How to use database indexes effectively to improve query performance and reduce response times.", }, { id: "10", title: "Docker Containerization", description: "Learn how to containerize applications with Docker for consistent deployment across environments.", }, { id: "11", title: "Authentication & Authorization", description: "Implementing secure authentication and authorization using JWT tokens and OAuth protocols.", }, { id: "12", title: "Testing Strategies", description: "Comprehensive guide to unit testing, integration testing, and end-to-end testing practices.", }, { id: "13", title: "Webpack Configuration", description: "Optimizing Webpack configuration for production builds and development workflows.", }, { id: "14", title: "Microservices Architecture", description: "Designing and implementing microservices architecture for scalable distributed systems.", }, { id: "15", title: "GraphQL API Development", description: "Building efficient GraphQL APIs with type safety and flexible data fetching capabilities.", }, ] ``` --- ### Auto Highlight ### Example ```tsx import * as React from "react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteAutoHighlightDemo() { return (
No tags found. {(tag: Tag) => ( {tag.value} )}
) } interface Tag { id: string value: string } const tags: Tag[] = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" }, { id: "t5", value: "internal" }, { id: "t6", value: "mobile" }, { id: "c-accordion", value: "component: accordion" }, { id: "c-alert-dialog", value: "component: alert dialog" }, { id: "c-autocomplete", value: "component: autocomplete" }, { id: "c-avatar", value: "component: avatar" }, { id: "c-checkbox", value: "component: checkbox" }, { id: "c-checkbox-group", value: "component: checkbox group" }, { id: "c-collapsible", value: "component: collapsible" }, { id: "c-combobox", value: "component: combobox" }, { id: "c-context-menu", value: "component: context menu" }, { id: "c-dialog", value: "component: dialog" }, { id: "c-field", value: "component: field" }, { id: "c-form", value: "component: form" }, { id: "c-input", value: "component: input" }, { id: "c-popover", value: "component: popover" }, { id: "c-select", value: "component: select" }, { id: "c-switch", value: "component: switch" }, { id: "c-tabs", value: "component: tabs" }, { id: "c-tooltip", value: "component: tooltip" }, ] ``` --- ### Limit Results ### Example ```tsx import * as React from "react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteStatus, useFilter, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" const limit = 8 export default function AutocompleteLimitResultsDemo() { const [value, setValue] = React.useState("") const { contains } = useFilter({ sensitivity: "base" }) const totalMatches = React.useMemo(() => { const trimmed = value.trim() if (!trimmed) { return tags.length } return tags.filter((t) => contains(t.value, trimmed)).length }, [value, contains]) const moreCount = Math.max(0, totalMatches - limit) return (
No tags found. {(tag: Tag) => ( {tag.value} )} {moreCount > 0 && ( {moreCount} results hidden (type a more specific query to narrow results) )}
) } interface Tag { id: string value: string } const tags: Tag[] = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" }, { id: "t5", value: "internal" }, { id: "t6", value: "mobile" }, { id: "c-accordion", value: "component: accordion" }, { id: "c-alert-dialog", value: "component: alert dialog" }, { id: "c-autocomplete", value: "component: autocomplete" }, { id: "c-avatar", value: "component: avatar" }, { id: "c-checkbox", value: "component: checkbox" }, { id: "c-checkbox-group", value: "component: checkbox group" }, { id: "c-collapsible", value: "component: collapsible" }, { id: "c-combobox", value: "component: combobox" }, { id: "c-context-menu", value: "component: context menu" }, { id: "c-dialog", value: "component: dialog" }, { id: "c-field", value: "component: field" }, { id: "c-form", value: "component: form" }, { id: "c-input", value: "component: input" }, { id: "c-popover", value: "component: popover" }, { id: "c-select", value: "component: select" }, { id: "c-switch", value: "component: switch" }, { id: "c-tabs", value: "component: tabs" }, { id: "c-tooltip", value: "component: tooltip" }, ] ``` --- ### With Rows ### Example ```tsx "use client" import * as React from "react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteRow, AutocompleteTrigger, } from "@/components/ui/autocomplete" import { Avatar, AvatarFallback } from "@/components/ui/avatar" import { Label } from "@/components/ui/label" export default function AutocompleteRowDemo() { const [pickerOpen, setPickerOpen] = React.useState(false) const [avatar, setAvatar] = React.useState(null) const [searchValue, setSearchValue] = React.useState("") function handleSelectAvatar(value: string | null) { if (!value) { return } setPickerOpen(false) setAvatar(value) setSearchValue("") } return ( setSearchValue("")} value={searchValue} onValueChange={(value, details) => { if (details.reason !== "item-press") { setSearchValue(value) } }} > {avatar || "+"} No avatars found {(group: AvatarGroup) => ( {group.label}
{chunkArray(group.items, COLUMNS).map((row, rowIdx) => ( {row.map((avatar) => ( handleSelectAvatar(avatar.emoji)} > {avatar.emoji} ))} ))}
)}
) } interface Avatar { id: string emoji: string name: string category: string } interface AvatarGroup { value: string label: string items: Avatar[] } const avatarCategories = [ { label: "People", avatars: [ { id: "1", emoji: "👤", name: "default user" }, { id: "2", emoji: "👨", name: "man" }, { id: "3", emoji: "👩", name: "woman" }, { id: "4", emoji: "🧑", name: "person" }, { id: "5", emoji: "👦", name: "boy" }, { id: "6", emoji: "👧", name: "girl" }, { id: "7", emoji: "👴", name: "old man" }, { id: "8", emoji: "👵", name: "old woman" }, { id: "9", emoji: "👶", name: "baby" }, { id: "10", emoji: "🧒", name: "child" }, { id: "11", emoji: "🧓", name: "older person" }, { id: "12", emoji: "👨‍💼", name: "businessman" }, { id: "13", emoji: "👩‍💼", name: "businesswoman" }, { id: "14", emoji: "👨‍💻", name: "developer" }, { id: "15", emoji: "👩‍💻", name: "developer woman" }, { id: "16", emoji: "👨‍🎨", name: "artist" }, ], }, { label: "Professions", avatars: [ { id: "17", emoji: "👩‍🎨", name: "artist woman" }, { id: "18", emoji: "👨‍⚕️", name: "doctor" }, { id: "19", emoji: "👩‍⚕️", name: "doctor woman" }, { id: "20", emoji: "👨‍🏫", name: "teacher" }, { id: "21", emoji: "👩‍🏫", name: "teacher woman" }, { id: "22", emoji: "👨‍🚀", name: "astronaut" }, { id: "23", emoji: "👩‍🚀", name: "astronaut woman" }, { id: "24", emoji: "👨‍🔬", name: "scientist" }, { id: "25", emoji: "👩‍🔬", name: "scientist woman" }, { id: "26", emoji: "👨‍🍳", name: "chef" }, { id: "27", emoji: "👩‍🍳", name: "chef woman" }, { id: "28", emoji: "👨‍🎤", name: "singer" }, { id: "29", emoji: "👩‍🎤", name: "singer woman" }, { id: "30", emoji: "👨‍✈️", name: "pilot" }, { id: "31", emoji: "👩‍✈️", name: "pilot woman" }, { id: "32", emoji: "👮", name: "police officer" }, ], }, { label: "Animals", avatars: [ { id: "33", emoji: "🐶", name: "dog" }, { id: "34", emoji: "🐱", name: "cat" }, { id: "35", emoji: "🐭", name: "mouse" }, { id: "36", emoji: "🐹", name: "hamster" }, { id: "37", emoji: "🐰", name: "rabbit" }, { id: "38", emoji: "🦊", name: "fox" }, { id: "39", emoji: "🐻", name: "bear" }, { id: "40", emoji: "🐼", name: "panda" }, { id: "41", emoji: "🐨", name: "koala" }, { id: "42", emoji: "🐯", name: "tiger" }, { id: "43", emoji: "🦁", name: "lion" }, { id: "44", emoji: "🐮", name: "cow" }, { id: "45", emoji: "🐷", name: "pig" }, { id: "46", emoji: "🐸", name: "frog" }, { id: "47", emoji: "🐵", name: "monkey" }, { id: "48", emoji: "🐧", name: "penguin" }, ], }, { label: "Objects & Symbols", avatars: [ { id: "49", emoji: "⭐", name: "star" }, { id: "50", emoji: "🌟", name: "glowing star" }, { id: "51", emoji: "💎", name: "diamond" }, { id: "52", emoji: "🔥", name: "fire" }, { id: "53", emoji: "⚡", name: "lightning" }, { id: "54", emoji: "🌙", name: "moon" }, { id: "55", emoji: "☀️", name: "sun" }, { id: "56", emoji: "🌈", name: "rainbow" }, { id: "57", emoji: "🎯", name: "target" }, { id: "58", emoji: "🚀", name: "rocket" }, { id: "59", emoji: "🎮", name: "gaming" }, { id: "60", emoji: "💻", name: "laptop" }, { id: "61", emoji: "📱", name: "phone" }, { id: "62", emoji: "🎨", name: "art" }, { id: "63", emoji: "🏆", name: "trophy" }, { id: "64", emoji: "🎪", name: "circus" }, ], }, ] const avatarGroups: AvatarGroup[] = avatarCategories.map((category) => ({ value: category.label, label: category.label, items: category.avatars.map((avatar) => ({ ...avatar, value: avatar.name.toLowerCase(), category: category.label, })), })) const COLUMNS = 6 function chunkArray(array: T[], size: number): T[][] { const result: T[][] = [] for (let i = 0; i < array.length; i += size) { result.push(array.slice(i, i + size)) } return result } ``` --- ### Virtualized ### Example ```tsx import * as React from "react" import { useVirtualizer } from "@tanstack/react-virtual" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, useFilter, } from "@/components/ui/autocomplete" import { Label } from "@/components/ui/label" export default function AutocompleteVirtualizedDemo() { const [open, setOpen] = React.useState(false) const [searchValue, setSearchValue] = React.useState("") const scrollElementRef = React.useRef(null) const { contains } = useFilter({ sensitivity: "base" }) const filteredItems = React.useMemo(() => { return virtualItems.filter((item) => contains(item, searchValue)) }, [contains, searchValue]) const virtualizer = useVirtualizer({ enabled: open, count: filteredItems.length, getScrollElement: () => scrollElementRef.current, estimateSize: () => 32, overscan: 20, paddingStart: 4, paddingEnd: 4, }) const handleScrollElementRef = React.useCallback( (element: HTMLDivElement) => { scrollElementRef.current = element if (element) { virtualizer.measure() } }, [virtualizer] ) const totalSize = virtualizer.getTotalSize() const totalSizePx = `${totalSize}px` return ( { if (!item) { return } const isStart = index === 0 const isEnd = index === filteredItems.length - 1 const shouldScroll = reason === "none" || (reason === "keyboard" && (isStart || isEnd)) if (shouldScroll) { queueMicrotask(() => { virtualizer.scrollToIndex(index, { align: isEnd ? "start" : "end" }) }) } }} >
No items found. {filteredItems.length > 0 && (
{virtualizer.getVirtualItems().map((virtualItem) => { const item = filteredItems[virtualItem.index] if (!item) { return null } return ( {item} ) })}
)}
) } const virtualItems = Array.from({ length: 10000 }, (_, i) => { const indexLabel = String(i + 1).padStart(5, "0") return `Item ${indexLabel}` }) ``` --- ### Avatar Displays an avatar with a fallback. ### References - [Docs](https://base-ui.com/react/components/avatar) ### Example ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" export default function AvatarDemo() { return ( BB ) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/avatar ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" ``` ```tsx title="Anatomy" ``` ## Examples ### Sizes ### Example ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" export default function AvatarSizesDemo() { return (
BB BB BB
) } ``` --- ### With Fallback ### Example ```tsx import { Avatar, AvatarFallback } from "@/components/ui/avatar" export default function AvatarWithFallbackDemo() { return ( BB ) } ``` --- ### Avatar Group ### Example ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" export default function AvatarGroupDemo() { return (
P1 P2 P3
) } ``` --- ### Badge Displays a badge for labeling or highlighting content. ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeDemo() { return Badge } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/badge ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Badge } from "@/components/ui/badge" ``` ```tsx title="Anatomy" ``` ## Examples ### Outline ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeOutlineDemo() { return Outline } ``` --- ### Secondary ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeSecondaryDemo() { return Secondary } ``` --- ### Success ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeSuccessDemo() { return Success } ``` --- ### Warning ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeWarningDemo() { return Warning } ``` --- ### Info ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeInfoDemo() { return Info } ``` --- ### Danger ### Example ```tsx import { Badge } from "@/components/ui/badge" export default function BadgeDangerDemo() { return Danger } ``` --- ### Breadcrumbs Displays a navigation path for better context. ### Example ```tsx import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumbs" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" export default function BreadcrumbsDemo() { return ( Home Toggle menu Documentation Themes GitHub Components Breadcrumb ) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/breadcrumbs ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumbs" ``` ```tsx title="Anatomy" ``` ## Examples ### Custom Separator ### Example ```tsx import { SlashIcon } from "lucide-react" import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumbs" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" export default function BreadcrumbsCustomSeparatorDemo() { return ( Home Toggle menu Documentation Themes GitHub Components Breadcrumb ) } ``` --- ### Button Displays a button for user interaction. ### Example ```tsx import { Button } from "@/components/ui/button" export default function ButtonDemo() { return } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/button ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Button } from "@/components/ui/button" ``` ```tsx title="Anatomy" ) } ``` --- ### With Icon ### Example ```tsx import { PencilIcon } from "lucide-react" import { Button } from "@/components/ui/button" export default function ButtonIconDemo() { return ( ) } ``` --- ### Secondary ### Example ```tsx import { Button } from "@/components/ui/button" export default function ButtonSecondaryDemo() { return } ``` --- ### Outline ### Example ```tsx import { Button } from "@/components/ui/button" export default function ButtonOutlineDemo() { return } ``` --- ### Ghost ### Example ```tsx import { Button } from "@/components/ui/button" export default function ButtonGhostDemo() { return } ``` --- ### Link ### Example ```tsx import { Button } from "@/components/ui/button" export default function ButtonLinkDemo() { return } ``` --- ### Destructive ### Example ```tsx import { Button } from "@/components/ui/button" export default function ButtonDestructiveDemo() { return } ``` --- ### Loading ### Example ```tsx import { Loader2Icon } from "lucide-react" import { Button } from "@/components/ui/button" export default function ButtonLoadingDemo() { return ( ) } ``` --- ### Calendar Provides a visual interface for date selection. ### References - [Docs](https://daypicker.dev/) ### Example ```tsx import { Calendar } from "@/components/ui/calendar" export default function CalendarDemo() { return } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/calendar ``` **Install dependencies** ```bash title="Terminal" npm install react-day-picker ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Calendar } from "@/components/ui/calendar" ``` ```tsx title="Anatomy" ``` ## Examples ### Single Date ### Example ```tsx "use client" import { useState } from "react" import { Calendar } from "@/components/ui/calendar" export default function CalendarSingleDemo() { const [selectedDate, setSelectedDate] = useState(undefined) return ( ) } ``` --- ### Multiple Dates ### Example ```tsx "use client" import { useState } from "react" import { Calendar } from "@/components/ui/calendar" export default function CalendarMultipleDemo() { const [selectedDates, setSelectedDates] = useState( undefined ) return ( ) } ``` --- ### Date Range ### Example ```tsx "use client" import { useState } from "react" import { DateRange } from "react-day-picker" import { Calendar } from "@/components/ui/calendar" export default function CalendarRangeDemo() { const [range, setRange] = useState(undefined) return ( ) } ``` --- ### Disabled ### Example ```tsx "use client" import { useState } from "react" import { Calendar } from "@/components/ui/calendar" export default function CalendarDisabledDemo() { const [selectedDate, setSelectedDate] = useState(undefined) return ( date < new Date()} selected={selectedDate} onSelect={setSelectedDate} /> ) } ``` --- ### Card Used to group and present information in a structured box. ### Example ```tsx import { LinkIcon, SendIcon } from "lucide-react" import { toast } from "sonner" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { Input } from "@/components/ui/input" export default function CardDemo() { return ( Invite Team Members Invite your team members to join your workspace.

You can invite up to 10 team members. You have 8 invites left.

Invited Members

KS

Karen Smith

karen@9ui.dev

CW

Chris Williams

chris@9ui.dev

) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/card ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" ``` ```tsx title="Anatomy" ``` ## Examples ### With image ### Example ```tsx import Image from "next/image" import { Button } from "@/components/ui/button" import { Card, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" export default function CardWithImageDemo() { return (
Blog Image
What is 9ui? Deep dive into the 9ui components and learn how to use them in your own projects.
) } ``` --- ### Carousel A slider to display multiple items in a scrollable view. ### References - [Docs](https://www.embla-carousel.com/) ### Example ```tsx import { AspectRatio } from "@/components/ui/aspect-ratio" import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel" const slides = [1, 2, 3, 4, 5] export default function CarouselDemo() { return (
{slides.map((slide) => (
{slide}
))}
) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/carousel ``` **Install dependencies** ```bash title="Terminal" npm install embla-carousel-react embla-carousel ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel" ``` ```tsx title="Anatomy" ``` ## Examples ### Vertical ### Example ```tsx import { AspectRatio } from "@/components/ui/aspect-ratio" import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel" const slides = [1, 2, 3, 4, 5] export default function CarouselVerticalDemo() { return (
{slides.map((slide) => (
{slide}
))}
) } ``` --- ### Multiple ### Example ```tsx import { AspectRatio } from "@/components/ui/aspect-ratio" import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel" const slides = [1, 2, 3, 4, 5] export default function CarouselMultipleDemo() { return (
{slides.map((slide) => (
{slide}
))}
) } ``` --- ### Looped ### Example ```tsx import { AspectRatio } from "@/components/ui/aspect-ratio" import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel" const slides = [1, 2, 3, 4, 5] export default function CarouselLoopedDemo() { return (
{slides.map((slide) => (
{slide}
))}
) } ``` --- ### Thumbnail ### Example ```tsx import { useState } from "react" import Image from "next/image" import { AspectRatio } from "@/components/ui/aspect-ratio" import { Carousel, CarouselApi, CarouselContent, CarouselItem, } from "@/components/ui/carousel" import { cn } from "@/lib/utils" const slides = [ "https://images.pexels.com/photos/1616403/pexels-photo-1616403.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2", "https://images.pexels.com/photos/1293120/pexels-photo-1293120.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2", "https://images.pexels.com/photos/1103970/pexels-photo-1103970.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2", "https://images.pexels.com/photos/2011824/pexels-photo-2011824.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2", "https://images.pexels.com/photos/2471235/pexels-photo-2471235.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2", ] export default function CarouselThumbnailDemo() { const [api, setApi] = useState() const [selectedIndex, setSelectedIndex] = useState(0) api?.on("select", () => { setSelectedIndex(api?.selectedScrollSnap() ?? 0) }) return (
{slides.map((slide) => ( Carousel slide ))}
{slides.map((slide, index) => ( ))}
) } ``` --- ### Chart A visual representation of data in various formats. ### References - [Docs](https://recharts.org) ### Example ```tsx "use client" import { Bar, BarChart, CartesianGrid, XAxis } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { month: "Jan", revenue: 1500 }, { month: "Feb", revenue: 3200 }, { month: "Mar", revenue: 2900 }, { month: "Apr", revenue: 2100 }, { month: "May", revenue: 4000 }, { month: "Jun", revenue: 3700 }, { month: "Jul", revenue: 4300 }, { month: "Aug", revenue: 4900 }, { month: "Sep", revenue: 4700 }, { month: "Oct", revenue: 5200 }, { month: "Nov", revenue: 6000 }, { month: "Dec", revenue: 7200 }, ] const chartConfig = { revenue: { label: "Revenue", color: "var(--chart-2)", }, expenses: { label: "Expenses", color: "var(--chart-3)", }, } satisfies ChartConfig export default function ChartDemo() { const totalRevenue = chartData.reduce((sum, item) => sum + item.revenue, 0) const averageRevenue = totalRevenue / chartData.length const highestRevenue = Math.max(...chartData.map((item) => item.revenue)) return ( Monthly Revenue Performance overview for 2024 } />
Total Revenue: ${totalRevenue.toLocaleString()}
Monthly Average: $ {averageRevenue.toLocaleString(undefined, { maximumFractionDigits: 0, })}
Highest Month: ${highestRevenue.toLocaleString()}
) } ``` ## About The Chart is built on top of [`recharts`](https://recharts.org), which is a React component library for building charts. ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/chart ``` **Install the following dependencies:** ```bash npm install recharts ``` **Copy and paste the following code into your project.** ## Examples ### Area Chart ### Example ```tsx "use client" import { TrendingUpIcon } from "lucide-react" import { Area, AreaChart, CartesianGrid, XAxis } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { month: "Jan", revenue: 1500 }, { month: "Feb", revenue: 3200 }, { month: "Mar", revenue: 2900 }, { month: "Apr", revenue: 2100 }, { month: "May", revenue: 4000 }, { month: "Jun", revenue: 3700 }, { month: "Jul", revenue: 4300 }, { month: "Aug", revenue: 4900 }, { month: "Sep", revenue: 4700 }, { month: "Oct", revenue: 5200 }, { month: "Nov", revenue: 6000 }, { month: "Dec", revenue: 7200 }, ] const chartConfig = { revenue: { label: "Revenue", color: "var(--chart-1)", }, } satisfies ChartConfig export default function ChartAreaDemo() { const totalRevenue = chartData.reduce((sum, item) => sum + item.revenue, 0) const averageRevenue = totalRevenue / chartData.length const lastMonthGrowth = ((chartData[11].revenue - chartData[10].revenue) / chartData[10].revenue) * 100 return ( Monthly Revenue Trend Performance overview for 2024 value.slice(0, 3)} /> } />
Total Revenue: ${totalRevenue.toLocaleString()}
Monthly Average: $ {averageRevenue.toLocaleString(undefined, { maximumFractionDigits: 0, })}
Month-over-month growth: {lastMonthGrowth.toFixed(1)}%
) } ``` ### Bar Chart ### Example ```tsx "use client" import { TrendingUpIcon } from "lucide-react" import { Bar, BarChart, CartesianGrid, Legend, XAxis } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { month: "Jan", revenue: 1500, expenses: 1200 }, { month: "Feb", revenue: 3200, expenses: 2800 }, { month: "Mar", revenue: 2900, expenses: 2500 }, { month: "Apr", revenue: 2100, expenses: 1900 }, { month: "May", revenue: 4000, expenses: 3500 }, { month: "Jun", revenue: 3700, expenses: 3200 }, ] const chartConfig = { revenue: { label: "Revenue", color: "var(--chart-1)", }, expenses: { label: "Expenses", color: "var(--chart-3)", }, } satisfies ChartConfig export default function ChartBarDemo() { const totalRevenue = chartData.reduce((sum, item) => sum + item.revenue, 0) const totalExpenses = chartData.reduce((sum, item) => sum + item.expenses, 0) const netProfit = totalRevenue - totalExpenses const profitMargin = (netProfit / totalRevenue) * 100 return ( Revenue vs Expenses First half of 2024 value.slice(0, 3)} /> } />
Net Profit: ${netProfit.toLocaleString()}
Profit Margin: {profitMargin.toFixed(1)}%
Total Expenses: ${totalExpenses.toLocaleString()}
) } ``` ### Line Chart ### Example ```tsx "use client" import { CartesianGrid, Legend, Line, LineChart, XAxis } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { month: "Jan", users: 100, activeUsers: 80, newUsers: 20 }, { month: "Feb", users: 120, activeUsers: 90, newUsers: 30 }, { month: "Mar", users: 150, activeUsers: 100, newUsers: 50 }, { month: "Apr", users: 200, activeUsers: 140, newUsers: 60 }, { month: "May", users: 250, activeUsers: 180, newUsers: 70 }, { month: "Jun", users: 300, activeUsers: 220, newUsers: 80 }, ] const chartConfig = { users: { label: "Total Users", color: "var(--chart-1)", }, activeUsers: { label: "Active Users", color: "var(--chart-2)", }, newUsers: { label: "New Users", color: "var(--chart-3)", }, } satisfies ChartConfig export default function ChartLineDemo() { const totalUsers = chartData[chartData.length - 1].users const totalActiveUsers = chartData[chartData.length - 1].activeUsers const userGrowth = ((chartData[5].users - chartData[0].users) / chartData[0].users) * 100 const activeUsersRate = (totalActiveUsers / totalUsers) * 100 return ( User Growth User metrics for first half of 2024 value.slice(0, 3)} /> } />
Total Users: {totalUsers.toLocaleString()}
Active Users Rate: {activeUsersRate.toFixed(1)}%
6-Month Growth: {userGrowth.toFixed(1)}%
) } ``` ### Pie Chart ### Example ```tsx "use client" import { Cell, Legend, Pie, PieChart } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { category: "Sales", amount: 4000, fill: "var(--chart-1)" }, { category: "Marketing", amount: 3000, fill: "var(--chart-2)" }, { category: "IT", amount: 2000, fill: "var(--chart-3)" }, { category: "HR", amount: 1000, fill: "var(--chart-4)" }, { category: "Operations", amount: 1000, fill: "var(--chart-5)" }, ] const chartConfig = { sales: { label: "Sales", color: "var(--chart-1)", }, marketing: { label: "Marketing", color: "var(--chart-2)", }, it: { label: "IT", color: "var(--chart-3)", }, hr: { label: "HR", color: "var(--chart-4)", }, operations: { label: "Operations", color: "var(--chart-5)", }, } satisfies ChartConfig export default function ChartPieDemo() { const totalBudget = chartData.reduce((sum, item) => sum + item.amount, 0) const highestBudget = Math.max(...chartData.map((item) => item.amount)) const highestCategory = chartData.find( (item) => item.amount === highestBudget )?.category return ( Budget Distribution Department budget allocation for 2024 } /> {chartData.map((entry, index) => ( ))}
Total Budget: ${totalBudget.toLocaleString()}
Largest Department: {highestCategory}
Highest Budget: ${highestBudget.toLocaleString()}
) } ``` ### Radar Chart ### Example ```tsx "use client" import { Legend, PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { category: "Performance", a: 90, b: 60 }, { category: "Reliability", a: 75, b: 90 }, { category: "Scalability", a: 95, b: 90 }, { category: "Security", a: 88, b: 65 }, { category: "Usability", a: 92, b: 88 }, ] const chartConfig = { a: { label: "Product A", color: "var(--chart-1)", }, b: { label: "Product B", color: "var(--chart-2)", }, } satisfies ChartConfig export default function ChartRadarDemo() { const productAAverage = chartData.reduce((sum, item) => sum + item.a, 0) / chartData.length const productBAverage = chartData.reduce((sum, item) => sum + item.b, 0) / chartData.length const bestPerformer = productAAverage > productBAverage ? "Product A" : "Product B" return ( Product Comparison Performance metrics across key categories } />
Best Overall: {bestPerformer}
Product A Average: {productAAverage.toFixed(1)}%
Product B Average: {productBAverage.toFixed(1)}%
) } ``` ### Radial Bar Chart ### Example ```tsx "use client" import { RadialBar, RadialBarChart } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { browser: "chrome", visitors: 540, fill: "var(--color-chrome)" }, { browser: "safari", visitors: 410, fill: "var(--color-safari)" }, { browser: "firefox", visitors: 262, fill: "var(--color-firefox)" }, { browser: "edge", visitors: 160, fill: "var(--color-edge)" }, { browser: "other", visitors: 100, fill: "var(--color-other)" }, ] const chartConfig = { visitors: { label: "Visitors", }, chrome: { label: "Chrome", color: "var(--chart-1)", }, safari: { label: "Safari", color: "var(--chart-2)", }, firefox: { label: "Firefox", color: "var(--chart-3)", }, edge: { label: "Edge", color: "var(--chart-4)", }, other: { label: "Other", color: "var(--chart-5)", }, } satisfies ChartConfig export default function ChartRadialBarDemo() { const totalVisitors = chartData.reduce((sum, item) => sum + item.visitors, 0) const highestVisitors = Math.max(...chartData.map((item) => item.visitors)) const topBrowser = chartData.find( (item) => item.visitors === highestVisitors )?.browser return ( Browser Usage Visitor distribution by browser } />
Total Visitors: {totalVisitors.toLocaleString()}
Most Used Browser: {topBrowser}
Peak Visitors: {highestVisitors.toLocaleString()}
) } ``` ### Scatter Chart ### Example ```tsx "use client" import { CartesianGrid, Scatter, ScatterChart, XAxis, YAxis, ZAxis, } from "recharts" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { population: 850000, price: 425000, city: "San Francisco" }, { population: 2700000, price: 385000, city: "Chicago" }, { population: 8400000, price: 750000, city: "New York" }, { population: 4000000, price: 890000, city: "Los Angeles" }, { population: 2300000, price: 350000, city: "Houston" }, { population: 1600000, price: 420000, city: "Philadelphia" }, { population: 730000, price: 480000, city: "Seattle" }, { population: 690000, price: 445000, city: "Boston" }, { population: 710000, price: 320000, city: "Denver" }, { population: 950000, price: 295000, city: "Austin" }, ] const chartConfig = { scatter: { label: "Cities", color: "var(--chart-1)", }, } satisfies ChartConfig export default function ChartScatterDemo() { const averagePrice = chartData.reduce((sum, item) => sum + item.price, 0) / chartData.length const highestPrice = Math.max(...chartData.map((item) => item.price)) const mostExpensiveCity = chartData.find( (item) => item.price === highestPrice )?.city return ( Housing Market Analysis Population vs House Prices in Major Cities `$${(value / 1000).toFixed(0)}k`} /> `${(value / 1000000).toFixed(1)}M`} /> } />
Most Expensive City: {mostExpensiveCity}
Average House Price: ${averagePrice.toLocaleString()}
Highest House Price: ${highestPrice.toLocaleString()}
) } ``` --- ### Checkbox Displays a box that can be checked or unchecked by the user. ### References - [Docs](https://base-ui.com/react/components/checkbox) ### Example ```tsx import { Checkbox } from "@/components/ui/checkbox" export default function CheckboxDemo() { return } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/checkbox ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Checkbox } from "@/components/ui/checkbox" ``` ```tsx title="Anatomy" ``` ## Examples ### With Label ### Example ```tsx import { Checkbox } from "@/components/ui/checkbox" import { Label } from "@/components/ui/label" export default function CheckboxWithLabelDemo() { return ( ) } ``` --- ### Disabled ### Example ```tsx import { Checkbox } from "@/components/ui/checkbox" export default function CheckboxDisabledDemo() { return } ``` --- ### Error ### Example ```tsx import { Checkbox } from "@/components/ui/checkbox" export default function CheckboxErrorDemo() { return } ``` --- ### Checkbox Group Manages selection state across multiple checkboxes. ### References - [Docs](https://base-ui.com/react/components/checkbox-group) ### Example ```tsx "use client" import { useState } from "react" import { Checkbox } from "@/components/ui/checkbox" import { CheckboxGroup } from "@/components/ui/checkbox-group" import { Label } from "@/components/ui/label" const groceries = ["milk", "cheese", "bread", "apples"] export default function CheckboxGroupDemo() { const [checkedItems, setCheckedItems] = useState([]) return ( setCheckedItems(value)} allValues={groceries} > {groceries.map((grocery) => ( ))} ) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/checkbox-group ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { CheckboxGroup } from "@/components/ui/checkbox-group" ``` ```tsx title="Anatomy" ``` --- ### Collapsible Display content in a collapsible container. ### References - [Docs](https://base-ui.com/react/components/collapsible) ### Example ```tsx import { useState } from "react" import { ChevronRightIcon } from "lucide-react" import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible" import { cn } from "@/lib/utils" export default function CollapsibleDemo() { const [open, setOpen] = useState(false) return ( Show recovery keys
  1. A5FD-7K1B-ZR92
  2. CQ9P-L02R-W8NV
  3. 8FD5-GH2B-0SA7
) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/collapsible ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible" ``` ```tsx title="Anatomy" ``` --- ### Combobox Autocomplete component for selecting items from a list. ### References - [Docs](https://base-ui.com/react/components/combobox) ### Example ```tsx import * as React from "react" import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@/components/ui/combobox" import { Label } from "@/components/ui/label" export default function ComboboxDemo() { return (
No fruits found. {(item: string) => ( {item} )}
) } const fruits = [ "Apple", "Banana", "Orange", "Pineapple", "Grape", "Mango", "Strawberry", "Blueberry", "Raspberry", "Blackberry", "Cherry", "Peach", "Pear", "Plum", "Kiwi", "Watermelon", "Cantaloupe", "Honeydew", "Papaya", "Guava", "Lychee", "Pomegranate", "Apricot", "Grapefruit", "Passionfruit", ] ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/combobox ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Combobox, ComboboxChip, ComboboxChips, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxTrigger, ComboboxValue, } from "@/components/ui/combobox" ``` ```tsx title="Anatomy" ``` ## Examples ### Input inside popup ### Example ```tsx import * as React from "react" import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxTrigger, ComboboxValue, } from "@/components/ui/combobox" import { Label } from "@/components/ui/label" export default function ComboboxInputInsidePopupDemo() { return (
No countries found. {(country: Country) => ( {country.label ?? country.value} )}
) } interface Country { code: string value: string | null continent: string label: string } const countries: Country[] = [ { code: "", value: null, continent: "", label: "Select country" }, { code: "af", value: "afghanistan", label: "Afghanistan", continent: "Asia" }, { code: "al", value: "albania", label: "Albania", continent: "Europe" }, { code: "dz", value: "algeria", label: "Algeria", continent: "Africa" }, { code: "ad", value: "andorra", label: "Andorra", continent: "Europe" }, { code: "ao", value: "angola", label: "Angola", continent: "Africa" }, { code: "ar", value: "argentina", label: "Argentina", continent: "South America", }, { code: "am", value: "armenia", label: "Armenia", continent: "Asia" }, { code: "au", value: "australia", label: "Australia", continent: "Oceania" }, { code: "at", value: "austria", label: "Austria", continent: "Europe" }, { code: "az", value: "azerbaijan", label: "Azerbaijan", continent: "Asia" }, { code: "bs", value: "bahamas", label: "Bahamas", continent: "North America", }, { code: "bh", value: "bahrain", label: "Bahrain", continent: "Asia" }, { code: "bd", value: "bangladesh", label: "Bangladesh", continent: "Asia" }, { code: "bb", value: "barbados", label: "Barbados", continent: "North America", }, { code: "by", value: "belarus", label: "Belarus", continent: "Europe" }, { code: "be", value: "belgium", label: "Belgium", continent: "Europe" }, { code: "bz", value: "belize", label: "Belize", continent: "North America" }, { code: "bj", value: "benin", label: "Benin", continent: "Africa" }, { code: "bt", value: "bhutan", label: "Bhutan", continent: "Asia" }, { code: "bo", value: "bolivia", label: "Bolivia", continent: "South America", }, { code: "ba", value: "bosnia-and-herzegovina", label: "Bosnia and Herzegovina", continent: "Europe", }, { code: "bw", value: "botswana", label: "Botswana", continent: "Africa" }, { code: "br", value: "brazil", label: "Brazil", continent: "South America" }, { code: "bn", value: "brunei", label: "Brunei", continent: "Asia" }, { code: "bg", value: "bulgaria", label: "Bulgaria", continent: "Europe" }, { code: "bf", value: "burkina-faso", label: "Burkina Faso", continent: "Africa", }, { code: "bi", value: "burundi", label: "Burundi", continent: "Africa" }, { code: "kh", value: "cambodia", label: "Cambodia", continent: "Asia" }, { code: "cm", value: "cameroon", label: "Cameroon", continent: "Africa" }, { code: "ca", value: "canada", label: "Canada", continent: "North America" }, { code: "cv", value: "cape-verde", label: "Cape Verde", continent: "Africa" }, { code: "cf", value: "central-african-republic", label: "Central African Republic", continent: "Africa", }, { code: "td", value: "chad", label: "Chad", continent: "Africa" }, { code: "cl", value: "chile", label: "Chile", continent: "South America" }, { code: "cn", value: "china", label: "China", continent: "Asia" }, { code: "co", value: "colombia", label: "Colombia", continent: "South America", }, { code: "km", value: "comoros", label: "Comoros", continent: "Africa" }, { code: "cg", value: "congo", label: "Congo", continent: "Africa" }, { code: "cr", value: "costa-rica", label: "Costa Rica", continent: "North America", }, { code: "hr", value: "croatia", label: "Croatia", continent: "Europe" }, { code: "cu", value: "cuba", label: "Cuba", continent: "North America" }, { code: "cy", value: "cyprus", label: "Cyprus", continent: "Asia" }, { code: "cz", value: "czech-republic", label: "Czech Republic", continent: "Europe", }, { code: "dk", value: "denmark", label: "Denmark", continent: "Europe" }, { code: "dj", value: "djibouti", label: "Djibouti", continent: "Africa" }, { code: "dm", value: "dominica", label: "Dominica", continent: "North America", }, { code: "do", value: "dominican-republic", label: "Dominican Republic", continent: "North America", }, { code: "ec", value: "ecuador", label: "Ecuador", continent: "South America", }, { code: "eg", value: "egypt", label: "Egypt", continent: "Africa" }, { code: "sv", value: "el-salvador", label: "El Salvador", continent: "North America", }, { code: "gq", value: "equatorial-guinea", label: "Equatorial Guinea", continent: "Africa", }, { code: "er", value: "eritrea", label: "Eritrea", continent: "Africa" }, { code: "ee", value: "estonia", label: "Estonia", continent: "Europe" }, { code: "et", value: "ethiopia", label: "Ethiopia", continent: "Africa" }, { code: "fj", value: "fiji", label: "Fiji", continent: "Oceania" }, { code: "fi", value: "finland", label: "Finland", continent: "Europe" }, { code: "fr", value: "france", label: "France", continent: "Europe" }, { code: "ga", value: "gabon", label: "Gabon", continent: "Africa" }, { code: "gm", value: "gambia", label: "Gambia", continent: "Africa" }, { code: "ge", value: "georgia", label: "Georgia", continent: "Asia" }, { code: "de", value: "germany", label: "Germany", continent: "Europe" }, { code: "gh", value: "ghana", label: "Ghana", continent: "Africa" }, { code: "gr", value: "greece", label: "Greece", continent: "Europe" }, { code: "gd", value: "grenada", label: "Grenada", continent: "North America", }, { code: "gt", value: "guatemala", label: "Guatemala", continent: "North America", }, { code: "gn", value: "guinea", label: "Guinea", continent: "Africa" }, { code: "gw", value: "guinea-bissau", label: "Guinea-Bissau", continent: "Africa", }, { code: "gy", value: "guyana", label: "Guyana", continent: "South America" }, { code: "ht", value: "haiti", label: "Haiti", continent: "North America" }, { code: "hn", value: "honduras", label: "Honduras", continent: "North America", }, { code: "hu", value: "hungary", label: "Hungary", continent: "Europe" }, { code: "is", value: "iceland", label: "Iceland", continent: "Europe" }, { code: "in", value: "india", label: "India", continent: "Asia" }, { code: "id", value: "indonesia", label: "Indonesia", continent: "Asia" }, { code: "ir", value: "iran", label: "Iran", continent: "Asia" }, { code: "iq", value: "iraq", label: "Iraq", continent: "Asia" }, { code: "ie", value: "ireland", label: "Ireland", continent: "Europe" }, { code: "il", value: "israel", label: "Israel", continent: "Asia" }, { code: "it", value: "italy", label: "Italy", continent: "Europe" }, { code: "jm", value: "jamaica", label: "Jamaica", continent: "North America", }, { code: "jp", value: "japan", label: "Japan", continent: "Asia" }, { code: "jo", value: "jordan", label: "Jordan", continent: "Asia" }, { code: "kz", value: "kazakhstan", label: "Kazakhstan", continent: "Asia" }, { code: "ke", value: "kenya", label: "Kenya", continent: "Africa" }, { code: "kw", value: "kuwait", label: "Kuwait", continent: "Asia" }, { code: "kg", value: "kyrgyzstan", label: "Kyrgyzstan", continent: "Asia" }, { code: "la", value: "laos", label: "Laos", continent: "Asia" }, { code: "lv", value: "latvia", label: "Latvia", continent: "Europe" }, { code: "lb", value: "lebanon", label: "Lebanon", continent: "Asia" }, { code: "ls", value: "lesotho", label: "Lesotho", continent: "Africa" }, { code: "lr", value: "liberia", label: "Liberia", continent: "Africa" }, { code: "ly", value: "libya", label: "Libya", continent: "Africa" }, { code: "li", value: "liechtenstein", label: "Liechtenstein", continent: "Europe", }, { code: "lt", value: "lithuania", label: "Lithuania", continent: "Europe" }, { code: "lu", value: "luxembourg", label: "Luxembourg", continent: "Europe" }, { code: "mg", value: "madagascar", label: "Madagascar", continent: "Africa" }, { code: "mw", value: "malawi", label: "Malawi", continent: "Africa" }, { code: "my", value: "malaysia", label: "Malaysia", continent: "Asia" }, { code: "mv", value: "maldives", label: "Maldives", continent: "Asia" }, { code: "ml", value: "mali", label: "Mali", continent: "Africa" }, { code: "mt", value: "malta", label: "Malta", continent: "Europe" }, { code: "mh", value: "marshall-islands", label: "Marshall Islands", continent: "Oceania", }, { code: "mr", value: "mauritania", label: "Mauritania", continent: "Africa" }, { code: "mu", value: "mauritius", label: "Mauritius", continent: "Africa" }, { code: "mx", value: "mexico", label: "Mexico", continent: "North America" }, { code: "fm", value: "micronesia", label: "Micronesia", continent: "Oceania", }, { code: "md", value: "moldova", label: "Moldova", continent: "Europe" }, { code: "mc", value: "monaco", label: "Monaco", continent: "Europe" }, { code: "mn", value: "mongolia", label: "Mongolia", continent: "Asia" }, { code: "me", value: "montenegro", label: "Montenegro", continent: "Europe" }, { code: "ma", value: "morocco", label: "Morocco", continent: "Africa" }, { code: "mz", value: "mozambique", label: "Mozambique", continent: "Africa" }, { code: "mm", value: "myanmar", label: "Myanmar", continent: "Asia" }, { code: "na", value: "namibia", label: "Namibia", continent: "Africa" }, { code: "nr", value: "nauru", label: "Nauru", continent: "Oceania" }, { code: "np", value: "nepal", label: "Nepal", continent: "Asia" }, { code: "nl", value: "netherlands", label: "Netherlands", continent: "Europe", }, { code: "nz", value: "new-zealand", label: "New Zealand", continent: "Oceania", }, { code: "ni", value: "nicaragua", label: "Nicaragua", continent: "North America", }, { code: "ne", value: "niger", label: "Niger", continent: "Africa" }, { code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" }, { code: "kp", value: "north-korea", label: "North Korea", continent: "Asia" }, { code: "mk", value: "north-macedonia", label: "North Macedonia", continent: "Europe", }, { code: "no", value: "norway", label: "Norway", continent: "Europe" }, { code: "om", value: "oman", label: "Oman", continent: "Asia" }, { code: "pk", value: "pakistan", label: "Pakistan", continent: "Asia" }, { code: "pw", value: "palau", label: "Palau", continent: "Oceania" }, { code: "ps", value: "palestine", label: "Palestine", continent: "Asia" }, { code: "pa", value: "panama", label: "Panama", continent: "North America" }, { code: "pg", value: "papua-new-guinea", label: "Papua New Guinea", continent: "Oceania", }, { code: "py", value: "paraguay", label: "Paraguay", continent: "South America", }, { code: "pe", value: "peru", label: "Peru", continent: "South America" }, { code: "ph", value: "philippines", label: "Philippines", continent: "Asia" }, { code: "pl", value: "poland", label: "Poland", continent: "Europe" }, { code: "pt", value: "portugal", label: "Portugal", continent: "Europe" }, { code: "qa", value: "qatar", label: "Qatar", continent: "Asia" }, { code: "ro", value: "romania", label: "Romania", continent: "Europe" }, { code: "ru", value: "russia", label: "Russia", continent: "Europe" }, { code: "rw", value: "rwanda", label: "Rwanda", continent: "Africa" }, { code: "ws", value: "samoa", label: "Samoa", continent: "Oceania" }, { code: "sm", value: "san-marino", label: "San Marino", continent: "Europe" }, { code: "sa", value: "saudi-arabia", label: "Saudi Arabia", continent: "Asia", }, { code: "sn", value: "senegal", label: "Senegal", continent: "Africa" }, { code: "rs", value: "serbia", label: "Serbia", continent: "Europe" }, { code: "sc", value: "seychelles", label: "Seychelles", continent: "Africa" }, { code: "sl", value: "sierra-leone", label: "Sierra Leone", continent: "Africa", }, { code: "sg", value: "singapore", label: "Singapore", continent: "Asia" }, { code: "sk", value: "slovakia", label: "Slovakia", continent: "Europe" }, { code: "si", value: "slovenia", label: "Slovenia", continent: "Europe" }, { code: "sb", value: "solomon-islands", label: "Solomon Islands", continent: "Oceania", }, { code: "so", value: "somalia", label: "Somalia", continent: "Africa" }, { code: "za", value: "south-africa", label: "South Africa", continent: "Africa", }, { code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" }, { code: "ss", value: "south-sudan", label: "South Sudan", continent: "Africa", }, { code: "es", value: "spain", label: "Spain", continent: "Europe" }, { code: "lk", value: "sri-lanka", label: "Sri Lanka", continent: "Asia" }, { code: "sd", value: "sudan", label: "Sudan", continent: "Africa" }, { code: "sr", value: "suriname", label: "Suriname", continent: "South America", }, { code: "se", value: "sweden", label: "Sweden", continent: "Europe" }, { code: "ch", value: "switzerland", label: "Switzerland", continent: "Europe", }, { code: "sy", value: "syria", label: "Syria", continent: "Asia" }, { code: "tw", value: "taiwan", label: "Taiwan", continent: "Asia" }, { code: "tj", value: "tajikistan", label: "Tajikistan", continent: "Asia" }, { code: "tz", value: "tanzania", label: "Tanzania", continent: "Africa" }, { code: "th", value: "thailand", label: "Thailand", continent: "Asia" }, { code: "tl", value: "timor-leste", label: "Timor-Leste", continent: "Asia" }, { code: "tg", value: "togo", label: "Togo", continent: "Africa" }, { code: "to", value: "tonga", label: "Tonga", continent: "Oceania" }, { code: "tt", value: "trinidad-and-tobago", label: "Trinidad and Tobago", continent: "North America", }, { code: "tn", value: "tunisia", label: "Tunisia", continent: "Africa" }, { code: "tr", value: "turkey", label: "Turkey", continent: "Asia" }, { code: "tm", value: "turkmenistan", label: "Turkmenistan", continent: "Asia", }, { code: "tv", value: "tuvalu", label: "Tuvalu", continent: "Oceania" }, { code: "ug", value: "uganda", label: "Uganda", continent: "Africa" }, { code: "ua", value: "ukraine", label: "Ukraine", continent: "Europe" }, { code: "ae", value: "united-arab-emirates", label: "United Arab Emirates", continent: "Asia", }, { code: "gb", value: "united-kingdom", label: "United Kingdom", continent: "Europe", }, { code: "us", value: "united-states", label: "United States", continent: "North America", }, { code: "uy", value: "uruguay", label: "Uruguay", continent: "South America", }, { code: "uz", value: "uzbekistan", label: "Uzbekistan", continent: "Asia" }, { code: "vu", value: "vanuatu", label: "Vanuatu", continent: "Oceania" }, { code: "va", value: "vatican-city", label: "Vatican City", continent: "Europe", }, { code: "ve", value: "venezuela", label: "Venezuela", continent: "South America", }, { code: "vn", value: "vietnam", label: "Vietnam", continent: "Asia" }, { code: "ye", value: "yemen", label: "Yemen", continent: "Asia" }, { code: "zm", value: "zambia", label: "Zambia", continent: "Africa" }, { code: "zw", value: "zimbabwe", label: "Zimbabwe", continent: "Africa" }, ] ``` --- ### Multiple ### Example ```tsx import * as React from "react" import { Combobox, ComboboxChip, ComboboxChips, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxValue, } from "@/components/ui/combobox" import { Label } from "@/components/ui/label" export default function ComboboxMultipleDemo() { const containerRef = React.useRef(null) return (
{(value: ProgrammingLanguage[]) => ( {value.length > 0 && (
{value.map((language) => ( {language.value} ))}
)}
)}
No languages found. {(language: ProgrammingLanguage) => ( {language.value} )}
) } interface ProgrammingLanguage { id: string value: string } const langs: ProgrammingLanguage[] = [ { id: "js", value: "JavaScript" }, { id: "ts", value: "TypeScript" }, { id: "py", value: "Python" }, { id: "java", value: "Java" }, { id: "cpp", value: "C++" }, { id: "cs", value: "C#" }, { id: "php", value: "PHP" }, { id: "ruby", value: "Ruby" }, { id: "go", value: "Go" }, { id: "rust", value: "Rust" }, { id: "swift", value: "Swift" }, ] ``` --- ### Creatable ### Example ```tsx import * as React from "react" import { Plus } from "lucide-react" import { Button } from "@/components/ui/button" import { Combobox, ComboboxChip, ComboboxChips, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxValue, } from "@/components/ui/combobox" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogTitle, } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" export default function ComboboxCreatableDemo() { const id = React.useId() const [labels, setLabels] = React.useState(initialLabels) const [selected, setSelected] = React.useState([]) const [query, setQuery] = React.useState("") const [openDialog, setOpenDialog] = React.useState(false) const containerRef = React.useRef(null) const createInputRef = React.useRef(null) const pendingQueryRef = React.useRef("") function handleCreate() { const input = createInputRef.current const value = input ? input.value.trim() : "" if (!value) { return } const normalized = value.toLocaleLowerCase() const baseId = normalized.replace(/\s+/g, "-") const existing = labels.find( (l) => l.value.trim().toLocaleLowerCase() === normalized ) if (existing) { setSelected((prev) => prev.some((i) => i.id === existing.id) ? prev : [...prev, existing] ) setOpenDialog(false) setQuery("") return } // Ensure we don't collide with an existing id (e.g., value "docs" vs. existing id "docs") const existingIds = new Set(labels.map((l) => l.id)) let uniqueId = baseId if (existingIds.has(uniqueId)) { let i = 2 while (existingIds.has(`${baseId}-${i}`)) { i += 1 } uniqueId = `${baseId}-${i}` } const newItem: LabelItem = { id: uniqueId, value } if (!selected.find((item) => item.id === newItem.id)) { setLabels((prev) => [...prev, newItem]) setSelected((prev) => [...prev, newItem]) } setOpenDialog(false) setQuery("") } function handleCreateSubmit(event: React.FormEvent) { event.preventDefault() handleCreate() } const trimmed = query.trim() const lowered = trimmed.toLocaleLowerCase() const exactExists = labels.some( (l) => l.value.trim().toLocaleLowerCase() === lowered ) // Show the creatable item alongside matches if there's no exact match const itemsForView: Array = trimmed !== "" && !exactExists ? [ ...labels, { creatable: trimmed, id: `create:${lowered}`, value: `Create "${trimmed}"`, }, ] : labels return ( { const next = value as LabelItem[] const last = next[next.length - 1] if (last && last.creatable) { pendingQueryRef.current = last.creatable setOpenDialog(true) return } const clean = next.filter((i: LabelItem) => !i.creatable) setSelected(clean) setQuery("") }} value={selected} inputValue={query} onInputValueChange={setQuery} onOpenChange={(open, details) => { if ("key" in details.event && details.event.key === "Enter") { // When pressing Enter: // - If the typed value exactly matches an existing item, add that item to the selected chips // - Otherwise, create a new item if (trimmed === "") { return } const existing = labels.find( (l) => l.value.trim().toLocaleLowerCase() === lowered ) if (existing) { setSelected((prev) => prev.some((i) => i.id === existing.id) ? prev : [...prev, existing] ) setQuery("") return } pendingQueryRef.current = trimmed setOpenDialog(true) } }} >
{(value: LabelItem[]) => ( {value.length > 0 && (
{value.map((label) => ( {label.value} ))}
)}
)}
No labels found. {(item: LabelItem) => item.creatable ? ( Create "{item.creatable}" ) : ( {item.value} ) }
Create new label Add a new label to select.
) } interface LabelItem { creatable?: string id: string value: string } const initialLabels: LabelItem[] = [ { id: "bug", value: "bug" }, { id: "docs", value: "documentation" }, { id: "enhancement", value: "enhancement" }, { id: "help-wanted", value: "help wanted" }, { id: "good-first-issue", value: "good first issue" }, ] ``` --- ### Command A searchable interface for quickly executing commands or actions. ### References - [Docs](https://github.com/pacocoursey/cmdk) ### Example ```tsx import { ArrowRightIcon, LayoutGridIcon, PlusIcon, UsersIcon, } from "lucide-react" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, } from "@/components/ui/command" import { Kbd } from "@/components/ui/kbd" export default function CommandDemo() { return ( No results found. Search projects... P Create new project... C Search teams... Create new team... T Go to home Go to profile Go to settings Go to billing ) } ``` ## About The Command is built on top of [`cmdk-base`](https://cmdk-base.vercel.app), which is ported from [`cmdk`](https://github.com/pacocoursey/cmdk), originally created by [@pacocoursey](https://x.com/pacocoursey). ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/command ``` **Install dependencies** ```bash title="Terminal" npm install cmdk-base ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, } from "@/components/ui/command" ``` ```tsx title="Anatomy" ``` ## Examples ### Dialog ### Example ```tsx "use client" import { useEffect, useState } from "react" import { ArrowRightIcon, LayoutGridIcon, PlusIcon, UsersIcon, } from "lucide-react" import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, } from "@/components/ui/command" import { Kbd } from "@/components/ui/kbd" export default function CommandDialogDemo() { const [open, setOpen] = useState(false) useEffect(() => { const down = (e: KeyboardEvent) => { if (e.key === "j" && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen((open) => !open) } } document.addEventListener("keydown", down) return () => document.removeEventListener("keydown", down) }, []) return ( <>
+J
No results found. Search projects... P Create new project... C Search teams... Create new team... T Go to home Go to profile Go to settings Go to billing ) } ``` --- ### Context Menu Used to provide options specific to an element or area. ### References - [Docs](https://base-ui.com/react/components/menu) ### Example ```tsx "use client" import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, } from "@/components/ui/context-menu" export default function ContextMenuDemo() { return (
Right Click Here Back ⌘[ Forward ⌘] Reload ⌘R More Save As Print Cast Inspect Settings Always on Top Show full URL Zoom 50% 100% 150%
) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/context-menu ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, } from "@/components/ui/context-menu" ``` ```tsx title="Anatomy" ``` --- ### Date Picker A date picker component. ### Example ```tsx import * as React from "react" import dayjs from "dayjs" import { CalendarIcon, ChevronsUpDownIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Calendar } from "@/components/ui/calendar" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" export default function DatePickerDemo() { const [open, setOpen] = React.useState(false) const [value, setValue] = React.useState(undefined) return ( ( )} /> ) } ``` ## Installation The date picker component is a composition of the `Calendar` and `Popover` components. Follow installation instructions for the [`Calendar`](/docs/components/calendar) and [`Popover`](/docs/components/popover) components. ## Usage ```tsx title="Imports" import { Calendar } from "@/components/ui/calendar" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" ``` ```tsx title="Anatomy" ``` --- ### Dialog A modal window for displaying content. ### References - [Docs](https://base-ui.com/react/components/dialog) ### Example ```tsx import { Button } from "@/components/ui/button" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" export default function DialogDemo() { return ( } /> Privacy Policy Please read our privacy policy carefully.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ultricies, odio quis blandit vestibulum, orci elit suscipit urna, at lobortis arcu enim vel purus. Maecenas luctus sem dui, lobortis dignissim enim consequat in. Nullam a volutpat purus. Aenean pellentesque eros nec rutrum suscipit. Fusce ac lectus volutpat, feugiat nulla et, suscipit dui. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut maximus, risus et convallis placerat, risus urna feugiat neque, in vestibulum leo arcu vitae justo. Duis magna mi, maximus at neque sed, tempor congue ligula. In iaculis metus nec euismod egestas. Donec id porttitor nulla. Donec feugiat iaculis lacus, ut elementum dui faucibus sed. Sed ut ipsum non tellus dignissim accumsan. Vivamus luctus malesuada lacus sed dictum.

Sed consectetur nibh mollis, ornare magna et, dictum tellus. Nam viverra dui a enim iaculis, sed blandit orci consectetur. Maecenas et nisi eleifend velit pretium eleifend sit amet eget nisl. Vestibulum eget ipsum semper purus pulvinar iaculis. Sed ut odio eu felis porttitor ultrices eu sed odio. Nullam lorem sapien, pellentesque convallis libero vel, tempus accumsan nisi. Morbi efficitur ex vitae felis luctus cursus. Suspendisse nibh neque, gravida sed elementum ullamcorper, gravida in nisi. Donec et luctus metus. Fusce sed est dictum, imperdiet nisi eu, suscipit odio. In id enim at tortor malesuada vulputate eu eu sem. Mauris blandit faucibus euismod.

Curabitur quam tortor, tristique euismod finibus viverra, bibendum sit amet nisl. Nulla lobortis pharetra mauris, ac semper urna tempor et. Maecenas enim magna, suscipit nec metus id, ornare pulvinar dolor. Cras rhoncus ante sit amet tempus luctus. Donec in nisl a dolor auctor tincidunt. Cras at arcu tortor. Pellentesque ante felis, convallis sit amet erat id, consectetur consequat sapien. Aliquam volutpat velit in est bibendum, vestibulum commodo leo interdum. Integer sodales ex eu tempus faucibus. Vestibulum ultricies erat vel leo accumsan posuere. Cras commodo felis vitae lacus suscipit, in tristique lectus venenatis. Sed et nibh urna. Praesent vitae eleifend turpis. Fusce sit amet pretium lorem, in tempus elit. Etiam at ornare est. Aenean felis arcu, fermentum scelerisque nibh at, lacinia sagittis neque.
( )} />
) } ``` ## Installation ```bash title="Terminal" npx shadcn@latest add @9ui/dialog ``` **Copy and paste the following code into your project.** ## Usage ```tsx title="Imports" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" ``` ```tsx title="Anatomy" ``` ## Examples ### Nested Dialogs ### Example ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" export default function DialogNestedDemo() { return ( } /> Profile View and edit your profile details.
BB Bora Baloglu
( )} /> Edit Edit the details of the item ( )} /> } />
) } ``` --- ### Drawer Displays a panel that slides out from the side of a screen to reveal more content. ### References - [Docs](https://vaul.emilkowal.ski/getting-started) ### Example ```tsx "use client" import { useState } from "react" import { StarIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer" import { Textarea } from "@/components/ui/textarea" import { cn } from "@/lib/utils" export default function DrawerDemo() { const [rating, setRating] = useState(undefined) const handleChangeRating = (newRating: number) => { if (newRating === rating) { setRating(undefined) } else { setRating(newRating) } } return ( } />
Provide Your Feedback We value your feedback. Please rate your experience and leave a review.
{[1, 2, 3, 4, 5].map((star) => ( handleChangeRating(star)} /> ))}