# Colors (/docs/colors)
## Overview [#overview]
Tokens mirror [Tailwind color conventions](https://tailwindcss.com/docs/colors#core-concepts), so names align with utility classes and stay predictable in your project.
## Core concepts [#core-concepts]
The UI draws from one of nine neutral gray scales. The scale you choose sets backgrounds, borders, and muted tones for the whole theme.
The default palette is `neutral`, anchored at `neutral-50` in light mode and `neutral-950` in dark mode. Preview another scale with the swatches below:
Click a swatch to switch the neutral scale and preview the theme across the docs. Subtle differences between grays are easiest to judge in dark mode.
Build your own color palette
Try the [Theme editor](/themes) to visually customize primary, neutral colors and radius.
## Theme Tokens [#theme-tokens]
These tokens live in your CSS file under `:root` and `.dark`.
### background / foreground [#background--foreground]
The default app background and text color for the page shell, page sections, and default text.
* `--background`
* `--foreground`
### card [#card]
Elevated surfaces and the content inside them, including Card, dashboard panels, and settings panels.
* `--card`
* `--card-foreground`
### popover [#popover]
Floating surfaces and the content inside them, such as Popover, DropdownMenu, ContextMenu, and other overlays.
* `--popover`
* `--popover-foreground`
### primary [#primary]
High-emphasis actions and brand surfaces, including the default Button, selected states, badges, and active accents.
* `--primary`
* `--primary-foreground`
### secondary [#secondary]
Lower-emphasis filled actions and supporting surfaces, such as secondary buttons, secondary badges, and supporting UI.
* `--secondary`
* `--secondary-foreground`
### muted [#muted]
Subtle surfaces and lower-emphasis content for descriptions, placeholders, empty states, helper text, and subdued surfaces.
* `--muted`
* `--muted-foreground`
### accent [#accent]
Interactive hover, focus, and active surfaces for ghost buttons, menu highlight states, hovered rows, and selected items.
* `--accent`
* `--accent-foreground`
### destructive [#destructive]
Destructive actions and error emphasis for destructive buttons, invalid states, and destructive menu items.
* `--destructive`
* `--destructive-foreground`
### info [#info]
Informational actions and emphasis for informational buttons, badges, alerts, status, etc.
* `--info`
* `--info-foreground`
### success [#success]
Success actions and emphasis for success buttons, badges, alerts, status, etc.
* `--success`
* `--success-foreground`
### warning [#warning]
Warning actions and emphasis for warning buttons, badges, alerts, status, etc.
* `--warning`
* `--warning-foreground`
### border [#border]
Default borders and separators on cards, menus, tables, separators, and layout dividers.
* `--border`
* `--border-foreground`
### input [#input]
Form control borders and input surface treatment on Input, Textarea, Select, and outline-style controls.
* `--input`
* `--input-foreground`
### ring [#ring]
Focus rings and outlines on buttons, inputs, checkboxes, menus, and other focusable controls. It follows the primary color.
* `--ring`
* `--ring-foreground`
### sidebar [#sidebar]
The base sidebar surface and default sidebar text on the Sidebar container and its default content.
* `--sidebar`
* `--sidebar-foreground`
### sidebar-primary [#sidebar-primary]
High-emphasis actions inside the sidebar for active items, icon tiles, badges, and sidebar CTAs.
* `--sidebar-primary`
* `--sidebar-primary-foreground`
### sidebar-accent [#sidebar-accent]
Hover and selected states inside the sidebar for menu hover states, open items, and interactive rows.
* `--sidebar-accent`
* `--sidebar-accent-foreground`
### sidebar-border [#sidebar-border]
Sidebar-specific borders and separators on sidebar headers, groups, and internal dividers.
* `--sidebar-border`
### sidebar-ring [#sidebar-ring]
Sidebar-specific focus rings on focused controls inside the sidebar. It follows the primary color.
* `--sidebar-ring`
### radius [#radius]
The base corner radius scale for cards, inputs, buttons, popovers, and the derived `radius-*` tokens.
Preset theme classes are documented in [Styling](/docs/styling).
# Forms (/docs/forms)
## Picking a form library [#picking-a-form-library]
For detailed guides with examples, validation, and field-type-specific patterns, see the linked pages above.
## Field Context [#field-context]
Form components automatically integrate with `Field` through context. When nested inside a `Field`, they inherit `disabled`, `invalid`, `required`, and `readOnly` states automatically.
Also `htmlFor` and `id` are automatically handled by the `Field` component. Never use them directly.
```tsx
import { Field } from "@/registry/react/components/field"
import { NumberInput } from "@/registry/react/components/number-input"
const Demo = () => (
)
```
## Input states [#input-states]
### Invalid [#invalid]
Pass the `invalid` prop to the `Field` component.
The `FieldError` is only visible when the `invalid` prop is `true`.
```tsx
import {
Field,
FieldLabel,
FieldError,
} from "@/registry/react/components/field"
import { Input } from "@/registry/react/components/input"
const Demo = () => (
)
```
### Required [#required]
Pass the `required` prop to the `Field` component.
Optionally, you can use the `FieldRequiredIndicator` to indicate that the field is required.
```tsx
import {
Field,
FieldLabel,
FieldRequiredIndicator,
FieldError,
} from "@/registry/react/components/field"
import { Input } from "@/registry/react/components/input"
export const Demo = () => (
)
```
# Introduction (/docs)
Built on top of [Ark UI](https://ark-ui.com) and [shadcn](https://ui.shadcn.com), it includes sensible defaults for styling, variants, and structure, so you can drop components into a project without starting from scratch. It’s not a black-box framework; you own the code, tweak what you need, and build on top of something that’s already thought through.
The name blends the two projects it’s built on: **sh** from shadcn and **ark** from Ark UI. **Shark**.
## Motivation [#motivation]
[Ark UI](https://ark-ui.com) offers a broad set of headless components and serves as a direct alternative to [Radix Primitives](https://radix-ui.com/primitives) and [Base UI](https://base-ui.com). It gives you accessible primitives and state management, but it leaves styling and layout entirely to you. **shadcn**, on the other hand, ships copy-paste components that look great. The gap is that Ark’s docs don’t show how to pair its primitives with Tailwind, or how to turn them into a reusable design system you can install, theme, and extend.
## How it works [#how-it-works]
Instead of only installing a package from npm and importing opaque pieces, you keep source in your repository and change it with your app. That sidesteps the usual spiral of wrappers, style overrides, and mismatched APIs when defaults are not enough.
This approach, where you keep and own the source code, means:
* **Full transparency:** You see exactly how each component is built, with no hidden layers between you and the implementation.
* **Easy customization:** Adjust any part of a component to match your design and functionality; you edit the source instead of working around a package API.
* **AI-friendly workflows:** Because the code lives in your repo, LLMs and teammates can read, reason about, and help improve components without reverse-engineering a library.
## AI Ready [#ai-ready]
The library is designed so **people and AI tools** can rely on it equally. Components are written to stay clear, readable, and predictable so assistants can understand structure, reason about changes, and modify files with less guesswork.
Because the source sits in your repository with **inspectable implementations** and **consistent patterns** from component to component, models get the context they need to read your UI layer end to end. An assistant can learn how your pieces work, propose refactors or small fixes, or generate new components that match your existing tokens, layout, and conventions.
# Installation (/docs/installation)
## Prerequisites [#prerequisites]
Components require [Tailwind CSS v4](https://tailwindcss.com). Before you begin, make sure you have a project set up with Tailwind CSS installed.
## Adding components [#adding-components]
### CLI [#cli]
```bash
npx shadcn@latest add @shark/
```
### Manual [#manual]
Copy component source from the component docs via the Manual tab.
### All at once [#all-at-once]
```bash
npx shadcn@latest add @shark/ui
```
## New Project Setup [#new-project-setup]
For new projects, you have the `CLI` and `Manual` setup.
### CLI [#cli-1]
```bash
npx shadcn@latest init @shark/style
```
### Manual [#manual-1]
## Styling [#styling]
For theming and layout setup, see [Styling](/docs/styling).
## Color system [#color-system]
See [Colors](/docs/colors) for the full palette and variable reference.
# LLMs.txt (/docs/llms-txt)
Shark UI provides [LLMs.txt](https://llmstxt.org/) endpoints so AI coding assistants can fetch documentation directly by URL.
## Available Files [#available-files]
**Core documentation:**
* [/llms.txt](/llms.txt) — Quick reference index for documentation
* [/llms-full.txt](/llms-full.txt) — Complete Shark UI documentation
**For limited context windows:**
* [/llms-components.txt](/llms-components.txt) — Component documentation only
* [/llms-patterns.txt](/llms-patterns.txt) — Common patterns and recipes
**All platforms:**
* [/llms.txt](/llms.txt) — Quick reference index
* [/llms-full.txt](/llms-full.txt) — Complete documentation
* [/llms-components.txt](/llms-components.txt) — All component documentation
* [/llms-patterns.txt](/llms-patterns.txt) — All patterns and recipes
## Integration [#integration]
**Claude Code:** Reference the docs in your prompt or add to your project’s `.claude` file:
```bash
Use Shark UI documentation from https://shark.vini.one/llms.txt
```
**Cursor:** Use the `@Docs` feature:
```bash
@Docs https://shark.vini.one/llms-full.txt
```
[Learn more](https://docs.cursor.com/context/@-symbols/@-docs)
**Windsurf:** Add to your `.windsurfrules` file:
```bash
#docs https://shark.vini.one/llms-full.txt
```
[Learn more](https://docs.codeium.com/windsurf/memories#memories-and-rules)
**Other AI tools:** Most assistants accept documentation URLs:
```bash
https://shark.vini.one/llms.txt
```
For component-specific documentation:
```bash
https://shark.vini.one/llms-components.txt
```
For patterns and best practices:
```bash
https://shark.vini.one/llms-patterns.txt
```
## Contributing [#contributing]
Spotted an issue with AI-generated code? Contributions to improve the LLMs.txt output are welcome on [GitHub](https://github.com/sharkui-inc/shark-ui).
# RTL (/docs/rtl)
## Setup [#setup]
Wrap your app with `LocaleProvider` and pass an RTL locale. Use this when you know the user’s language ahead of time or when the app switches locale dynamically.
```tsx showLineNumbers {4,6}
import { LocaleProvider } from "@/components/ui/locale";
export const App = () => (
{/* Your app */}
);
```
Common RTL locales include `ar-BH`, `ar-SA`, `he-IL`, and `fa-IR`.
> **Note:** If no `LocaleProvider` is set up, the default locale is `en-US` and the direction stays `ltr`.
## Applying Direction [#applying-direction]
Read the current `dir` from `useLocale` and pass it to your root element—usually ``—so the layout renders correctly and child components inherit the right text direction.
```tsx
import { LocaleProvider, useLocale } from "@/components/ui/locale";
const AppContent = () => {
const { locale, dir } = useLocale();
return (
{/* Your app content */}
);
};
```
## How it works [#how-it-works]
Shark UI components rely on logical CSS properties wherever possible. That means spacing and layout use `ms-*`, `me-*`, `ps-*`, `pe-*`, and `start-*` / `end-*` instead of physical ones like `ml-*`, `mr-*`, `left-*`, or `right-*`. When you flip `dir` to `rtl`, the layout adapts automatically because those utilities follow the text direction.
Setting `dir={dir}` on the root ensures that direction propagates down. Ark UI’s overlays, tooltips, and menus respect the document direction, so they position themselves correctly whether the user reads left-to-right or right-to-left.
## Animations [#animations]
Animations should follow the reading direction. Physical utilities like `slide-in-from-right` will slide the wrong way in RTL. Use logical alternatives so motion stays consistent:
* `slide-in-from-end` / `slide-out-to-end` instead of `slide-in-from-right` / `slide-out-to-right`
* `slide-in-from-start` / `slide-out-to-start` instead of `slide-in-from-left` / `slide-out-to-left`
***
For the full API reference, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/locale).
# Skills (/docs/skills)
This skill covers Shark UI components, patterns, and styling. Your assistant uses it to implement those pieces correctly in your project.
You can ask for something concrete:
* *“Add a settings dialog with a form and save/cancel buttons.”*
* *“Add a combobox with search filtering.”*
* *“Migrate this shadcn dropdown to Shark’s Menu.”*
* *“Add a Select with grouped options and a placeholder.”*
## Installation [#installation]
```bash
npx skills add sharkui-inc/shark-ui
```
The public directory and leaderboard live at [skills.sh](https://skills.sh).
## What you get [#what-you-get]
The layout matches other mature UI skills: a compact entry file and deeper material you load only when the task needs it.
### Component knowledge [#component-knowledge]
Your assistant gets a map of the registry: what to import, how parts nest, and where the sharp edges are. Overlays (**Dialog** vs **Sheet**), menus (**Menu** vs **Context Menu**), list controls (**Select** vs **Combobox** with collections and filters), **Field**-driven forms, and **Sidebar** shells each have a short guide in the skill pack. That material stays aligned with the published component list in this project and the MDX on this site, so “what the skill says” and “what the docs say” stay in sync.
### Styling conventions [#styling-conventions]
Tailwind CSS v4, semantic tokens, and the small habits that keep Shark visually consistent, plus the editorial rules we keep for examples and previews (thumbs, field widths, chart tooltips, sidebar preview framing, and similar detail).
### Migration patterns [#migration-patterns]
When someone pastes radix, base-ui, or shadcn-shaped code, the skill nudges the rewrite toward **Shark UI**: collection objects for list primitives, and the chart or sidebar patterns we actually ship, not a generic “swap the import path” pass.
### CLI and registry [#cli-and-registry]
The same workflow this site documents for adding components (shadcn-style CLI and the public registry JSON hosted here). The skill reminds agents to follow each component’s installation section, dependency list, and registry entry.
### Registry examples [#registry-examples]
Every serious answer should look at the shipped examples that sit beside each component in source. Those snippets are the library’s own compositions; matching them keeps generated UI aligned with how Shark is maintained, not a one-line stub from a model’s training cut-off.
## How it works [#how-it-works]
1. **Activation:** After install, your agent discovers the skill the same way it discovers other Agent Skills.
2. **Progressive depth:** The **SKILL.md** file links into the registry index, shared rule files, and per-component notes only when the prompt touches that surface.
3. **Pattern enforcement:** The assistant follows Shark composition, combobox collection and filtering, chart tooltip wiring where applicable, sidebar preview layout, and the import conventions.
4. **Examples before invention:** The workflow tells the model to read the MDX for the component and the matching example snippets.
## Supported agents [#supported-agents]
Any editor or CLI that implements the [Agent Skills](https://agentskills.io) layout can consume this bundle: Claude Code, Cursor, Codex, Cline, Windsurf, GitHub Copilot, and the rest of the list on [skills.sh](https://skills.sh).
## In the repository [#in-the-repository]
The skill is versioned in the same open-source project as Shark UI. If you want to read or change it, open the project on [GitHub](https://github.com/sharkui-inc/shark-ui/tree/main/skills/shark-ui).
# Styling (/docs/styling)
## Overview [#overview]
Shark UI’s styling system is built for clarity and consistent theming. It follows [shadcn/ui theme tokens](https://ui.shadcn.com/docs/theming#theme-tokens) and adds three extra tokens: `success`, `warning`, and `info` to cover status states.
## CSS Configuration [#css-configuration]
See [Installation](/docs/installation#new-project-setup) for installing with CLI. To add the theme yourself, paste the following into `globals.css`:
```css title="globals.css"
@theme inline {
--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-info: var(--info);
--color-info-foreground: var(--info-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-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);
--radius-xs: calc(var(--radius) * 0.25);
--radius-sm: calc(var(--radius) * 0.5);
--radius-md: calc(var(--radius) * 0.75);
--radius-lg: calc(var(--radius) * 1);
--radius-xl: calc(var(--radius) * 1.5);
--radius-2xl: calc(var(--radius) * 2);
--radius-3xl: calc(var(--radius) * 3);
--radius-4xl: calc(var(--radius) * 4);
}
:root {
--radius: 0.5rem;
--background: var(--color-neutral-50);
--foreground: var(--color-neutral-800);
--card: var(--color-neutral-50);
--card-foreground: var(--color-neutral-800);
--popover: var(--color-neutral-50);
--popover-foreground: var(--color-neutral-800);
--primary: var(--color-neutral-800);
--primary-foreground: var(--color-neutral-50);
--secondary: color-mix(
in srgb,
var(--color-neutral-950) 6%,
var(--background)
);
--secondary-foreground: var(--color-neutral-800);
--muted: color-mix(in srgb, var(--color-neutral-950) 6%, var(--background));
--muted-foreground: color-mix(
in srgb,
var(--color-neutral-500) 80%,
var(--color-neutral-950)
);
--accent: color-mix(in srgb, var(--color-neutral-950) 6%, var(--background));
--accent-foreground: var(--color-neutral-800);
--destructive: var(--color-red-500);
--destructive-foreground: var(--color-red-700);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
--border: color-mix(in srgb, var(--color-neutral-950) 12%, var(--background));
--input: color-mix(in srgb, var(--color-neutral-950) 13%, var(--background));
--ring: var(--color-neutral-400);
--sidebar: var(--color-neutral-50);
--sidebar-foreground: color-mix(
in srgb,
var(--color-neutral-800) 64%,
var(--sidebar)
);
--sidebar-primary: var(--color-neutral-800);
--sidebar-primary-foreground: var(--color-neutral-50);
--sidebar-accent: color-mix(
in srgb,
var(--color-neutral-950) 6%,
var(--sidebar)
);
--sidebar-accent-foreground: var(--color-neutral-800);
--sidebar-border: color-mix(
in srgb,
var(--color-neutral-950) 11%,
var(--sidebar)
);
--sidebar-ring: var(--color-neutral-400);
--chart-1: var(--color-orange-600);
--chart-2: var(--color-teal-600);
--chart-3: var(--color-cyan-900);
--chart-4: var(--color-amber-400);
--chart-5: var(--color-amber-500);
}
.dark {
--background: var(--color-neutral-950);
--foreground: var(--color-neutral-100);
--card: color-mix(in srgb, var(--background) 98%, var(--color-neutral-50));
--card-foreground: var(--color-neutral-100);
--popover: color-mix(
in srgb,
var(--background) 96%,
var(--color-neutral-50)
);
--popover-foreground: var(--color-neutral-100);
--primary: var(--color-neutral-100);
--primary-foreground: var(--color-neutral-800);
--secondary: color-mix(in srgb, var(--color-white) 8%, var(--background));
--secondary-foreground: var(--color-neutral-100);
--muted: color-mix(in srgb, var(--color-neutral-50) 8%, var(--background));
--muted-foreground: color-mix(
in srgb,
var(--color-neutral-500) 70%,
var(--color-neutral-50)
);
--accent: color-mix(in srgb, var(--color-neutral-50) 8%, var(--background));
--accent-foreground: var(--color-neutral-100);
--destructive: color-mix(
in srgb,
var(--color-red-600) 90%,
var(--color-neutral-50)
);
--destructive-foreground: var(--color-red-400);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
--border: color-mix(in srgb, var(--color-neutral-50) 12%, var(--background));
--input: color-mix(in srgb, var(--color-neutral-50) 13%, var(--background));
--ring: var(--color-neutral-500);
--sidebar: color-mix(
in srgb,
var(--color-neutral-950) 97%,
var(--color-neutral-50)
);
--sidebar-foreground: color-mix(
in srgb,
var(--color-neutral-100) 64%,
var(--sidebar)
);
--sidebar-primary: var(--color-neutral-100);
--sidebar-primary-foreground: var(--color-neutral-800);
--sidebar-accent: color-mix(
in srgb,
var(--color-neutral-50) 8%,
var(--sidebar)
);
--sidebar-accent-foreground: var(--color-neutral-100);
--sidebar-border: color-mix(
in srgb,
var(--color-neutral-50) 11%,
var(--sidebar)
);
--sidebar-ring: var(--color-neutral-400);
--chart-1: var(--color-blue-700);
--chart-2: var(--color-emerald-500);
--chart-3: var(--color-amber-500);
--chart-4: var(--color-purple-500);
--chart-5: var(--color-rose-500);
}
```
{/* TODO: work on font-heading
## Font Variables
The theme preset defines `--font-sans` with system fallbacks. To use custom typefaces, set this variable in your layout via `next/font` or `@fontsource-variable`:
```tsx title="app/layout.tsx"
import { Inter } from "next/font/google";
const fontSans = Inter({ variable: "--font-sans", subsets: ["latin"] });
export default function RootLayout({ children }) {
return (
{children}
);
}
``` */}
## Token convention [#token-convention]
Each color has two tokens: the base name is the **fill**. The same name with **`-foreground`** is the **text** and **icon** color meant to sit on that fill. Use both together on every new surface so content stays readable.
```tsx
```
## Adding new tokens [#adding-new-tokens]
New variables must be wired through **`@theme inline`** as `--color-*` entries. See [Tailwind CSS](https://tailwindcss.com/docs/theme)
```css title="globals.css"
@theme inline {
--color-brand: var(--brand);
--color-brand-foreground: var(--brand-foreground);
}
:root {
--brand: oklch(0.55 0.18 250);
--brand-foreground: oklch(0.99 0 0);
}
```
## Customizing colors [#customizing-colors]
Override variables on `:root` and `.dark`. Always update foreground when you change a fill.
```css title="globals.css"
:root {
--primary: oklch(76.8% 0.233 130.85);
--primary-foreground: oklch(98.6% 0.031 120.757);
}
.dark {
--primary: oklch(53.2% 0.157 131.589);
--primary-foreground: oklch(96.7% 0.067 122.328);
}
```
## Radius Scale [#radius-scale]
`--radius` is the base radius token for your theme.
We derive a small radius scale from it so components can use consistent corner sizes while still sharing a single source of truth.
```css title="app/globals.css" showLineNumbers
@theme inline {
--radius-xs: calc(var(--radius) * 0.25);
--radius-sm: calc(var(--radius) * 0.5);
--radius-md: calc(var(--radius) * 0.75);
--radius-lg: calc(var(--radius) * 1);
--radius-xl: calc(var(--radius) * 1.5);
--radius-2xl: calc(var(--radius) * 2);
--radius-3xl: calc(var(--radius) * 3);
--radius-4xl: calc(var(--radius) * 4);
}
:root {
--radius: 0.5rem;
}
```
This means:
* `radius-lg` is the base value.
* Smaller radius scale down from `--radius`.
* Larger radius scale up from `--radius`.
* Changing `--radius` updates the entire radius scale.
## Color system [#color-system]
See [Colors](/docs/colors) for the full token reference, and naming rules.
# January 2026 - Beta 1 (/docs/changelog/26-01-first-beta)
Beta 1 marks the first public release of Shark UI. The goal was to build a set of components on [Ark UI](https://ark-ui.com) styled with [Tailwind CSS](https://tailwindcss.com), so you can add accessible primitives to a project without wiring up styles from scratch.
## Components [#components]
Roughly 80+ components are available, grouped into several categories:
* **UI primitives**: Accordion, Alert, Avatar, Badge, Button, Card, Separator, Skeleton, Tabs
* **Overlays and dialogs**: Dialog, Menu, Popover, Sheet, Tooltip, Hover Card, Context Menu
* **Forms and inputs**: Input, Textarea, Checkbox, Radio Group, Select, Native Select, Field, Combobox, Input OTP, File Upload, Editable
* **Layout and navigation**: Sidebar, Scroll Area, Resizable, Pagination, Breadcrumb, Bottom Navigation
* **Feedback and display**: Progress, Slider, Rating Group, Toggle, Toggle Group, Steps, Carousel
Components are copy-paste: they land in your project, and you own the code. Tailwind and Ark UI are the only runtime dependencies.
## Theme editor [#theme-editor]
The theme editor lets you tweak colors and copy the resulting CSS variables into your project. Themes follow the shadcn color convention, with extra variables for success, warning, info, and destructive states.
## Limitations [#limitations]
This is a beta. Some components may change before a stable release. If you run into issues, open an issue on [GitHub](https://github.com/sharkui-inc/shark-ui).
# March 2026 - Release Candidate (/docs/changelog/26-03-rc)
This release candidate builds on Beta 1 with new components, utilities, theme editor enhancements, and improved documentation. The component registry has been restructured with thumbs and consistent example naming.
## New Components [#new-components]
* **Announcement** — Banner-style component for promotions and notices
* **Calendar** — Full calendar with month/year navigation and range selection
* **Color Picker** — Color selection with swatches and input fields
* **Date Picker** — Date input with calendar popover and presets
* **Drawer** — Slide-out panel for mobile-first layouts
* **Image Cropper** — Crop images with zoom, aspect ratio, and constraints
* **Prose** — Typography styles for markdown and long-form content
* **Signature Pad** — Canvas-based signature capture
* **Skip Nav** — Accessibility link to skip to main content
* **Timer** — Countdown, interval, and Pomodoro timers
## New Utilities [#new-utilities]
* **Client Only** — Render components only on the client
* **Format** — Date, number, and relative time formatting
* **Show** — Conditional rendering with animation support
* **Download Trigger** — Programmatic file downloads
* **Swap** — Toggle between two states with animation
* **Presence** — Mount/unmount with enter/exit animations
* **JSON Tree View** — Expandable JSON display
* **Highlight** — Syntax highlighting for code blocks
## Component Updates [#component-updates]
Registry examples and thumbs were standardized across components:
* **Button** — Variants, sizes, and icon support
* **Card** — Composition and theming
* **Combobox** — Multi-select and controlled state
* **Dialog** — Sizes and compositions
* **Field** — Required fields and validation
* **Floating Panel** — Positions and resize behavior
* **Input Group** — Button integration and addons
* **Menu** — Nested menus and keyboard navigation
* **Number Input** — Stepper and format options
* **Pagination** — Sizes and variants
* **Select** — Multi-select and custom items
* **Textarea** — Autoresize and validation
* **Toast** — Placement, types, and promises
* **Toggle** — Sizes, outline, and icon variants
* **Tooltip** — Positioning and keyboard support
* **Tour** — Step types, progress, and async steps
## Theme Editor [#theme-editor]
* Updated color variable system and theme presets
* Support for success, warning, info, and destructive states
* Prose typography styles for content blocks
## Home Page [#home-page]
* Redesigned hero with Announcement component
* Component examples (Input OTP, forms, calendar, charts, and more)
* Framework support badges (React, Solid, Vue, Svelte)
* Theme cards for activity, chat, and sharing examples
## Documentation [#documentation]
* **Forms** — New forms section with React Hook Form and TanStack Form guides
* **RTL** — Right-to-left layout support
* Component docs updated with API references and examples
* New installation guides for various frameworks
## Infrastructure [#infrastructure]
* Open Graph image generation for docs and changelog
* RSS feed and sitemap generation
* LLMs.txt route for AI crawlers
## Dependencies [#dependencies]
* Package updates for Next.js, React, Ark UI, and dev tooling
# May 2026 - Post-RC updates (/docs/changelog/26-05-post-rc)
This update follows the March release candidate with ecosystem listing, a new Hitbox utility, expanded hooks and agent documentation, site improvements, and many fixes across theming and components.
## Registry & ecosystem [#registry--ecosystem]
* **shadcn registry** — Shark UI is wired into the broader shadcn registry configuration via `components.json`
* **Registry build** — Manifest and `public/r/*.json` metadata refreshed; registry build script updated for maintainers
## New utility [#new-utility]
* **Hitbox** — Enlarge tap and click targets without changing layout; includes docs, registry examples, and dedicated styles
## Hooks & CLI-facing docs [#hooks--cli-facing-docs]
* **use-is-mobile** — Documented hook for responsive behavior
* **style**, **ui**, **utils** — Registry items exposed for CLI installs alongside other meta updates
## Site & product [#site--product]
* **Blocks** — Early `/blocks` route scaffold for the blocks experience
* **Analytics** — Site analytics integration
* **Funding** — Ko-fi support link in project metadata
* **Agent skill** — Shark UI skill for Cursor and expanded primitive references for contributors and coding agents
## Documentation & layout [#documentation--layout]
* **Installation & components** — Dedicated overview pages for installation and the component index (`ComponentsList`), with framework guides remaining under Installation
* **Docs shell** — Fixes for table of contents when headings are sparse, component doc routing, CLI copy snippets, and content replacement behavior
* **Layout** — Global scrollbar and overflow refinements on the docs body
## Components & theming [#components--theming]
* **Theme** — Switcher reliability and reduced first-paint flicker; border and input CSS variables; background token alignment
* **Dialog** — RTL layout fix
* **Table** — Spacing and header text treatment
* **CodeBlock** / **Highlight** — Standardized prop for line numbers
* **Badge** — Improved touch behavior for coarse pointers
* **Combobox** — Follow-up fixes and examples after the combobox workstream
* **Announcement** — Updates from community PRs
* **Drawer**, **Toast**, **Sidebar**, **Timer**, **Prose** — Props, examples, and styling tweaks
* **data-slot** — Consistency fix for slot-based styling hooks
## Dependencies & infrastructure [#dependencies--infrastructure]
* Dependency updates and small component refactors
* CI workflow adjustments for contributor runs
# Changelog (/docs/changelog)
Releases are listed by date, newest first. Each entry describes what changed—components added or updated, breaking changes, and fixes. For release tags and diffs, check the [Shark UI repository](https://github.com/sharkui-inc/shark-ui) on GitHub.
# Formisch (/docs/forms/formisch)
This guide will cover building forms using the [Field](/docs/components/field) component, adding schema validation with [Valibot](https://valibot.dev), handling errors, ensuring accessibility, and more.
## Demo [#demo]
We’ll build a form with a text input and textarea. When you submit, the form data is validated and any errors will be shown.
Browser validation disabled
For the purposes of this demo, browser validation is disabled to illustrate schema validation. In production, keep native validation enabled when appropriate.
## Approach [#approach]
This form uses Formisch for state and Valibot for validation. We'll build forms using the `Field` component, which gives you complete flexibility over the markup and styling.
* Uses Formisch's `useForm` hook for form state management.
* Uses the `Form` component for submit handling.
* Import Formisch’s `Field` under an alias (`FormischField`) for controlled inputs.
* Uses Shark `Field` components for building accessible forms.
* Uses client-side validation by passing your Valibot schema into `schema`.
## Anatomy [#anatomy]
Typical structure: wrap each field with `FormischField`, and the `Field` component.
```tsx showLineNumbers {5-12}
```
## Form [#form]
### Create a schema [#create-a-schema]
Define your form shape with a Valibot schema.
**Note:** Formisch only supports [Valibot](https://formisch.dev/react/guides/installation/#install-valibot) for validation.
### Setup [#setup]
* Create the form with `useForm` from Formisch and pass your schema to the `schema` option,
* Wrap fields in `Form` and pass the form to the `onSubmit` option.
```tsx showLineNumbers title="form.tsx" {9-15,18-20}
import { Form, Field as FormischField, useForm } from "@formisch/react";
import * as v from "valibot";
const formSchema = v.object({
// ...
});
export const BugReportForm = () => {
const form = useForm({
schema: formSchema,
initialInput: {
title: "",
description: "",
},
});
return (
);
};
```
### Build [#build]
Build the form using the `FormischField` from Formisch and the Shark `Field`.
### Done [#done]
That's it. You now have a fully accessible form with client-side validation.
When you submit the form, the `onSubmit` handler on `Form` receives validated output. If the form data is invalid, Formisch will display the errors on `field.errors` for `FieldError`.
## Validation [#validation]
### Client-side [#client-side]
Formisch validates your form data using the Valibot schema. Define a schema and pass it to the `schema` option of the `useForm` hook.
```tsx showLineNumbers title="example-form.tsx" {4-7,11}
import { useForm } from "@formisch/react"
import * as v from "valibot"
const formSchema = v.object({
title: v.string(),
description: v.optional(v.string()),
})
export const ExampleForm = () => {
const form = useForm({
schema: formSchema,
initialInput: {
title: "",
description: "",
},
})
}
```
### Modes [#modes]
Configure when validation runs via the `validate` and `revalidate` options:
```tsx showLineNumbers title="form.tsx" {3-4}
const form = useForm({
schema: formSchema,
validate: "submit",
revalidate: "input",
})
```
| Option | Role |
| ----------- | -------------------------------------- |
| `"initial"` | Validation triggers on initial render. |
| `"touch"` | Validation triggers on field touch. |
| `"input"` | Validation triggers on field input. |
| `"change"` | Validation triggers on field change. |
| `"blur"` | Validation triggers on field blur. |
| `"submit"` | Validation triggers on form submit. |
## Displaying Errors [#displaying-errors]
Display errors next to the field using `FieldError`. For styling and accessibility:
* Add the `invalid` prop to the `Field` component.
* Don't need to add the `invalid` prop to the form control such as `Input`, `Checkbox`, etc.
```tsx showLineNumbers title="form.tsx" {3,6}
{(field) => (
Email{field.errors?.[0]}
)}
```
## Different types of fields [#different-types-of-fields]
### Input [#input]
* Spread `field.props` on `Input` and set `value={field.input}`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Textarea [#textarea]
* Spread `field.props` on `Textarea` and set `value={field.input}`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### NativeSelect [#nativeselect]
* Spread `field.props` on `NativeSelect` and set `value={field.input}`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Select [#select]
* Wire `field.input` and `field.onChange` to `Select`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Checkbox [#checkbox]
* Wire `field.input` and `field.onChange` to `Checkbox`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
* Remember to add `data-slot="checkbox-group"` to the `FieldGroup` component for proper styling and spacing.
### Radio group [#radio-group]
* Wire `field.input` and `field.onChange` to `RadioGroup`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Switch [#switch]
* Wire `field.input` and `field.onChange` to `Switch`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### NumberInput [#numberinput]
* Wire `field.input` and `field.onChange` to `NumberInput`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Slider [#slider]
* Wire `field.input` and `field.onChange` to `Slider`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Combobox [#combobox]
* Wire `field.input` and `field.onChange` to `Combobox`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Autocomplete [#autocomplete]
* Wire `field.input` and `field.onChange` to `Autocomplete`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Date Picker [#date-picker]
* Wire `field.input` and `field.onChange` to `DatePicker`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Input OTP [#input-otp]
* Wire `field.input` and `field.onChange` to `InputOTP`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Rating [#rating]
* Use `field.onChange` with `Rating`’s `onValueChange` and bind `value` from `field.input`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### File Upload [#file-upload]
* Use `FileUpload` `onFileAccept` to push files into the field value (`field.onChange`).
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Complex Forms [#complex-forms]
Here is an example of a more complex form with multiple fields and validation.
## Resetting the Form [#resetting-the-form]
Import `reset` and pass the form to reset the form to its default values.
```tsx showLineNumbers
import { reset } from "@formisch/react";
```
## Array Fields [#array-fields]
Formisch provides a `FieldArray` component for managing dynamic array fields. Also provides helpers like `insert` and `remove` for dynamic lists. This is useful when you need to add or remove fields dynamically.
### Array Field Structure [#array-field-structure]
Use `FieldArray` component and pass the form to the `of` prop.
```tsx showLineNumbers title="form.tsx" {9-15}
import { FieldArray, Field as FormischField, useForm } from "@formisch/react";
export const ExampleForm = () => {
const form = useForm({
// ... form config
});
return (
{(arrayField) => (
arrayField.items.map((itemId, index) => (
// Nested field for each array item
))
)}
);
}
```
### Nested Fields [#nested-fields]
Use `arrayField.items` to render each nested field, passing the correct path for each item.
```tsx showLineNumbers title="form.tsx"
{
arrayField.items.map((itemId, index) => (
{(field) => (
field.onChange(e.target.value)}
placeholder="name@example.com"
type="email"
value={field.input}
/>
{arrayField.items.length > 1 && (
remove(form, { path: ["emails"], at: index })
}
size="icon-xs"
type="button"
variant="ghost"
>
)}
{field.errors?.[0]}
)}
))
}
```
### Adding items [#adding-items]
* Use `insert` to add a new array item.
* Provide `initialInput` matching the nested structure of the array element.
```tsx showLineNumbers title="form.tsx"
import { insert } from "@formisch/react";
```
### Removing items [#removing-items]
Use `remove` with the array path and index.
```tsx showLineNumbers title="form.tsx"
import { remove } from "@formisch/react";
{
arrayField.items.length > 1 && (
remove(form, { path: ["emails"], at: index })}
aria-label={`Remove email ${index + 1}`}
>
);
}
```
### Array validation [#array-validation]
Use Valibot's `array` method to validate array fields.
```tsx showLineNumbers title="form.tsx"
import * as v from "valibot";
const formSchema = v.object({
emails: v.pipe(
v.array(
v.object({
contact: v.object({
address: v.pipe(
v.string(),
v.nonEmpty("Enter an email address."),
v.email("Enter a valid email address.")
),
}),
})
),
v.minLength(1, "Add at least one email address."),
v.maxLength(5, "You can add up to 5 email addresses.")
),
});
```
# React Hook Form (/docs/forms/react-hook-form)
This guide will cover building forms using the [Field](/docs/components/field) component, adding schema validation with [Zod](https://zod.dev), handling errors, ensuring accessibility, and more.
## Demo [#demo]
We’ll build a form with a text input and textarea. When you submit, the form data is validated and any errors will be shown.
Browser validation disabled
For the purposes of this demo, browser validation is disabled to illustrate schema validation. In production, keep native validation enabled when appropriate.
## Approach [#approach]
This form uses React Hook Form for state and Zod for validation. We'll build forms using the `Field` component, which gives you complete flexibility over the markup and styling.
* Uses React Hook Form's useForm hook for form state management.
* Uses the `Controller` component for controlled inputs.
* Uses the `Field` components for building accessible forms.
* Uses client-side validation by passing your Zod schema into `resolver`.
## Anatomy [#anatomy]
Typical structure: wrap each field with `Controller`, and the `Field` component.
```tsx showLineNumbers {7-18}
```
## Form [#form]
### Create a schema [#create-a-schema]
Define your form shape with a Zod schema.
**Note:** React Hook Form supports other [Standard Schema](https://react-hook-form.com/get-started#SchemaValidation) libraries; Zod is used here for clarity.
```tsx showLineNumbers title="form.tsx"
import * as z from "zod"
const formSchema = z.object({
title: z
.string()
.min(5, "Bug title must be at least 5 characters.")
.max(32, "Bug title must be at most 32 characters."),
description: z
.string()
.min(20, "Description must be at least 20 characters.")
.max(100, "Description must be at most 100 characters."),
})
```
### Setup [#setup]
Create the form with `useForm` from React Hook Form and pass your schema to the `resolver` option.
```tsx showLineNumbers title="form.tsx" {10-16}
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
const formSchema = z.object({
// ...
});
export const BugReportForm = () => {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
title: "",
description: "",
},
});
const onSubmit = (data: z.infer) => {
console.log(data);
}
return (
);
};
```
### Build [#build]
Build the form using the `Controller` from React Hook Form and the Shark `Field`.
### Done [#done]
That's it. You now have a fully accessible form with client-side validation.
When you submit the form, the `onSubmit` function will be called with the validated form data. If the form is invalid, React Hook Form will display the errors on `field.state.error` for `FieldError`.
## Validation [#validation]
### Client-side [#client-side]
React Hook Form validates your form data using the Zod schema. Define a schema and pass it to the `resolver` option of the `useForm` hook.
```tsx showLineNumbers title="example-form.tsx" {5-8,12}
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
const formSchema = z.object({
title: z.string(),
description: z.string().optional(),
})
export const ExampleForm = () => {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
title: "",
description: "",
},
})
}
```
### Modes [#modes]
Configure when validation runs via the `mode` option:
```tsx showLineNumbers title="form.tsx" {3}
const form = useForm({
resolver: zodResolver(formSchema),
mode: "onChange",
})
```
| Mode | Description |
| ------------- | -------------------------------------------------------- |
| `"onChange"` | Validation triggers on every change. |
| `"onBlur"` | Validation triggers on blur. |
| `"onSubmit"` | Validation triggers on submit (default). |
| `"onTouched"` | Validation triggers on first blur, then on every change. |
| `"all"` | Validation triggers on blur and change. |
## Displaying Errors [#displaying-errors]
Display errors next to the field using `FieldError`. For styling and accessibility:
* Add the `invalid` prop to the `Field` component.
* Don't need to add the `invalid` prop to the form control such as `Input`, `Checkbox`, etc.
```tsx showLineNumbers title="form.tsx" {5,8}
(
Email{fieldState.error?.message}
)}
/>
```
## Different types of fields [#different-types-of-fields]
### Input [#input]
* Spread the field object onto the `Input` component.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Textarea [#textarea]
* Spread the field object onto the `Textarea` component.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### NativeSelect [#nativeselect]
* Spread the field object onto the `NativeSelect` component.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Select [#select]
* Wire `field.value` and `field.onChange` to `Select`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Checkbox [#checkbox]
* Wire `field.value` and `field.onChange` to `Checkbox`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
* Remember to add `data-slot="checkbox-group"` to the `FieldGroup` component for proper styling and spacing.
### Radio group [#radio-group]
* Wire `field.value` and `field.onChange` to `RadioGroup`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Switch [#switch]
* Wire `field.value` and `field.onChange` to `Switch`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### NumberInput [#numberinput]
* Wire `field.value` and `field.onChange` to `NumberInput`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Slider [#slider]
* Wire `field.value` and `field.onChange` to `Slider`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Combobox [#combobox]
* Wire `field.value` and `field.onChange` to `Combobox`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Autocomplete [#autocomplete]
* Wire `field.value` and `field.onChange` to `Autocomplete`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Date Picker [#date-picker]
* Wire `field.value` and `field.onChange` to `DatePicker`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Input OTP [#input-otp]
* Wire `field.value` and `field.onChange` to `InputOTP`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Rating [#rating]
* Wire `field.value` and `field.onChange` to `Rating`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### File Upload [#file-upload]
* Wire `field.value` and `field.onChange` to `FileUpload`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Complex Forms [#complex-forms]
Here is an example of a more complex form with multiple fields and validation.
## Resetting the Form [#resetting-the-form]
Use `form.reset()` to reset the form to its default values.
```tsx showLineNumbers
```
## Array Fields [#array-fields]
React Hook Form provides a `useFieldArray` hook for managing dynamic array fields. This is useful when you need to add or remove fields dynamically.
### Using useFieldArray [#using-usefieldarray]
Use the `useFieldArray` hook to manage array fields. It provides `fields`, `append`, and `remove` methods.
```tsx showLineNumbers title="form.tsx" {8-11}
import { useFieldArray, useForm } from "react-hook-form";
export const ExampleForm = () => {
const form = useForm({
// ... form config
});
const { fields, append, remove } = useFieldArray({
control: form.control,
name: "emails",
});
}
```
### Array Field Structure [#array-field-structure]
Wrap your array fields in a `FieldSet` with a `FieldLegend` and `FieldDescription`.
```tsx showLineNumbers title="form.tsx"
```
### Controller Pattern for Array Items [#controller-pattern-for-array-items]
Map over the `fields` array and use `Controller` for each item. **Make sure to use `field.id` as the key**.
```tsx showLineNumbers title="form.tsx"
{
fields.map((field, index) => (
(
{/* Remove button */}
{fieldState.error?.message}
)}
/>
))}
```
### Adding Items [#adding-items]
Use the `append` method to add new items to the array.
```tsx showLineNumbers title="form.tsx"
```
### Removing Items [#removing-items]
Use the `remove` method to remove items from the array. Add the remove button conditionally.
```tsx showLineNumbers title="form.tsx"
{
fields.length > 1 && (
remove(index)}
aria-label={`Remove email ${index + 1}`}
>
)}
}
```
### Array Validation [#array-validation]
Use Zod's `array` method to validate array fields.
```tsx showLineNumbers title="form.tsx"
const formSchema = z.object({
emails: z
.array(
z.object({
address: z.string().email("Enter a valid email address."),
})
)
.min(1, "Add at least one email address.")
.max(5, "You can add up to 5 email addresses."),
});
```
# TanStack Form (/docs/forms/tanstack-form)
This guide will cover building forms using the [Field](/docs/components/field) component, adding schema validation with [Zod](https://zod.dev), handling errors, ensuring accessibility, and more.
## Demo [#demo]
We’ll build a form with a text input and textarea. When you submit, the form data is validated and any errors will be shown.
Browser validation disabled
For the purposes of this demo, browser validation is disabled to illustrate schema validation. In production, keep native validation enabled when appropriate.
## Approach [#approach]
This form uses TanStack Form for state and Zod for validation. We'll build forms using the `Field` component, which gives you complete flexibility over the markup and styling.
* Uses TanStack Form's `useForm` hook for form state management.
* Uses `form.Field` with a render function for controlled inputs.
* Uses the `Field` components for building accessible forms.
* Uses client-side validation by passing your Zod schema into `validators`.
## Anatomy [#anatomy]
Here's a basic example of a form using TanStack Form with the `Field` component.
```tsx showLineNumbers {11-29}
```
## Form [#form]
### Create a schema [#create-a-schema]
Define your form shape with a Zod schema.
**Note:** TanStack Form works with Zod and other [Standard Schema](https://tanstack.com/form/latest/docs/framework/react/guides/validation#schema-validation) libraries via its validators API.
```tsx showLineNumbers title="form.tsx"
import * as z from "zod"
const formSchema = z.object({
title: z
.string()
.min(5, "Bug title must be at least 5 characters.")
.max(32, "Bug title must be at most 32 characters."),
description: z
.string()
.min(20, "Description must be at least 20 characters.")
.max(100, "Description must be at most 100 characters."),
})
```
### Setup [#setup]
* Create the form with `useForm` from TanStack Form and pass your schema to the `onSubmit` option.
* Use e.preventDefault() and e.stopPropagation() before calling form.handleSubmit()
```tsx showLineNumbers title="form.tsx" {9-18}
import { useForm } from "@tanstack/react-form";
import * as z from "zod";
const formSchema = z.object({
// ...
});
export const ExampleForm = () => {
const form = useForm({
defaultValues: {
title: "",
description: "",
},
validators: { onSubmit: formSchema },
onSubmit: async ({ value }) => {
console.log(value);
},
});
return (
);
};
```
### Build [#build]
build the form using the `form.Field` from TanStack Form and the Shark `Field`.
### Done [#done]
That's it. You now have a fully accessible form with client-side validation.
When you submit the form, the `onSubmit` handler receives validated values. If the form is invalid, TanStack Form exposes errors on `field.state.meta.errors` for `FieldError`.
## Validation [#validation]
### Client-side [#client-side]
TanStack Form validates your form data using the Zod schema. Define a schema and pass it to the `validators` option of the `useForm` hook.
```tsx showLineNumbers title="example-form.tsx" {4-7,15}
import { useForm } from "@tanstack/react-form"
import * as z from "zod"
const formSchema = z.object({
title: z.string(),
description: z.string().optional(),
})
export const ExampleForm = () => {
const form = useForm({
defaultValues: {
title: "",
description: "",
},
validators: { onSubmit: formSchema },
onSubmit: async () => {},
})
}
```
### Modes [#modes]
Configure when validation runs via the `validators` option:
```tsx showLineNumbers title="form.tsx" {2}
const form = useForm({
validators: { onSubmit: formSchema },
})
```
| Mode | Description |
| ------------ | ------------------------------------ |
| `"onChange"` | Validation triggers on every change. |
| `"onBlur"` | Validation triggers on blur. |
| `"onSubmit"` | Validation triggers on submit. |
## Displaying Errors [#displaying-errors]
Display errors next to the field using `FieldError`. For styling and accessibility:
* Add the `invalid` prop to the `Field` component.
* Don't need to add the `invalid` prop to the form control such as `Input`, `Checkbox`, etc.
```tsx showLineNumbers title="form.tsx" {4,13-15}
(
Email
field.handleChange(e.target.value)}
type="email"
value={field.state.value}
/>
{field.state.meta.errors.map((e) => e?.message).join(", ")}
)}
/>
```
## Different types of fields [#different-types-of-fields]
### Input [#input]
* Bind `field.state.value` and `field.handleChange` to `Input`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Textarea [#textarea]
* Bind `field.state.value` and `field.handleChange` to `Textarea`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### NativeSelect [#nativeselect]
* Bind `field.state.value` and `field.handleChange` to `NativeSelect`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Select [#select]
* Wire `value` and `onValueChange` on `Select`. For overlays, call `field.handleBlur()` from `onInteractOutside` when your example needs blur sync.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
### Checkbox [#checkbox]
* Wire `field.state.value` and `field.handleChange` to `Checkbox`.
* Add the `invalid` prop to the `Field` component and pass the error message to the `FieldError` component.
* For checkbox arrays, use `mode="array"` on the `form.Field` component and TanStack Form's array helpers.
* Remember to add `data-slot="checkbox-group"` to the `FieldGroup` component for proper styling and spacing.
### Radio group [#radio-group]
* Wire `field.state.value` and `field.handleChange` to `RadioGroup`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Switch [#switch]
* Use `field.state.value` and `field.handleChange` with `Switch`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### NumberInput [#numberinput]
* Wire `field.state.value` and `field.handleChange` to `NumberInput`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Slider [#slider]
* Wire `field.state.value` and `field.handleChange` to `Slider`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Combobox [#combobox]
* Wire `field.state.value` and `field.handleChange` to `Combobox`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Autocomplete [#autocomplete]
* Wire `field.state.value` and `field.handleChange` to `Autocomplete`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Date Picker [#date-picker]
* Wire `field.state.value` and `field.handleChange` to `DatePicker`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Input OTP [#input-otp]
* Use `field.state.value` (as `string[]`) and `field.handleChange` with `InputOTP`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Rating [#rating]
* Wire `field.state.value` and `field.handleChange` to `Rating`.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### File Upload [#file-upload]
* Wire `onFileAccept` to sync files into form state.
* Add the `invalid` prop to the `Field` component and pass the `FieldError` component.
### Complex Forms [#complex-forms]
Here is an example of a more complex form with multiple fields and validation.
## Resetting the Form [#resetting-the-form]
Use `form.reset()` to reset the form to its default values.
```tsx showLineNumbers
```
## Array Fields [#array-fields]
TanStack Form provides powerful array field management with `mode="array"`. This allows you to dynamically add, remove, and update array items with full validation support.
### Array Field Structure [#array-field-structure]
Use `mode="array"` on the parent field to enable array field management.
```tsx showLineNumbers title="form.tsx" {3,11-13}
(
)}
/>
```
### Nested Fields [#nested-fields]
Access individual array items using bracket notation: `fieldName[index].propertyName`.
```tsx showLineNumbers title="form.tsx"
(
subField.handleChange(e.target.value)}
placeholder="name@example.com"
type="email"
/>
{field.state.value.length > 1 && (
field.removeValue(index)}
aria-label={`Remove email ${index + 1}`}
>
)}
{subField.state.meta.errors.map((e) => e?.message).join(", ")}
)}
/>
```
### Adding Items [#adding-items]
Use `field.pushValue(item)` to add items to an array field. You can disable the button when the array reaches its maximum length.
```tsx showLineNumbers title="form.tsx"
```
### Removing Items [#removing-items]
Use `field.removeValue(index)` to remove items from an array field. You can conditionally show the remove button only when there's more than one item.
```tsx showLineNumbers title="form.tsx"
{
field.state.value.length > 1 && (
field.removeValue(index)}
aria-label={`Remove email ${index + 1}`}
>
)
}
```
### Removing items [#removing-items-1]
Use `removeValue(index)` on the array field.
```tsx showLineNumbers title="form.tsx"
{
emailsField.state.value.length > 1 && (
emailsField.removeValue(index)}
aria-label={`Remove email ${index + 1}`}
>
);
}
```
### Array validation [#array-validation]
Validate arrays with Zod’s `z.array()` and `.min()` / `.max()`.
```tsx showLineNumbers title="form.tsx"
const formSchema = z.object({
emails: z
.array(
z.object({
address: z.string().email("Enter a valid email address."),
})
)
.min(1, "Add at least one email address.")
.max(5, "You can add up to 5 email addresses."),
});
```
# useIsMobile (/docs/hooks/use-is-mobile)
`useIsMobile` tells you if the viewport is less than **768px**. It uses `matchMedia`, so it reacts instantly to browser zoom and device rotation, not just window resizing.
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/use-is-mobile
```
This hook has no package dependencies beyond React.
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import { useIsMobile } from "@/registry/react/hooks/use-is-mobile";
```
## Example [#example]
```tsx showLineNumbers
"use client";
import { useIsMobile } from "@/registry/react/hooks/use-is-mobile";
export function Example() {
const isMobile = useIsMobile();
return (
{isMobile ? "Mobile layout" : "Desktop layout"}
);
}
```
# Astro (/docs/installation/astro)
## Use the Shark Preset [#use-the-shark-preset]
### Create Project [#create-project]
Run the `init` command with the Shark preset and the Astro template:
```bash
npx shadcn@latest init @shark/style -t astro
```
### Add Components [#add-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components in an Astro page with a React client directive:
```astro title="src/pages/index.astro" showLineNumbers
---
import Layout from "@/layouts/main.astro";
import { Button } from "@/components/ui/button";
---
```
## Use the CLI [#use-the-cli]
### Create Project [#create-project-1]
Run the `init` command to scaffold a new Astro project and configure Shark UI:
```bash
npx shadcn@latest init @shark/style -t astro
```
For a monorepo project, use the `--monorepo` flag:
```bash
npx shadcn@latest init @shark/style -t astro --monorepo
```
### Add Components [#add-components-1]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
If you created a monorepo, run the command from `apps/web` or specify the workspace from the repo root:
```bash
npx shadcn@latest add @shark/button -c apps/web
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components in an Astro page with a React client directive:
```astro title="src/pages/index.astro" showLineNumbers
---
import Layout from "@/layouts/main.astro";
import { Button } from "@/components/ui/button";
---
```
If you created a monorepo, update `apps/web/src/pages/index.astro` and import from your workspace UI package instead. The monorepo layout at `apps/web/src/layouts/main.astro` already imports your shared global CSS for you.
## Existing Project [#existing-project]
### Create Project [#create-project-2]
If you need a new Astro project, create one first. Otherwise, skip this step.
```bash
npm create astro@latest astro-app -- --template with-tailwindcss --install --add react --git
```
This command sets up Tailwind CSS and the React integration for you. If you're adding Shark UI to an older or custom Astro app, make sure both are configured before continuing.
The Tailwind starter loads your global stylesheet through `src/layouts/main.astro`. Keep that layout in place, or make sure your page imports `@/styles/global.css`.
### Edit tsconfig.json [#edit-tsconfigjson]
Add path aliases so imports resolve correctly:
```ts title="tsconfig.json" showLineNumbers
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
```
### Run the CLI [#run-the-cli]
Run the `shadcn` init command with the Shark preset to set up your project.
```bash
npx shadcn@latest init @shark/style
```
### Add Components [#add-components-2]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components in an Astro page with a React client directive:
```astro title="src/pages/index.astro" showLineNumbers
---
import Layout from "@/layouts/main.astro";
import { Button } from "@/components/ui/button";
---
```
# Laravel (/docs/installation/laravel)
The shadcn CLI does not scaffold a new Laravel app. Start by creating a Laravel app with the React starter kit, then choose how you want to configure Shark UI.
### Create Project [#create-project]
Create a new Laravel app using the Laravel installer:
```bash
laravel new my-app
```
If you already have a Laravel app with React and Inertia configured, skip this step.
Choose the **React** starter kit when prompted. For more information, see the official [Laravel frontend documentation](https://laravel.com/docs/starter-kits#react).
Then move into your project directory:
```bash
cd my-app
```
## Use the Shark Preset [#use-the-shark-preset]
### Run the Command [#run-the-command]
Run the `init` command with the Shark preset and the Laravel template from the root of your Laravel app:
```bash
npx shadcn@latest init @shark/style -t laravel
```
When asked to overwrite `components.json` and components, choose `Yes`.
### Add Components [#add-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Switch` component to your project:
```bash
npx shadcn@latest add @shark/switch
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="resources/js/pages/index.tsx" showLineNumbers
import { Switch } from "@/components/ui/switch";
const MyPage = () => {
return (
);
};
export default MyPage;
```
## Use the CLI [#use-the-cli]
### Run the CLI [#run-the-cli]
Run the `shadcn` init command with the Shark preset from the root of your Laravel app:
```bash
npx shadcn@latest init @shark/style
```
When asked to overwrite `components.json` and components, choose `Yes`.
### Add Components [#add-components-1]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Switch` component to your project:
```bash
npx shadcn@latest add @shark/switch
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="resources/js/pages/index.tsx" showLineNumbers
import { Switch } from "@/components/ui/switch";
const MyPage = () => {
return (
);
};
export default MyPage;
```
# Manual Installation (/docs/installation/manual)
Manual setup gives you full control over every file. Use this when the CLI does not fit your setup or you prefer to wire the project yourself.
### Add Tailwind CSS [#add-tailwind-css]
Components are styled using Tailwind CSS. Install Tailwind CSS in your project first:
[Follow the Tailwind CSS installation instructions.](https://tailwindcss.com/docs/installation)
### Add dependencies [#add-dependencies]
Add the dependencies used by Shark UI components:
```bash
pnpm add @ark-ui/react tailwind-variants clsx tailwind-merge lucide-react tw-animate-css
```
### Configure import aliases [#configure-import-aliases]
Choose one of the following alias setups.
#### Option A: tsconfig.json paths [#option-a-tsconfigjson-paths]
```json title="tsconfig.json" showLineNumbers {2-6}
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
```
#### Option B: package.json#imports [#option-b-packagejsonimports]
```json title="package.json" showLineNumbers
{
"imports": {
"#components/*": "./src/components/*.tsx",
"#lib/*": "./src/lib/*.ts",
"#hooks/*": "./src/hooks/*.ts"
}
}
```
```json title="tsconfig.json" showLineNumbers
{
"compilerOptions": {
"moduleResolution": "bundler",
"resolvePackageJsonImports": true
}
}
```
The `@` alias is a preference. You can use other aliases if needed. If you use `package.json#imports`, keep the matching alias roots in `components.json`.
### Configure styles [#configure-styles]
Create a file `styles/globals.css` and follow [Styling](/docs/styling#css-configuration) to configure the theme.
### Add a cn helper [#add-a-cn-helper]
```ts title="lib/utils.ts" showLineNumbers
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs))
```
### Create a components.json file [#create-a-componentsjson-file]
Create a `components.json` file in the project root so the shadcn CLI knows where to write components if you use it later.
```json title="components.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "styles/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide",
"registries": {
"@shark": "https://shark.vini.one/r/{name}.json"
}
}
```
If you're using `package.json#imports`, use the corresponding `#...` aliases instead:
```json title="components.json" showLineNumbers
{
"aliases": {
"components": "#components",
"utils": "#lib/utils",
"ui": "#components/ui",
"lib": "#lib",
"hooks": "#hooks"
}
}
```
## Adding components [#adding-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
# Next.js (/docs/installation/next)
## Use the Shark Preset [#use-the-shark-preset]
### Create Project [#create-project]
Run the `init` command with the Shark preset and the Next.js template:
```bash
npx shadcn@latest init @shark/style -t next
```
### Add Components [#add-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="app/page.tsx" showLineNumbers
import { Button } from "@/components/ui/button";
export default function Home() {
return (
);
}
```
## Use the CLI [#use-the-cli]
### Create Project [#create-project-1]
Run the `init` command to scaffold a new Next.js project and configure Shark UI:
```bash
npx shadcn@latest init @shark/style -t next
```
### Add Components [#add-components-1]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="app/page.tsx" showLineNumbers
import { Button } from "@/components/ui/button";
export default function Home() {
return (
);
}
```
## Existing Project [#existing-project]
### Create Project [#create-project-2]
If you need a new Next.js project, create one with `create-next-app`. Otherwise, skip this step.
```bash
npx create-next-app@latest
```
Choose the recommended defaults so Tailwind CSS, the App Router, and the default `@/*` import alias are configured for you.
If you prefer a `src/` directory, use `--src-dir` or choose `Yes` when prompted:
```bash
npx create-next-app@latest --src-dir
```
With `--src-dir`, Next.js places your app in `src/app` and configures the `@/*` alias to point to `./src/*`.
### Configure Tailwind CSS and Import Aliases [#configure-tailwind-css-and-import-aliases]
If you created your project with the recommended `create-next-app` defaults, you can skip this step.
If you're adding Shark UI to an older or custom Next.js app, make sure Tailwind CSS is installed first. You can follow the official [Next.js installation guide](https://nextjs.org/docs/app/getting-started).
Then make sure your `tsconfig.json` includes the `@/*` import alias:
```json title="tsconfig.json" showLineNumbers
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}
```
If you used `--src-dir`, point the alias to `./src/*` instead.
### Run the CLI [#run-the-cli]
Run the `shadcn` init command with the Shark preset to set up your project.
```bash
npx shadcn@latest init @shark/style
```
### Add Components [#add-components-2]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="app/page.tsx" showLineNumbers
import { Button } from "@/components/ui/button";
export default function Home() {
return (
);
}
```
If you used `--src-dir`, add the component to `src/app/page.tsx` instead.
# React Router (/docs/installation/react-router)
## Use the Shark Preset [#use-the-shark-preset]
### Create Project [#create-project]
```bash
npx shadcn@latest init @shark/style -t react-router
```
### Add Components [#add-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="app/routes/home.tsx" showLineNumbers
import { Button } from "~/components/ui/button";
import type { Route } from "./+types/home";
export const meta = ({}: Route.MetaArgs) => {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
};
export default function Home() {
return (
);
}
```
## Use the CLI [#use-the-cli]
### Create Project [#create-project-1]
Run the `init` command to scaffold a new React Router project and configure Shark UI:
```bash
npx shadcn@latest init @shark/style -t react-router
```
For a monorepo project, use the `--monorepo` flag:
```bash
npx shadcn@latest init @shark/style -t react-router --monorepo
```
### Add Components [#add-components-1]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
If you created a monorepo, run the command from `apps/web` or specify the workspace from the repo root:
```bash
npx shadcn@latest add @shark/button -c apps/web
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="app/routes/home.tsx" showLineNumbers
import { Button } from "~/components/ui/button";
import type { Route } from "./+types/home";
export const meta = ({}: Route.MetaArgs) => {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
};
export default function Home() {
return (
);
}
```
If you created a monorepo, update `apps/web/app/routes/home.tsx` and import from your workspace UI package instead.
## Existing Project [#existing-project]
### Create Project [#create-project-2]
If you need a new React Router project, create one first. Otherwise, skip this step.
```bash
npx create-react-router@latest my-app
```
### Run the CLI [#run-the-cli]
Run the `shadcn` init command with the Shark preset to set up your project.
```bash
npx shadcn@latest init @shark/style
```
### Add Components [#add-components-2]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="app/routes/home.tsx" showLineNumbers
import { Button } from "~/components/ui/button";
import type { Route } from "./+types/home";
export const meta = ({}: Route.MetaArgs) => {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
};
export default function Home() {
return (
## Use the Shark Preset [#use-the-shark-preset]
### Create Project [#create-project]
Run the `init` command with the Shark preset and the TanStack Start template:
```bash
npx shadcn@latest init @shark/style -t start
```
### Add Components [#add-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="src/routes/index.tsx" showLineNumbers
import { createFileRoute } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
export const Route = createFileRoute("/")({
component: App,
});
function App() {
return (
);
}
```
## Use the CLI [#use-the-cli]
### Create Project [#create-project-1]
Run the `init` command to scaffold a new TanStack Start project and configure Shark UI:
```bash
npx shadcn@latest init @shark/style -t start
```
For a monorepo project, use the `--monorepo` flag:
```bash
npx shadcn@latest init @shark/style -t start --monorepo
```
### Add Components [#add-components-1]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
If you created a monorepo, run the command from `apps/web` or specify the workspace from the repo root:
```bash
npx shadcn@latest add @shark/button -c apps/web
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="src/routes/index.tsx" showLineNumbers
import { createFileRoute } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
export const Route = createFileRoute("/")({
component: App,
});
function App() {
return (
);
}
```
If you created a monorepo, update `apps/web/src/routes/index.tsx` and import from your workspace UI package instead.
## Existing Project [#existing-project]
### Create Project [#create-project-2]
If you need a new TanStack Start project, create one first. Otherwise, skip this step.
```bash
npx @tanstack/cli@latest create
```
Choose TanStack Start, the React framework, and the recommended defaults so Tailwind CSS and the `@/*` import alias are configured for you.
Do not add the `shadcn` add-on when prompted. The `shadcn` CLI will configure Shark UI later in this guide.
The TanStack CLI already configures Tailwind CSS and the default `@/*` import alias for you. If you're adding Shark UI to an older or custom TanStack Start app, make sure both are configured before continuing.
### Run the CLI [#run-the-cli]
Run the `shadcn` init command with the Shark preset to set up your project.
```bash
npx shadcn@latest init @shark/style
```
### Add Components [#add-components-2]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="src/routes/index.tsx" showLineNumbers
import { createFileRoute } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
export const Route = createFileRoute("/")({
component: App,
});
function App() {
return (
);
}
```
# Vite (/docs/installation/vite)
## Use the Shark Preset [#use-the-shark-preset]
### Create Project [#create-project]
Run the `init` command with the Shark preset and the Vite template:
```bash
npx shadcn@latest init @shark/style -t vite
```
### Add Components [#add-components]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="src/App.tsx" showLineNumbers
import { Button } from "@/components/ui/button";
function App() {
return (
);
}
export default App;
```
## Use the CLI [#use-the-cli]
### Create Project [#create-project-1]
Run the `init` command to scaffold a new Vite project and configure Shark UI:
```bash
npx shadcn@latest init @shark/style -t vite
```
For a monorepo project, use the `--monorepo` flag:
```bash
npx shadcn@latest init @shark/style -t vite --monorepo
```
### Add Components [#add-components-1]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
If you created a monorepo, run the command from `apps/web` or specify the workspace from the repo root:
```bash
npx shadcn@latest add @shark/button -c apps/web
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="src/App.tsx" showLineNumbers
import { Button } from "@/components/ui/button";
function App() {
return (
);
}
export default App;
```
If you created a monorepo, update `apps/web/src/App.tsx` and import from your workspace UI package instead.
## Existing Project [#existing-project]
### Create Project [#create-project-2]
If you need a new Vite project, create one first and select the **React + TypeScript** template. Otherwise, skip this step.
```bash
npm create vite@latest
```
### Add Tailwind CSS [#add-tailwind-css]
If your project already has Tailwind CSS configured, skip this step.
```bash
npm install tailwindcss @tailwindcss/vite
```
Replace everything in `src/index.css` with the following:
```css title="src/index.css"
@import "tailwindcss";
```
### Edit tsconfig.json [#edit-tsconfigjson]
Vite splits TypeScript config across multiple files. Add `baseUrl` and `paths` to the `compilerOptions` in both `tsconfig.json` and `tsconfig.app.json`:
```ts title="tsconfig.json" showLineNumbers
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
```
### Edit tsconfig.app.json [#edit-tsconfigappjson]
Add the same path resolution so your IDE resolves imports correctly:
```ts title="tsconfig.app.json" showLineNumbers
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
```
### Update vite.config.ts [#update-viteconfigts]
Add the path alias so Vite resolves `@/` at build time:
```bash
npm install -D @types/node
```
```typescript title="vite.config.ts" showLineNumbers
import path from "path"
import tailwindcss from "@tailwindcss/vite"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})
```
### Run the CLI [#run-the-cli]
Run the `shadcn` init command with the Shark preset to set up your project.
```bash
npx shadcn@latest init @shark/style
```
### Add Components [#add-components-2]
You can add components with the CLI or copy them manually from the component docs:
* **CLI**: Run `npx shadcn@latest add @shark/` (e.g. `button`, `dialog`).
* **Manual**: Copy component source from Manual tab in the component docs.
For example, add the `Button` component to your project:
```bash
npx shadcn@latest add @shark/button
```
You can also install every component at once:
```bash
npx shadcn@latest add @shark/ui
```
Then import and use components like this:
```tsx title="src/App.tsx" showLineNumbers
import { Button } from "@/components/ui/button";
function App() {
return (
);
}
export default App;
```
# Client Only (/docs/utilities/client-only)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/client-only
```
Install the following dependencies:
```bash
npm install @ark-ui/react
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import { ClientOnly } from "@/registry/react/components/client-only";
```
```tsx
This content only renders on the client.
```
## Examples [#examples]
### With fallback [#with-fallback]
Pass `fallback` to show content while the client-only content is loading:
## API Reference [#api-reference]
### ClientOnly [#clientonly]
Shows children only on the client. Useful for hydration-safe client components.
| Prop | Type | Default |
| ---------- | ----------- | ------- |
| `fallback` | `ReactNode` | - |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/client-only#api-reference).
# Download Trigger (/docs/utilities/download-trigger)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/download-trigger
```
Install the following dependencies:
```bash
npm install @ark-ui/react
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import { DownloadTrigger } from "@/registry/react/components/download-trigger";
```
```tsx
Download
```
## Examples [#examples]
### Download SVG [#download-svg]
Download an SVG file by passing the SVG markup to `data` and setting `mimeType` to `image/svg+xml`.
### Promise [#promise]
Trigger downloads from a promise that returns a `Blob`, `File`, or `string`. Pass a function to `data`.
## API Reference [#api-reference]
### DownloadTrigger [#downloadtrigger]
| Prop | Type | Default |
| ---------- | ------------------------------------------------------------ | ------- |
| `data` | `DownloadableData \| (() => MaybePromise)` | - |
| `fileName` | `string` | - |
| `mimeType` | `FileMimeType` | - |
| `asChild` | `boolean` | - |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/download-trigger#api-reference).
# Format (/docs/utilities/format)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/format
```
Install the following dependencies:
```bash
npm install @ark-ui/react
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import {
FormatByte,
FormatNumber,
FormatRelativeTime
} from "@/registry/react/components/format";
```
```tsx
```
## Examples [#examples]
### Format Byte [#format-byte]
Format byte values as file sizes.
#### Unit display [#unit-display]
Use the `unitDisplay` prop to control how the unit is displayed.
#### Unit system [#unit-system]
Use `unitSystem` to choose between decimal (1000 bytes) or binary (1024 bytes).
### Format Relative Time [#format-relative-time]
Format dates as relative time.
#### Style [#style]
Use the `style` prop for long, short, or narrow format.
### Format Number [#format-number]
Format numbers with locale-aware options.
#### Currency [#currency]
Use `style="currency"` with the `currency` prop for currency formatting.
#### Percentage [#percentage]
Use `style="percent"` to format numbers as percentages.
#### Compact notation [#compact-notation]
Use `notation="compact"` for compact display.
## API Reference [#api-reference]
### FormatByte [#formatbyte]
| Prop | Type | Default |
| ------------- | ----------------------------------- | -------- |
| `value` | `number` | - |
| `unit` | `"byte"` \| `"bit"` | `"byte"` |
| `unitDisplay` | `"long"` \| `"short"` \| `"narrow"` | - |
| `unitSystem` | `"decimal"` \| `"binary"` | - |
| `locale` | `string` | - |
### FormatRelativeTime [#formatrelativetime]
| Prop | Type | Default |
| --------- | ----------------------------------- | ------- |
| `value` | `Date` \| `number` | - |
| `numeric` | `"always"` \| `"auto"` | - |
| `style` | `"long"` \| `"short"` \| `"narrow"` | - |
| `locale` | `string` | - |
### FormatNumber [#formatnumber]
| Prop | Type | Default |
| ----------------------- | ---------------------------------------------------------------- | ------------ |
| `value` | `number` | - |
| `locale` | `string` | - |
| `style` | `"decimal"` \| `"percent"` \| `"currency"` \| `"unit"` | `"decimal"` |
| `currency` | `string` | - |
| `unit` | `string` | - |
| `notation` | `"standard"` \| `"compact"` \| `"scientific"` \| `"engineering"` | `"standard"` |
| `minimumFractionDigits` | `number` | - |
| `maximumFractionDigits` | `number` | - |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/components/format#api-reference).
# Highlight (/docs/utilities/highlight)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/highlight
```
Install the following dependencies:
```bash
npm install @ark-ui/react
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import { Highlight } from "@/registry/react/components/highlight";
```
```tsx
```
## Examples [#examples]
### Multiple [#multiple]
Pass an array of strings to the `query` prop to highlight multiple substrings.
### Search Query [#search-query]
Use the Highlight component to highlight search query results.
### With Squiggle [#with-squiggle]
Use a custom decoration like a wavy underline for a fancier highlight effect.
### Custom Style [#custom-style]
Use the `className` prop to customize the style of the highlighted text.
## API Reference [#api-reference]
### Highlight [#highlight]
| Prop | Type | Default |
| ------------ | -------------------- | ------------ |
| `query` | `string \| string[]` | **required** |
| `text` | `string` | **required** |
| `ignoreCase` | `boolean` | `false` |
| `exactMatch` | `boolean` | `false` |
| `className` | `string` | `""` |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/highlight#api-reference).
# Hitbox (/docs/utilities/hitbox)
Kudos to [Kian Bazza](https://bazza.dev/craft/2026/hit-area) for the original implementation.
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/hitbox
```
Import the following semantic color variables into your CSS file when using
`hitbox-debug`
```css
@theme inline {
--color-info: var(--info);
--color-success: var(--success);
}
:root {
--info: var(--color-blue-500);
--success: var(--color-emerald-500);
}
.dark {
--info: var(--color-blue-500);
--success: var(--color-emerald-500);
}
```
Import the following CSS rules into your
`globals.css`
```css title="globals.css" showLineNumbers
@utility hitbox-debug {
position: relative;
&::before {
content: "";
position: absolute;
top: var(--hitbox-t, 0px);
right: var(--hitbox-r, 0px);
bottom: var(--hitbox-b, 0px);
left: var(--hitbox-l, 0px);
pointer-events: inherit;
@apply border border-dashed border-info bg-info/10;
}
&:hover::before {
@apply border border-dashed border-success bg-success/10;
}
}
@utility hitbox {
position: relative;
&::before {
content: "";
position: absolute;
top: var(--hitbox-t, 0px);
right: var(--hitbox-r, 0px);
bottom: var(--hitbox-b, 0px);
left: var(--hitbox-l, 0px);
pointer-events: inherit;
}
}
@utility hitbox-* {
position: relative;
--hitbox-t: calc(-1 * --spacing(--value(number, [*])));
--hitbox-b: calc(-1 * --spacing(--value(number, [*])));
--hitbox-l: calc(-1 * --spacing(--value(number, [*])));
--hitbox-r: calc(-1 * --spacing(--value(number, [*])));
&::before {
content: "";
position: absolute;
top: var(--hitbox-t, 0px);
right: var(--hitbox-r, 0px);
bottom: var(--hitbox-b, 0px);
left: var(--hitbox-l, 0px);
pointer-events: inherit;
}
}
@utility hitbox-l-* {
position: relative;
--hitbox-l: calc(-1 * --spacing(--value(number, [*])));
&::before { content: ""; position: absolute; top: var(--hitbox-t, 0px); right: var(--hitbox-r, 0px); bottom: var(--hitbox-b, 0px); left: var(--hitbox-l, 0px); pointer-events: inherit; }
}
@utility hitbox-r-* {
position: relative;
--hitbox-r: calc(-1 * --spacing(--value(number, [*])));
&::before { content: ""; position: absolute; top: var(--hitbox-t, 0px); right: var(--hitbox-r, 0px); bottom: var(--hitbox-b, 0px); left: var(--hitbox-l, 0px); pointer-events: inherit; }
}
@utility hitbox-t-* {
position: relative;
--hitbox-t: calc(-1 * --spacing(--value(number, [*])));
&::before { content: ""; position: absolute; top: var(--hitbox-t, 0px); right: var(--hitbox-r, 0px); bottom: var(--hitbox-b, 0px); left: var(--hitbox-l, 0px); pointer-events: inherit; }
}
@utility hitbox-b-* {
position: relative;
--hitbox-b: calc(-1 * --spacing(--value(number, [*])));
&::before { content: ""; position: absolute; top: var(--hitbox-t, 0px); right: var(--hitbox-r, 0px); bottom: var(--hitbox-b, 0px); left: var(--hitbox-l, 0px); pointer-events: inherit; }
}
@utility hitbox-x-* {
position: relative;
--hitbox-l: calc(-1 * --spacing(--value(number, [*])));
--hitbox-r: calc(-1 * --spacing(--value(number, [*])));
&::before { content: ""; position: absolute; top: var(--hitbox-t, 0px); right: var(--hitbox-r, 0px); bottom: var(--hitbox-b, 0px); left: var(--hitbox-l, 0px); pointer-events: inherit; }
}
@utility hitbox-y-* {
position: relative;
--hitbox-t: calc(-1 * --spacing(--value(number, [*])));
--hitbox-b: calc(-1 * --spacing(--value(number, [*])));
&::before { content: ""; position: absolute; top: var(--hitbox-t, 0px); right: var(--hitbox-r, 0px); bottom: var(--hitbox-b, 0px); left: var(--hitbox-l, 0px); pointer-events: inherit; }
}
```
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
Link
```
## Examples [#examples]
### Basic [#basic]
Use `hitbox-*` to extend the hit area uniformly on all sides.
### Individual sides [#individual-sides]
Target specific sides with `hitbox-l-*`, `hitbox-r-*`, `hitbox-t-*`, and `hitbox-b-*`.
### Horizontal and vertical [#horizontal-and-vertical]
Use `hitbox-x-*` and `hitbox-y-*` for axis shorthands.
### Custom values [#custom-values]
Use arbitrary values with bracket syntax.
### Debugging [#debugging]
Add `hitbox-debug` to visualize the expanded hit area.
### Sidebar navigation [#sidebar-navigation]
Use `hitbox-y-*` so adjacent items feel continuous.
## API Reference [#api-reference]
| Class | Expansion |
| -------------- | -------------------------------- |
| `hitbox` | Base (no expansion) |
| `hitbox-*` | All sides (spacing or `[value]`) |
| `hitbox-l-*` | Left |
| `hitbox-r-*` | Right |
| `hitbox-t-*` | Top |
| `hitbox-b-*` | Bottom |
| `hitbox-x-*` | Left and right |
| `hitbox-y-*` | Top and bottom |
| `hitbox-debug` | Debug overlay |
# JSON Tree View (/docs/utilities/json-tree-view)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/json-tree-view
```
Install the following dependencies:
```bash
npm install @ark-ui/react lucide-react
```
Import the following variables into your CSS file
```css
@theme inline {
--color-destructive-foreground: var(--destructive-foreground);
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}
:root {
--destructive-foreground: var(--color-red-700);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
}
.dark {
--destructive-foreground: var(--color-red-400);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
}
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import { JsonTreeView } from "@/registry/react/components/json-tree-view";
const data = { name: "John", age: 30, address: { city: "NYC" } };
```
## Examples [#examples]
### Different data types [#different-data-types]
Display various JavaScript data types including objects, arrays, primitives, dates, and special values.
### Controlling expand level [#controlling-expand-level]
Use the `defaultExpandedDepth` prop to control how many levels are expanded by default.
### Map and Set [#map-and-set]
Native JavaScript Map and Set objects are supported.
## API Reference [#api-reference]
### JsonTreeView [#jsontreeview]
| Prop | Type | Default |
| ---------------------- | --------------------- | ------- |
| `data` | `object` | - |
| `defaultExpandedDepth` | `number` | - |
| `renderValue` | `(node) => ReactNode` | - |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/json-tree-view#api-reference).
# Presence (/docs/utilities/presence)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/presence
```
Install the following dependencies:
```bash
npm install @ark-ui/react
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import React from "react";
import { Presence } from "@/registry/react/components/presence";
const [visible, setVisible] = React.useState(false);
Content that animates in and out
```
## Lazy [#lazy]
Setting `lazyMount` and `unmountOnExit` to `true` lazy mounts and unmounts the content.
It will remove the element from the DOM when it is not present.
```tsx showLineNumbers
{/** content */}
```
## States [#states]
Presence automatically manages the attribute `data-state` to `open` and `closed`. This enables styling and animations based on visibility state.
```tsx showLineNumbers
//
//
```
## API Reference [#api-reference]
### Presence [#presence]
| Prop | Type | Default |
| --------------- | --------- | ------- |
| `present` | `boolean` | `false` |
| `unmountOnExit` | `boolean` | `true` |
| `lazyMount` | `boolean` | `true` |
| `asChild` | `boolean` | `false` |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/presence#api-reference).
# Show (/docs/utilities/show)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/show
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import { Show } from "@/registry/react/components/show";
```
```tsx
}>
{/** content */}
```
## API Reference [#api-reference]
### Show [#show]
| Prop | Type | Default |
| ---------- | ----------------- | -------- |
| `when` | `boolean` | required |
| `fallback` | `React.ReactNode` | - |
| `children` | `React.ReactNode` | required |
# Swap (/docs/utilities/swap)
## Installation [#installation]
CLI
Manual
```bash
npx shadcn@latest add @shark/swap
```
Install the following dependencies:
```bash
npm install @ark-ui/react tailwind-variants
```
Add the following keyframes to your
`globals.css`
for the flip variant:
```css title="globals.css"
@theme inline {
--animate-flip-in: flip-in 0.2s ease-out;
--animate-flip-out: flip-out 0.2s ease-out;
@keyframes flip-in {
from {
transform: rotateY(180deg);
}
to {
transform: rotateY(0deg);
}
}
@keyframes flip-out {
from {
transform: rotateY(0deg);
}
to {
transform: rotateY(180deg);
}
}
}
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage [#usage]
```tsx
import {
Swap,
SwapIndicator
} from "@/registry/react/components/swap";
```
```tsx
```
## Examples [#examples]
### Fade [#fade]
### Flip [#flip]
### Rotate [#rotate]
### Scale [#scale]
### Blur [#blur]
## API Reference [#api-reference]
### Swap [#swap]
| Prop | Type | Default |
| --------------- | --------------------------------------------------- | -------- |
| `variant` | `"fade" \| "scale" \| "flip" \| "rotate" \| "blur"` | `"fade"` |
| `swap` | `boolean` | `-` |
| `lazyMount` | `boolean` | `true` |
| `unmountOnExit` | `boolean` | `true` |
| `className` | `string` | `-` |
| `asChild` | `boolean` | `-` |
### SwapIndicator [#swapindicator]
| Prop | Type | Default |
| ----------- | --------------- | ------------ |
| `type` | `'on' \| 'off'` | **required** |
| `className` | `string` | `-` |
| `asChild` | `boolean` | `-` |
***
For a complete list of props, see the [Ark UI documentation](https://ark-ui.com/docs/utilities/swap#api-reference).
# Accordion - Examples and Patterns
Examples for Accordion component.
## Example: card
```tsx
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
const AccordionCardDemo = () => (
Product Information
Common questions about our products, shipping, and returns.
Product Information
Our flagship product combines cutting-edge technology with sleek
design. Built with premium materials, it offers unparalleled
performance and reliability.
Shipping Details
We offer worldwide shipping through trusted courier partners.
Standard delivery takes 3-5 business days, while express shipping
ensures delivery within 1-2 business days.
Return Policy
We stand behind our products with a comprehensive 30-day return
policy. If you're not completely satisfied, simply return the
item in its original condition.
setValue(value)} value={value}>
Product Information
Our flagship product combines cutting-edge technology with sleek
design. Built with premium materials, it offers unparalleled
performance and reliability.
Shipping Details
We offer worldwide shipping through trusted courier partners.
Standard delivery takes 3-5 business days.
Return Policy
We stand behind our products with a comprehensive 30-day return
policy. If you're not completely satisfied, simply return the
item in its original condition.
Our flagship product combines cutting-edge technology with sleek
design. Built with premium materials, it offers unparalleled
performance and reliability.
Key features include advanced processing capabilities, and an
intuitive user interface designed for both beginners and experts.
Shipping Details
We offer worldwide shipping through trusted courier partners. Standard
delivery takes 3-5 business days, while express shipping ensures
delivery within 1-2 business days.
All orders are carefully packaged and fully insured. Track your
shipment in real-time through our dedicated tracking portal.
Return Policy
We stand behind our products with a comprehensive 30-day return
policy. If you're not completely satisfied, simply return the
item in its original condition.
Our hassle-free return process includes free return shipping and full
refunds processed within 48 hours of receiving the returned item.
);
export default AccordionDemo;
```
## Example: disabled
```tsx
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
const Example = () => (
Product Information
Our flagship product combines cutting-edge technology with sleek
design. Built with premium materials, it offers unparalleled
performance and reliability.
Shipping Details
We offer worldwide shipping through trusted courier partners. Standard
delivery takes 3-5 business days.
Return Policy
We stand behind our products with a comprehensive 30-day return
policy. If you're not completely satisfied, simply return the
item in its original condition.
);
export default Example;
```
## Example: multiple
```tsx
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
const Example = () => (
Product Information
Our flagship product combines cutting-edge technology with sleek
design. Built with premium materials, it offers unparalleled
performance and reliability.
Shipping Details
We offer worldwide shipping through trusted courier partners. Standard
delivery takes 3-5 business days.
Return Policy
We stand behind our products with a comprehensive 30-day return
policy. If you're not completely satisfied, simply return the
item in its original condition.
);
export default Example;
```
## Example: non-collapsible
```tsx
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
const Example = () => (
Product Information
Our flagship product combines cutting-edge technology with sleek
design. Built with premium materials, it offers unparalleled
performance and reliability.
Shipping Details
We offer worldwide shipping through trusted courier partners. Standard
delivery takes 3-5 business days.
Return Policy
We stand behind our products with a comprehensive 30-day return
policy. If you're not completely satisfied, simply return the
item in its original condition.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
imperdiet placerat nisl, ac consequat sem hendrerit in.
Why do we use it?
Pellentesque quis sapien tortor. Nulla egestas tristique justo, in
commodo quam posuere id. Cras varius, nunc non placerat vulputate,
dolor turpis elementum elit, non lobortis lacus nunc nec nisl.
Where does it come from?
Pellentesque turpis est, mollis eu arcu eu, tempor tincidunt urna.
Pellentesque pellentesque est euismod accumsan ullamcorper. Quisque
urna lorem, porttitor ac malesuada at, vehicula eget nulla. Donec
eget consequat erat, quis pharetra ex.
Where can I get some?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
egestas semper eros a maximus. Sed consequat tempus lobortis.
Phasellus sed vulputate turpis. Nulla facilisi. Curabitur consequat
dui tellus.
Who can I contact if I have questions?
Donec tortor lorem, finibus vel suscipit vehicula, sagittis
efficitur erat. Proin sagittis aliquam sagittis. Nullam sed porta
leo. Nunc sed velit felis.
What happens if I don't agree to these terms?
Aenean maximus, libero vel laoreet congue, purus leo iaculis libero,
egestas egestas quam mi at quam. Curabitur eu tempus mauris.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
posuere cubilia curae;
Where can I get some?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
egestas semper eros a maximus. Sed consequat tempus lobortis.
Phasellus sed vulputate turpis. Nulla facilisi. Curabitur consequat
dui tellus.
);
export default Example;
```
# Download Trigger - Examples and Patterns
Examples for Download Trigger component.
## Example: default
```tsx
import { DownloadIcon, FileTextIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { DownloadTrigger } from "@/components/ui/download-trigger";
import { Item, ItemTitle } from "@/components/ui/item";
const DownloadTriggerDemo = () => (
{content}
);
const content = "Hello, World! This is a sample text file.";
export default DownloadTriggerDemo;
```
## Example: download-svg
```tsx
import { DownloadIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { DownloadTrigger } from "@/components/ui/download-trigger";
const Example = () => (
);
const svgContent = ``;
export default Example;
```
## Example: promise
```tsx
"use client";
import { DownloadIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { DownloadTrigger } from "@/components/ui/download-trigger";
const Example = () => (
);
const fetchData = () =>
new Promise((resolve) => {
setTimeout(() => {
resolve(
new Blob(['{"message": "Loaded asynchronously!"}'], {
type: "application/json",
})
);
}, 500);
});
export default Example;
```
# Drawer - Examples and Patterns
Examples for Drawer component.
## Example: custom-spacing
```tsx
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerBody,
DrawerClose,
DrawerContent,
DrawerContentInner,
DrawerFooter,
DrawerHeader,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameUsername
);
export default Example;
```
## Example: default
```tsx
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerBody,
DrawerClose,
DrawerContent,
DrawerContentInner,
DrawerFooter,
DrawerHeader,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const DrawerDemo = () => (
NameUsername
);
export default DrawerDemo;
```
## Example: drawer-content-inner
```tsx
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerBody,
DrawerClose,
DrawerContent,
DrawerContentInner,
DrawerFooter,
DrawerHeader,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameEmail
);
export default Example;
```
## Example: inset
```tsx
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerBody,
DrawerClose,
DrawerContent,
DrawerContentInner,
DrawerFooter,
DrawerHeader,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameEmail
);
export default Example;
```
## Example: snap-points
```tsx
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerBody,
DrawerContent,
DrawerContentInner,
DrawerHeader,
DrawerTrigger,
} from "@/components/ui/drawer";
const Example = () => (
This drawer has multiple snap points. Try dragging the handle to
quarter, half, or full height.
);
export default Example;
```
# Editable - Examples and Patterns
Examples for Editable component.
## Example: activation-click
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameUsername
);
export default Example;
```
## Example: activation-focus
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameUsername
);
export default Example;
```
## Example: activation-none
```tsx
import { CheckIcon, EditIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableEditTrigger,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameUsername
);
export default Example;
```
## Example: controlled
```tsx
"use client";
import { CheckIcon, PencilIcon } from "lucide-react";
import React from "react";
import { Button } from "@/components/ui/button";
import {
Card,
CardAction,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableInput,
EditablePreview,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => {
const [isEditing, setIsEditing] = React.useState(false);
return (
NameUsername
);
};
export default Example;
```
## Example: dblclick
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameUsername
);
export default Example;
```
## Example: default
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const EditableDemo = () => (
NameUsername
);
export default EditableDemo;
```
## Example: orientation-horizontal
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import { Input } from "@/components/ui/input";
const Example = () => (
);
export default Example;
```
## Example: orientation-vertical
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import { Textarea } from "@/components/ui/textarea";
const Example = () => (
);
export default Example;
```
## Example: size-lg
```tsx
import { CheckIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import { Input } from "@/components/ui/input";
const Example = () => (
);
export default Example;
```
## Example: size-md
```tsx
import { CheckIcon, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import { Input } from "@/components/ui/input";
const Example = () => (
);
export default Example;
```
## Example: size-sm
```tsx
import { CheckIcon, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import { Input } from "@/components/ui/input";
const Example = () => (
);
export default Example;
```
## Example: with-textarea
```tsx
import { CheckIcon, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableCancelTrigger,
EditableControl,
EditableInput,
EditablePreview,
EditableSubmitTrigger,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Textarea } from "@/components/ui/textarea";
const Example = () => (
Description
);
export default Example;
```
## Example: without-controls
```tsx
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Editable,
EditableArea,
EditableInput,
EditablePreview,
} from "@/components/ui/editable";
import {
Field,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
NameEmail
);
export default Example;
```
# Field - Examples and Patterns
Examples for Field component.
## Example: autocomplete-field
```tsx
"use client";
import { useFilter, useListCollection } from "@ark-ui/react";
import {
Autocomplete,
AutocompleteContent,
AutocompleteEmpty,
AutocompleteInput,
AutocompleteItem,
} from "@/components/ui/autocomplete";
import { ComboboxList } from "@/components/ui/combobox";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const Example = () => {
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
initialItems,
filter: contains,
});
return (
Fruits filter(inputValue)}
>
No items found.
{collection.items.map((item) => (
{item.label}
))}
Select an item.
);
};
const initialItems = [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
{ label: "Grape", value: "grape" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Mango", value: "mango" },
{ label: "Pineapple", value: "pineapple" },
{ label: "Kiwi", value: "kiwi" },
{ label: "Peach", value: "peach" },
{ label: "Pear", value: "pear" },
];
export default Example;
```
## Example: checkbox-field
```tsx
"use client";
import { Checkbox } from "@/components/ui/checkbox";
import {
Field,
FieldContent,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const Example = () => (
Receive notifications
You'll receive a notification when someone posts a comment
);
export default Example;
```
## Example: checkbox-group-field
```tsx
"use client";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
import {
Field,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
} from "@/components/ui/field";
const Example = () => (
);
export default Example;
```
## Example: combobox-field
```tsx
"use client";
import { useFilter, useListCollection } from "@ark-ui/react";
import {
Combobox,
ComboboxContent,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const Example = () => {
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
initialItems,
filter: contains,
});
return (
Fruits filter(inputValue)}
>
{collection.items.map((item) => (
{item.label}
))}
Select an item.
);
};
const initialItems = [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
{ label: "Grape", value: "grape" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Mango", value: "mango" },
{ label: "Pineapple", value: "pineapple" },
{ label: "Kiwi", value: "kiwi" },
{ label: "Peach", value: "peach" },
{ label: "Pear", value: "pear" },
];
export default Example;
```
## Example: combobox-multiple-field
```tsx
"use client";
import { useFilter, useListCollection } from "@ark-ui/react";
import {
Combobox,
ComboboxContent,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const Example = () => {
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
initialItems,
filter: contains,
});
return (
Fruits filter(inputValue)}
>
{collection.items.map((item) => (
{item.label}
))}
Select multiple items.
);
};
const initialItems = [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
{ label: "Grape", value: "grape" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Mango", value: "mango" },
{ label: "Pineapple", value: "pineapple" },
{ label: "Kiwi", value: "kiwi" },
{ label: "Peach", value: "peach" },
{ label: "Pear", value: "pear" },
];
export default Example;
```
## Example: default
```tsx
"use client";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const FieldDemo = () => (
Username
Choose a unique username for your account.
);
export default FieldDemo;
```
## Example: disabled-field
```tsx
"use client";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
EmailThis field is currently disabled.
);
export default Example;
```
## Example: field-group
```tsx
"use client";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@/components/ui/field";
const Example = () => (
);
export default Example;
```
## Example: input-group
```tsx
"use client";
import { ArrowRightIcon } from "lucide-react";
import {
Field,
FieldError,
FieldLabel,
} from "@/components/ui/field";
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group";
const Example = () => (
SubscribePlease enter a valid email address.
);
export default Example;
```
## Example: number-input
```tsx
"use client";
import { Field } from "@/components/ui/field";
import {
NumberInput,
NumberInputDecrement,
NumberInputGroup,
NumberInputIncrement,
NumberInputInput,
NumberInputScrubber,
} from "@/components/ui/number-input";
const Example = () => (
Quantity
);
export default Example;
```
## Example: orientation-horizontal
```tsx
"use client";
import { Field, FieldLabel } from "@/components/ui/field";
import { Switch } from "@/components/ui/switch";
const Example = () => (
Enable notifications
);
export default Example;
```
## Example: orientation-vertical
```tsx
"use client";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
Name
Stacks label, control, and description vertically.
);
export default Example;
```
## Example: radio-group-field
```tsx
"use client";
import {
Field,
FieldDescription,
FieldLegend,
FieldSet,
} from "@/components/ui/field";
import {
RadioGroup,
RadioGroupItem,
} from "@/components/ui/radio-group";
const Example = () => (
);
export default Example;
```
## Example: required-field
```tsx
"use client";
import {
Field,
FieldError,
FieldLabel,
FieldRequiredIndicator,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
Password Please fill out this field.
);
export default Example;
```
## Example: select-field
```tsx
"use client";
import { createListCollection } from "@ark-ui/react";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const Example = () => (
CountryUsed for shipping estimates
);
const collection = createListCollection({
items: ["Brazil", "Mexico", "Ireland"],
});
export default Example;
```
## Example: slider-field
```tsx
"use client";
import { Field, FieldDescription } from "@/components/ui/field";
import { Slider, SliderLabel } from "@/components/ui/slider";
const Example = () => (
VolumeAdjust the volume of the media player
);
export default Example;
```
## Example: switch-field
```tsx
"use client";
import { Field, FieldLabel } from "@/components/ui/field";
import { Switch } from "@/components/ui/switch";
const Example = () => (
Airplane mode
);
export default Example;
```
## Example: textarea-field
```tsx
"use client";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Textarea } from "@/components/ui/textarea";
const Example = () => (
Bio
Write a short bio. Maximum 500 characters.
);
export default Example;
```
## Example: with-error
```tsx
"use client";
import {
Field,
FieldError,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
const Example = () => (
EmailPlease enter a valid email address.
);
export default Example;
```
# File Upload - Examples and Patterns
Examples for File Upload component.
## Example: accepted-file-types
```tsx
import {
FileUpload,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadHelper,
FileUploadList,
FileUploadTitle,
} from "@/components/ui/file-upload";
const Example = () => (
Drop your images here
Only PNG and JPEG formats are allowed.
);
export default Example;
```
## Example: clear-trigger
```tsx
"use client";
import { Trash2Icon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadClearTrigger,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadList,
FileUploadTitle,
} from "@/components/ui/file-upload";
const Example = () => (
Drop files here
);
export default Example;
```
## Example: custom-preview
```tsx
"use client";
import { XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadItem,
FileUploadItemDeleteTrigger,
FileUploadItemGroup,
FileUploadItemPreview,
FileUploadItemPreviewImage,
FileUploadTitle,
useFileUpload,
} from "@/components/ui/file-upload";
const Example = () => (
Drop files here
);
const CustomPreviewList = () => {
const fileUpload = useFileUpload();
const files = fileUpload.acceptedFiles;
if (files.length === 0) {
return null;
}
return (
{files.map((file) => (
))}
);
};
export default Example;
```
## Example: custom-spacing
```tsx
"use client";
import {
FileUpload,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadList,
FileUploadTitle,
} from "@/components/ui/file-upload";
const Example = () => (
Drop your files here
);
export default Example;
```
## Example: default
```tsx
"use client";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadDescription,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadHelper,
FileUploadList,
FileUploadTitle,
FileUploadTrigger,
} from "@/components/ui/file-upload";
import { Separator } from "@/components/ui/separator";
const FileUploadDemo = () => (
Drop files here
or
You can upload up to 2 files at a time.
);
export default FileUploadDemo;
```
## Example: directory-upload
```tsx
"use client";
import { Folder } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadList,
FileUploadTrigger,
} from "@/components/ui/file-upload";
const Example = () => (
);
export default Example;
```
## Example: dropzone
```tsx
"use client";
import {
FileUpload,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadList,
FileUploadTitle,
} from "@/components/ui/file-upload";
const Example = () => (
Drop your files here
);
export default Example;
```
## Example: media-capture
```tsx
"use client";
import { CameraIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadList,
FileUploadTrigger,
} from "@/components/ui/file-upload";
const Example = () => (
);
export default Example;
```
## Example: multiple-files
```tsx
"use client";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadDescription,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadHelper,
FileUploadList,
FileUploadTitle,
FileUploadTrigger,
} from "@/components/ui/file-upload";
import { Separator } from "@/components/ui/separator";
const Example = () => (
Drop files here
or
You can upload up to 5 files at a time.
);
export default Example;
```
## Example: trigger
```tsx
import { PaperclipIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
FileUpload,
FileUploadList,
FileUploadTrigger,
} from "@/components/ui/file-upload";
const Example = () => (
);
export default Example;
```
## Example: with-field
```tsx
"use client";
import { Field, FieldLabel } from "@/components/ui/field";
import {
FileUpload,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadList,
} from "@/components/ui/file-upload";
const Example = () => (
Upload images
);
export default Example;
```
# Float - Examples and Patterns
Examples for Float component.
## Example: default
```tsx
import { BellIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Float } from "@/components/ui/float";
const FloatDemo = () => (
9+
);
export default FloatDemo;
```
## Example: placement
```tsx
import { Badge } from "@/components/ui/badge";
import { Float } from "@/components/ui/float";
const Example = () => (
),
});
},
});
return (
);
};
export default Example;
```
## Example: tanstack/demo
```tsx
"use client";
import { toast } from "@registry/react/components/toast";
import { useForm } from "@tanstack/react-form";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
} from "@/components/ui/card";
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
InputGroup,
InputGroupAddon,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group";
const formSchema = z.object({
title: z
.string()
.min(5, "Bug title must be at least 5 characters.")
.max(32, "Bug title must be at most 32 characters."),
description: z
.string()
.min(20, "Description must be at least 20 characters.")
.max(100, "Description must be at most 100 characters."),
});
export const BugReportForm = () => {
const form = useForm({
defaultValues: {
title: "",
description: "",
},
validators: {
onSubmit: formSchema,
},
onSubmit: ({ value }) => {
toast.info({
id: "bug-report-submitted",
title: "Bug submitted",
description: (
{JSON.stringify(value, null, 2)}
),
});
},
});
return (
);
};
export default BugReportForm;
```
## Example: tanstack/file-upload
```tsx
"use client";
import { toast } from "@registry/react/components/toast";
import { useForm } from "@tanstack/react-form";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import {
FileUpload,
FileUploadDropzone,
FileUploadDropzoneIcon,
FileUploadList,
} from "@/components/ui/file-upload";
const formSchema = z.object({
resume: z
.array(z.instanceof(File))
.min(1, "Please upload at least one PDF or Word document."),
});
const Example = () => {
const form = useForm({
defaultValues: { resume: [] as File[] },
validators: {
onSubmit: formSchema,
},
onSubmit: ({ value }) => {
toast.info({
id: "resume-submitted",
title: "Resume uploaded",
description: (
Once upon a time, in a far-off land, there was a very lazy king who
spent all day lounging on his throne. One day, his advisors came to
him with a problem: the kingdom was running out of money.
The King's Plan
The king thought long and hard, and finally came up with{" "}
a brilliant plan: he would tax the jokes in the
kingdom.
"After all," he said, "everyone enjoys a good joke, so
it's only fair that they should pay for the privilege."
The Joke Tax
The king's subjects were not amused. They grumbled and
complained, but the king was firm:
1st level of puns: 5 gold coins
2nd level of jokes: 10 gold coins
3rd level of one-liners : 20 gold coins
As a result, people stopped telling jokes, and the kingdom fell into a
gloom. But there was one person who refused to let the king's
foolishness get him down: a court jester named Jokester.
Jokester's Revolt
Jokester began sneaking into the castle in the middle of the night and
leaving jokes all over the place: under the king's pillow, in his
soup, even in the royal toilet. The king was furious, but he
couldn't seem to stop Jokester.
And then, one day, the people of the kingdom discovered that the jokes
left by Jokester were so funny that they couldn't help but laugh.
And once they started laughing, they couldn't stop.
The People's Rebellion
The people of the kingdom, feeling uplifted by the laughter, started
to tell jokes and puns again, and soon the entire kingdom was in on
the joke.
King's Treasury
People's happiness
Empty
Overflowing
Modest
Satisfied
Full
Ecstatic
The king, seeing how much happier his subjects were, realized the
error of his ways and repealed the joke tax. Jokester was declared a
hero, and the kingdom lived happily ever after.
The moral of the story is: never underestimate the power of a good
laugh and always be careful of bad ideas.
);
export default TypographyDemo;
```
## Example: details
```tsx
import { Prose } from "@/components/ui/prose";
const Example = () => (
How did the joke tax end?
The king repealed the tax after seeing how much happier his subjects
were. Jokester was declared a hero.
);
export default Example;
```
## Example: small
```tsx
import { Prose } from "@/components/ui/prose";
const Example = () => (
Never underestimate the power of a good laugh and always be careful of bad
ideas.
);
export default Example;
```
## Example: table
```tsx
import { Prose } from "@/components/ui/prose";
const Example = () => (
King's Treasury
People's happiness
Empty
Overflowing
Modest
Satisfied
Full
Ecstatic
);
export default Example;
```
# Qr Code - Examples and Patterns
Examples for Qr Code component.
## Example: default
```tsx
import { QrCode, QrCodeFrame } from "@/components/ui/qr-code";
const QrCodeDemo = () => (
);
export default QrCodeDemo;
```
## Example: download
```tsx
"use client";
import { DownloadIcon } from "lucide-react";
import React from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import {
QrCode,
QrCodeDownload,
QrCodeFrame,
} from "@/components/ui/qr-code";
const QUALITY_BY_LEVEL = {
0: "L",
20: "M",
40: "Q",
60: "H",
} as const;
const Example = () => {
const [value, setValue] = React.useState("");
const qualityLabel = QUALITY_BY_LEVEL[getQualityLevel(value.length)];
return (
setValue(e.target.value)}
placeholder="Generate a QR code"
value={value}
/>
Vinicius Vicentini Tuestday morning at Mercado N.89
Rate your driver
);
export default RatingDemo;
```
## Example: disabled
```tsx
import { Rating } from "@/components/ui/rating";
const Example = () => ;
export default Example;
```
## Example: half-star
```tsx
import { Rating } from "@/components/ui/rating";
const Example = () => ;
export default Example;
```
## Example: readonly
```tsx
import { Rating } from "@/components/ui/rating";
const Example = () => ;
export default Example;
```
## Example: testimonial
```tsx
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import {
Card,
CardContent,
CardDescription,
CardTitle,
} from "@/components/ui/card";
import { Rating } from "@/components/ui/rating";
const Example = () => (
“This completely changed our workflow. Fast, reliable, and the
team loves it. Would recommend to anyone.”
VV
Vinicius Vicentini
Frontend Developer
);
export default Example;
```
# Resizable - Examples and Patterns
Examples for Resizable component.
## Example: default
```tsx
import {
Resizable,
ResizablePanel,
ResizableResizeTrigger,
} from "@/components/ui/resizable";
const ResizableDemo = () => (
One
Two
Three
);
export default ResizableDemo;
```
## Example: handle
```tsx
import {
Resizable,
ResizablePanel,
ResizableResizeTrigger,
} from "@/components/ui/resizable";
const Example = () => (
Panel 1
Panel 2
);
export default Example;
```
## Example: min-max
```tsx
import {
Resizable,
ResizablePanel,
ResizableResizeTrigger,
} from "@/components/ui/resizable";
const Example = () => (
Sidebar
Content
);
export default Example;
```
## Example: multiple-panels
```tsx
import {
Resizable,
ResizablePanel,
ResizableResizeTrigger,
} from "@/components/ui/resizable";
const Example = () => (
Left
Center
Right
);
export default Example;
```
## Example: orientation-horizontal
```tsx
import {
Resizable,
ResizablePanel,
ResizableResizeTrigger,
} from "@/components/ui/resizable";
const Example = () => (
Left
Right
);
export default Example;
```
## Example: orientation-vertical
```tsx
import {
Resizable,
ResizablePanel,
ResizableResizeTrigger,
} from "@/components/ui/resizable";
const Example = () => (
Top
Bottom
);
export default Example;
```
# Scroll Area - Examples and Patterns
Examples for Scroll Area component.
## Example: both-directions
```tsx
import { ScrollArea } from "@/components/ui/scroll-area";
const Example = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut nulla
metus. Ut consequat augue et semper porttitor. Integer vel ante arcu.
Nullam tincidunt dolor odio, ac tincidunt leo dictum eu. Proin auctor,
nulla vel tincidunt lacinia, leo erat sagittis erat, quis porta orci sem
id purus. Nam fermentum turpis vitae pretium facilisis. Mauris id
iaculis augue, ut tristique purus. Pellentesque sed diam semper, porta
nibh ut, sodales nunc. Aliquam accumsan a mi eget fringilla. Vestibulum
varius mi vitae sem placerat, et imperdiet lorem fringilla. Curabitur
sed congue mi, quis tincidunt tortor. Suspendisse pharetra sem vel risus
volutpat, a auctor massa faucibus.
Etiam posuere felis et consectetur molestie. Cras sed rhoncus nisl.
Aenean quis est sit amet quam facilisis lacinia at non magna. In eu orci
accumsan, ultrices justo vitae, sodales nibh. Curabitur in sagittis dui.
Maecenas commodo cursus magna, non fringilla nisl commodo in. Vestibulum
nec fermentum dolor. Etiam euismod nisl non scelerisque faucibus.
Aliquam erat volutpat. Donec quis nunc ultrices, viverra quam ut,
sagittis tortor. Nullam nulla tortor, convallis nec magna ut, lacinia
interdum est. Proin lobortis diam sollicitudin venenatis dictum.
Mauris a dui a nibh ullamcorper tempus. Maecenas laoreet magna venenatis
leo mattis sagittis. Donec in convallis leo, quis suscipit leo. Sed a
augue purus. Integer id vulputate erat. Quisque a arcu purus. Nulla
feugiat ex tellus, ac elementum magna porttitor a. Sed convallis rhoncus
aliquam. Praesent euismod metus a fermentum faucibus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
This is a nested scroll area. Duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde
omnis iste natus error sit voluptatem accusantium doloremque
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
imperdiet placerat nisl, ac consequat sem hendrerit in.
Why do we use it?
Pellentesque quis sapien tortor. Nulla egestas tristique justo, in
commodo quam posuere id. Cras varius, nunc non placerat vulputate,
dolor turpis elementum elit, non lobortis lacus nunc nec nisl.
Where does it come from?
Pellentesque turpis est, mollis eu arcu eu, tempor tincidunt urna.
Pellentesque pellentesque est euismod accumsan ullamcorper. Quisque
urna lorem, porttitor ac malesuada at, vehicula eget nulla. Donec
eget consequat erat, quis pharetra ex.
Where can I get some?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
egestas semper eros a maximus. Sed consequat tempus lobortis.
Phasellus sed vulputate turpis. Nulla facilisi. Curabitur consequat
dui tellus.
Who can I contact if I have questions?
Donec tortor lorem, finibus vel suscipit vehicula, sagittis
efficitur erat. Proin sagittis aliquam sagittis. Nullam sed porta
leo. Nunc sed velit felis.
What happens if I don't agree to these terms?
Aenean maximus, libero vel laoreet congue, purus leo iaculis libero,
egestas egestas quam mi at quam. Curabitur eu tempus mauris.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
posuere cubilia curae;
Where can I get some?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
egestas semper eros a maximus. Sed consequat tempus lobortis.
Phasellus sed vulputate turpis. Nulla facilisi. Curabitur consequat
dui tellus.
);
export default SpinnerDemo;
```
## Example: input-group
```tsx
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Spinner } from "@/components/ui/spinner";
const Example = () => (
);
export default Example;
```
## Example: size
```tsx
import { Spinner } from "@/components/ui/spinner";
const Example = () => (
);
export default Example;
```
# Status - Examples and Patterns
Examples for Status component.
## Example: custom-color
```tsx
import { Status } from "@/components/ui/status";
const Example = () => (
);
export default Example;
```
## Example: custom-size
```tsx
import { Status } from "@/components/ui/status";
const Example = () => (
Users with row context menu. Right-click a row to open the menu.
NameEmail
{users.map((user) => (
{user.name}{user.email}
View
⌘ V
Edit
⌘ E
Delete
⌘ ⌫
))}