`, and `` elements for structured data.
* Buttons have visible focus rings via `focus-visible:ring-2` and minimum touch target sizes (`min-h-[44px]`, `min-w-[44px]`).
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the image lift animation, metadata panel entrance blur/spring, and the open/close button blur transitions are all disabled.
## Props
# UI Components (/docs/components)
# Infinite Slider (/docs/components/infinite-slider)
## Features
* Smooth infinite scrolling animation
* Customizable speed and gap
* Pause on hover support
* Horizontal and vertical directions
* Reverse direction option
* Seamless loop without jumps
## Accessibility
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the scrolling animation is stopped and the translation is reset to 0, displaying the content statically.
## Props
# Interactive Image Selector (/docs/components/interactive-image-selector)
## Features
* Toggle selection with keyboard and pointer
* Animated selection ring and scales
* Custom renderers and captions
* Responsive grid
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| ------------ | -------------------- | ------------------------------------------ |
| `aria-label` | Reset button | Provides accessible name "Reset selection" |
| `aria-label` | Select/Cancel button | Dynamically describes current action |
### Screen Reader
* Each gallery image has an `alt` attribute (e.g., "Gallery item 1").
* The selection count is displayed as visible text in the action bar.
* The Share and Delete buttons use icon-only presentation without accessible labels.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, shake and slide-in animations are disabled.
## Props
# Job Listing Component (/docs/components/job-listing-component)
## Features
* Animated card entrance and hover
* Filter/tags friendly layout
* Accessible focus states
* Responsive grid/list views
## Accessibility
### Keyboard Interactions
| Key | Description |
| -------- | -------------------------------------- |
| `Escape` | Closes the expanded job detail overlay |
### Screen Reader
* Job cards use layout animations via `layoutId` for smooth shared element transitions.
* The expanded detail view uses a click-outside handler to dismiss, and `Escape` key support for keyboard users.
* SVG logo icons (Resend, Turso, Supabase) include `title` elements for screen reader identification.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, layout animations and `layoutId` transitions are disabled, backdrop opacity animations are instant, and tap scale effects are removed.
## Props
# Magnetic Button (/docs/components/magnetic-button)
## Features
* Cursor-following magnetic effect
* Configurable strength and activation radius
* Spring physics for natural movement
* Respects reduced motion preferences
* Touch device detection (effect disabled on touch)
* Disabled state support
* Polymorphic with `asChild` prop
* GPU-accelerated transforms
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| --------------------- | ----------- | ---------------------------------------------------------------------------- |
| `role="presentation"` | Wrapper div | Indicates the outer mouse-tracking wrapper is decorative and not interactive |
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the magnetic cursor-following effect is completely disabled. The component also detects hover-capable devices and disables the effect on touch devices. When the `disabled` prop is set, the magnetic effect is also disabled.
## Props
## Usage
```tsx
import MagneticButton from "@/components/magnetic-button";
export default function Example() {
return (
Hover near me
);
}
```
## Examples
### Default
```tsx
Click me
```
### Custom Strength
Increase the magnetic pull strength (0-1):
```tsx
Strong magnetic effect
```
### Custom Radius
Adjust the activation radius in pixels:
```tsx
Large activation area
```
### Custom Spring Config
Fine-tune the spring animation:
```tsx
Bouncy
```
### As Child (Polymorphic)
Use with any component using `asChild`:
```tsx
Go to Docs
```
# Notification Badge (/docs/components/notification-badge)
## Features
* Three variants: dot (simple indicator), count (number badge), status (presence indicator)
* Animated count changes with scale pop effect
* Ping animation for attention-grabbing alerts
* Status colors for user presence: online (green), offline (gray), busy (red), away (yellow)
* Flexible positioning in all four corners
* Wraps any element as children
* Respects reduced motion preferences
* Accessible with proper ARIA attributes
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| -------------------- | --------- | --------------------------------------------------------------- |
| `aria-hidden="true"` | Ping span | Hides the decorative ping animation from assistive technologies |
### Screen Reader
* The badge count is rendered as plain text, readable by screen readers.
* The component wraps children in a `span` with `relative inline-flex` positioning, preserving the semantic meaning of the wrapped element.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, entrance/exit scale animations are disabled, the animated count transitions appear instantly, and the ping animation is not rendered.
## Props
# Number Flow (/docs/components/number-flow)
## Features
* Smooth counting animations
* Locale-aware formatting
* Easing and duration controls
* Accessible labels
## Accessibility
### Reduced Motion
This component does not currently support the `prefers-reduced-motion` media query. Digit slide animations will play regardless of user preference.
### ARIA
The increment and decrement buttons include descriptive `aria-label` attributes (`"Increase number"` and `"Decrease number"`). Buttons are properly disabled at the `min` and `max` boundaries via the `disabled` attribute.
### Keyboard
The controls use native `` elements with `type="button"`, so they are focusable and operable via keyboard (Enter and Space) without additional handling.
## Props
# Phototab (/docs/components/phototab)
## Features
* Tab and swipe transitions
* Lazy image loading friendly
* Keyboard and ARIA tabs
* Responsive grid
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| ------------ | ---------------- | ------------------------------------------- |
| `aria-label` | Tab list | Provides an accessible name "Phototab Tabs" |
| `aria-label` | Each tab trigger | Describes the tab name |
### Screen Reader
* Built on Radix UI's Tabs primitives, which provide built-in `role="tablist"`, `role="tab"`, and `role="tabpanel"` semantics with automatic `aria-selected` state management.
* Each tab trigger includes a visually hidden `` with the tab name.
* Tab images have `alt` attributes matching the tab name.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the hover background pill animation uses instant transitions instead of spring physics.
## Props
# Power Off Slide (/docs/components/power-off-slide)
## Features
* Drag to confirm interaction
* Spring physics and rubber-banding
* Accessible labels and states
* Theming options
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| --------------- | ----------- | ----------------------------------------------- |
| `aria-disabled` | Drag handle | Indicates when the slider is disabled |
| `tabIndex={0}` | Drag handle | Makes the handle focusable when not disabled |
| `tabIndex={-1}` | Drag handle | Removes the handle from tab order when disabled |
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the drag interaction is disabled (`drag={false}`), the shimmer text animation stops, and the handle uses instant transitions.
## Props
# Price Flow (/docs/components/price-flow)
## Features
* Smooth number swaps for pricing
* Works with monthly/annual toggles
* Locale formatting
* Accessible labeling
## Accessibility
### Reduced Motion
This component does not currently support the `prefers-reduced-motion` media query. Digit slide animations will play regardless of user preference.
## Props
# Reveal Text (/docs/components/reveal-text)
## Features
* Enter animations from up, down, left, or right
* Character or word stagger options
* Delay and duration controls
* Built with Framer Motion
* Accessible and performant
## Accessibility
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the text is displayed immediately at full opacity without any directional slide animation.
## Props
# Reviews Carousel (/docs/components/reviews-carousel)
## Features
* Smooth spring-based animations for card transitions
* Keyboard navigation (Arrow keys)
* Auto-play support with configurable interval
* Customizable indicators and navigation buttons
* Respects `prefers-reduced-motion` for accessibility
* Stack-based card layout with depth effect
* Blur and opacity effects for inactive cards
## Accessibility
### Keyboard Interactions
| Key | Description |
| ------------ | ------------------------------- |
| `ArrowLeft` | Navigate to the previous review |
| `ArrowRight` | Navigate to the next review |
### ARIA Attributes
| Attribute | Element | Purpose |
| --------------- | ------------------ | --------------------------------------------------------------- |
| `aria-label` | Navigation buttons | Labels for "Anterior" (previous) and "Siguiente" (next) buttons |
| `aria-label` | Indicator buttons | Labels each indicator with its position number |
| `type="button"` | All buttons | Prevents implicit form submission |
| `disabled` | Navigation buttons | Disables previous/next when at the bounds |
### Screen Reader
* Reviews use semantic `figure` and `figcaption` elements with `blockquote` for proper review structure.
* Navigation state is communicated through disabled button states.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, card scale, vertical offset, blur, and opacity transitions use instant `0ms` durations instead of spring animations.
## Props
# Rich Popover (/docs/components/rich-popover)
## Features
* Animated entrance and smart positioning
* Supports media, lists, actions
* Keyboard and screen reader support
* Theming and sizes
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| ------------------- | ---------------- | -------------------------------------------------- |
| `aria-label` | YouTube SVG icon | Provides an accessible name "YouTube" for the icon |
| `role="img"` | YouTube SVG icon | Identifies the SVG as an image |
| `focusable="false"` | YouTube SVG icon | Prevents the decorative icon from receiving focus |
### Screen Reader
* The popover is built on Radix UI's Popover primitives, which provide built-in focus management and ARIA dialog semantics.
* External links use `rel="noopener noreferrer"` and include an external link icon for visual indication.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the popover content appears and disappears instantly without scale, slide, or blur animations.
## Props
# Scramble Hover (/docs/components/scramble-hover)
## Features
* Character scrambling on hover with duration and speed controls
* Works with headings and inline text
* Smooth and performant
* Built with Framer Motion
* Accessible fallbacks
## Accessibility
### Reduced Motion
Respects the `prefers-reduced-motion` media query. When enabled, the scramble animation is fully disabled and the original text is always shown.
### Keyboard Support
The component renders as a `` element with `type="button"`, making it natively focusable and keyboard-accessible. The scramble effect triggers on focus and clears on blur.
### Hover Device Detection
Detects whether the user's device supports hover via the `(hover: hover) and (pointer: fine)` media query. On touch devices, hover-based animations are disabled to avoid stuck states.
## Props
# Scroll Reveal Paragraph (/docs/components/scroll-reveal-paragraph)
## Features
* Reveal on scroll with smooth fade and translate
* Per-paragraph stagger for long-form content
* Configurable thresholds and offsets
* Built with Framer Motion + Intersection Observer
* Accessible and performant
## Accessibility
### Screen Reader
* The component renders as a semantic `p` element, preserving paragraph structure for assistive technologies.
* Each word is wrapped in a `span` for animation purposes, but the text content remains fully readable.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, all words are displayed at full opacity immediately without the scroll-driven reveal effect. The ghost text overlay used for the reveal effect is also hidden.
## Props
# Scrollable Card Stack (/docs/components/scrollable-card-stack)
## Features
* Stacked, snap-scrolling layout
* Parallax and reveal motion
* Keyboard and touch friendly
* Theming options
## Accessibility
### Keyboard Interactions
| Key | Description |
| -------------------------- | ------------------------------ |
| `ArrowUp` / `ArrowLeft` | Navigates to the previous card |
| `ArrowDown` / `ArrowRight` | Navigates to the next card |
| `Home` | Jumps to the first card |
| `End` | Jumps to the last card |
### ARIA Attributes
| Attribute | Element | Purpose |
| -------------------- | ---------------- | -------------------------------------------------------------- |
| `aria-label` | Section | Provides an accessible name "Scrollable card stack" |
| `aria-live="polite"` | Section | Announces card changes to screen readers |
| `aria-atomic="true"` | Section | Ensures the full announcement is read together |
| `role="application"` | Container | Indicates custom keyboard interaction model |
| `role="tablist"` | Navigation dots | Groups the dot indicators as a tab list |
| `role="tab"` | Each dot | Identifies each dot as a tab control |
| `aria-selected` | Each dot | Indicates the active card indicator |
| `aria-label` | Each dot | Describes position (e.g., "Go to card 1 of 5") |
| `aria-hidden` | Non-active cards | Hides stacked cards behind the current one from screen readers |
| `aria-label` | Profile link | Describes the link action (e.g., "View John's profile") |
### Screen Reader
* A live region announces the current card position (e.g., "Card 1 of 5 selected. Use arrow keys to navigate one card at a time, or click the dots below.").
* Non-active cards are marked `aria-hidden` so only the visible card is announced.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, card scale/translate animations, blur effects, and hover scale transitions are all disabled.
## Props
# Scrubber (/docs/components/scrubber)
## Features
* Dark, self-contained slider with built-in label and value display
* Animated thumb that appears on hover and drag with spring physics
* Subtle scrub indicator for position feedback at rest
* Configurable tick marks for visual reference
* Keyboard accessible (arrow keys, Home, End)
* Supports controlled and uncontrolled usage
* Respects `prefers-reduced-motion`
## Accessibility
### Keyboard Interactions
| Key | Description |
| ------------------------- | ------------------------------- |
| `ArrowRight` / `ArrowUp` | Increases the value by one step |
| `ArrowLeft` / `ArrowDown` | Decreases the value by one step |
| `Home` | Sets the value to the minimum |
| `End` | Sets the value to the maximum |
### ARIA Attributes
| Attribute | Element | Purpose |
| --------------- | ------------- | --------------------------------------------------- |
| `role="slider"` | Track element | Identifies the component as a slider |
| `aria-label` | Track element | Provides an accessible name (uses the `label` prop) |
| `aria-valuemin` | Track element | Communicates the minimum value |
| `aria-valuemax` | Track element | Communicates the maximum value |
| `aria-valuenow` | Track element | Communicates the current value |
| `tabIndex={0}` | Track element | Makes the slider focusable |
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the thumb capsule animation uses instant transitions instead of spring physics.
## Props
# Searchable Dropdown (/docs/components/searchable-dropdown)
## Features
* Real-time search filtering with smooth transitions
* Support for item descriptions and icons
* Keyboard navigation (Escape to close)
* Animated clear button for search input
* Staggered animation for filtered results
* Empty state message when no results found
* Click outside to close
* Fully accessible and customizable
## Accessibility
### Keyboard Interactions
| Key | Description |
| ----------------- | ---------------------------------------------------------------------------------- |
| `Enter` / `Space` | Opens the dropdown when the trigger button is focused, or selects the focused item |
| `Escape` | Closes the dropdown, clears the search, and returns focus to the trigger button |
| `Arrow Down` | Moves focus to the next filtered item (wraps to first) |
| `Arrow Up` | Moves focus to the previous filtered item (wraps to last) |
| `Home` | Moves focus to the first filtered item |
| `End` | Moves focus to the last filtered item |
### ARIA Attributes
| Attribute | Element | Purpose |
| --------------------------- | -------------- | ------------------------------------------------------------- |
| `aria-expanded` | Trigger button | Indicates whether the dropdown is open or closed |
| `aria-haspopup="listbox"` | Trigger button | Indicates the button opens a listbox |
| `aria-label` | Trigger button | Provides an accessible name including the selected item label |
| `role="combobox"` | Search input | Identifies the search field as a combobox |
| `aria-autocomplete="list"` | Search input | Indicates the input provides list-based autocomplete |
| `aria-controls` | Search input | References the `id` of the dropdown items list |
| `aria-expanded` | Search input | Indicates the dropdown state within the combobox |
| `aria-label` | Search input | Provides an accessible name for the search field |
| `role="option"` | List item | Identifies each item as a selectable option |
| `aria-selected` | List item | Indicates the currently selected or focused item |
| `aria-label="Clear search"` | Clear button | Provides an accessible name for the search clear action |
### Screen Reader
* The trigger button announces its expanded/collapsed state and the currently selected value
* Each option announces its label, optional description, and selected state
* A checkmark icon includes a `` element for screen readers
* The clear search button is announced with its label
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, dropdown open/close, search field slide-in, item stagger, and chevron rotation animations are disabled instantly.
## Props
## Item Props
Each item in the dropdown can have the following properties:
| Property | Type | Description |
| ------------- | ------------------ | ----------------------------------------- |
| `id` | `string \| number` | Unique identifier for the item |
| `label` | `string` | Display text for the item |
| `icon` | `React.ReactNode` | Optional icon to display before the label |
| `description` | `string` | Optional description text below the label |
# Siri Orb (/docs/components/siri-orb)
## Features
* Animated waves and glow
* Reacts to input levels
* Smooth looping
* Easy to style
## Accessibility
### Reduced Motion
Respects the `prefers-reduced-motion` media query via a CSS `@media` rule. When enabled, the orb's rotation animation is paused and displays a static gradient. This is a purely decorative component with no interactive or semantic content.
## Props
# Skeleton (/docs/components/skeleton-loader)
## Features
* **Auto-adaptive**: Wrap any component and skeleton matches its exact shape
* Uses Tailwind's `animate-pulse` for smooth animation
* Toggle between loading/loaded states with `loading` prop
* Works with any component structure
* Accessible with ARIA attributes
## Accessibility
### ARIA Attributes
* Sets `aria-busy="true"` on the container while loading, signaling to assistive technologies that content is being loaded.
* Uses `aria-live="polite"` so screen readers announce when content finishes loading.
* Marks the pulse overlay with `aria-hidden="true"` to hide decorative elements from the accessibility tree.
### Reduced Motion
Uses Tailwind's `animate-pulse` for the shimmer effect. Tailwind respects the `prefers-reduced-motion` media query at the CSS level, but the component does not include an explicit JavaScript-level `useReducedMotion` check.
## Props
## Usage
### Wrap your UI
Simply wrap your component - the skeleton will match its exact dimensions:
```tsx
```
### Basic blocks
Use without children for simple skeleton shapes:
```tsx
```
# Social Selector (/docs/components/social-selector)
## Features
* Animated select/deselect
* Icon and label variants
* Keyboard and screen reader support
* Responsive layout
## Accessibility
### ARIA Attributes
| Attribute | Element | Purpose |
| ------------ | ---------------- | ------------------------------------------------ |
| `aria-label` | Platform buttons | Describes the action (e.g., "Select X platform") |
### Screen Reader
* Each platform icon button contains a visually hidden `` with the platform name.
* SVG icons include `` elements (e.g., "X icon", "Bluesky icon", "Threads icon").
* External links use `rel="noopener noreferrer"` for security.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, the background pill sliding animation and the domain text blur/slide transitions are disabled.
## Props
# Switchboard Card (/docs/components/switchboard-card)
## Features
* Animated light grid with configurable dimensions
* Three light states: off, medium, high (all use brand color)
* Word pattern support - automatically generates patterns for short words (e.g., "NEXT", "REACT", "VUE")
* Custom pattern support via array of light indices
* Random light generation for dynamic effects
* Next.js-style variant with dark gradient background
* Smooth transitions and animations
* Fully customizable grid layout
## Accessibility
### ARIA Attributes
* When rendered as a link (`href` prop) or button (`onButtonClick` prop), the wrapper includes `aria-label` set to the card's `title` for screen reader context.
* Focus-visible ring styles (`focus-visible:ring-2`) provide clear keyboard focus indication on interactive card variants.
### Reduced Motion
The light grid uses CSS transitions (`opacity` and `transform`) with configurable `transitionDuration`. There is no explicit `prefers-reduced-motion` check at the JavaScript or CSS level. The animations are subtle opacity/scale transitions rather than layout-shifting motion.
## Props
## Usage
### Basic Usage
```tsx
import SwitchboardCard from "@repo/smoothui/components/switchboard-card";
```
### With Grid Pattern (Simplest)
The easiest way to customize is to pass a grid pattern directly. You can use either:
* A 2D array `[rows][columns]` where each cell is `0` (off) or `1` (high)
* A flat array of length `columns * rows` where each element is `0` (off) or `1` (high)
```tsx
// Using 2D array [5 rows][18 columns]
const nextPattern = [
[1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1], // Row 0
[1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0], // Row 1
[1,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,1,0], // Row 2
[1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0], // Row 3
[1,1,1,1,1,0,0,1,1,1,0,0,0,1,1,1,1,1], // Row 4
];
// Or using flat array
const flatPattern = Array(90).fill(0).map((_, i) => {
// Your logic to determine which lights should be on
return i % 10 === 0 ? 1 : 0;
});
```
### With Word Pattern
The easiest way to display a word is to use the `word` prop. The component will automatically generate the pattern:
```tsx
```
Supported letters: N, E, X, T, R, A, C, V, U (more can be added)
### With Custom Pattern
For full control, you can define custom light patterns by passing an array of light indices:
```tsx
const customPattern = [15, 19, 26, 29, 34, 55, 60, 70, 74, 83];
```
### Random Lights
For a dynamic effect, use `randomLights`:
```tsx
```
### Interactive Card
Make the card clickable with `href` or `onButtonClick`:
```tsx
```
# Tweet Card (/docs/components/tweet-card)
## Features
* Beautiful tweet card design with full-width images
* Hover-activated external link button positioned at bottom-right
* Support for tweets with images, videos, and text
* Server-side and client-side rendering options
* Loading and error states included
* Fully customizable styling
* Configurable user info position (top or bottom)
* Customizable avatar rounded style
* Accessible with proper ARIA labels
* Consistent 24px padding on all sides
## Accessibility
### Semantic HTML
Uses `` as the root element and `` for tweet text, providing meaningful document structure for assistive technologies.
### ARIA Attributes
* The external link button includes `aria-label="Open tweet in new tab"` for screen reader context.
* Decorative SVG icons use `aria-hidden="true"` to hide them from the accessibility tree.
* All external links include `rel="noreferrer"` or `rel="noopener noreferrer"` for security.
### Reduced Motion
The hover-activated link button uses Tailwind's `motion-safe:hover:scale-110` utility, which automatically disables the scale animation when `prefers-reduced-motion` is enabled. There is no additional JavaScript-level `useReducedMotion` check, as animations are CSS-only.
## Props
## Usage
### Client-Side (Recommended)
Use `ClientTweetCard` for client-side rendering:
```tsx
import { ClientTweetCard, TweetSkeleton } from "@repo/smoothui/components";
export function MyComponent() {
return (
}
/>
);
}
```
### Server-Side
Use `TweetCard` for server-side rendering:
```tsx
import { TweetCard } from "@repo/smoothui/components";
export default async function MyPage() {
return (
);
}
```
## Configuration Options
### User Info Position
Control where the user information (avatar and name) appears in the card:
```tsx
// User info at bottom (default)
}
/>
// User info at top
}
/>
```
### Avatar Rounded Style
Customize the avatar's border radius:
```tsx
// Rounded corners (default)
}
/>
// Fully rounded (circle)
}
/>
// Large rounded corners
}
/>
// Medium rounded corners
}
/>
```
### Combined Configuration
Use both props together for different layouts:
```tsx
// Top position with circular avatar
}
/>
// Bottom position with large rounded corners
}
/>
```
## Examples
### Default Configuration
Default layout with user info at bottom and rounded avatar:
```tsx
}
/>
```
### User Info at Top
Display user information at the top of the card:
```tsx
}
/>
```
### Circular Avatar
Use a fully rounded (circular) avatar:
```tsx
}
/>
```
### Top Position with Circular Avatar
Combine top position with circular avatar:
```tsx
}
/>
```
## Grid Layout
Display multiple tweets in a grid with different configurations:
```tsx
import { ClientTweetCard, TweetSkeleton } from "@repo/smoothui/components";
export function TweetGrid() {
return (
}
/>
}
/>
}
/>
);
}
```
# Typewriter Text (/docs/components/typewriter-text)
## Features
* Progressive character reveal with configurable speed
* Optional looping with pauses
* Works with headings, paragraphs, and inline text
* Built with Framer Motion
* Accessible and performant
## Accessibility
### Reduced Motion
Respects the `prefers-reduced-motion` media query via a custom `useReducedMotion` hook. When enabled, the full text is displayed immediately without the character-by-character reveal animation.
## Props
# User Account Avatar (/docs/components/user-account-avatar)
## Features
* Image with fallback support
* Multiple sizes
* Customizable fallback text
* Group avatars support
* Status indicators
* Built on Radix UI Avatar
## Accessibility
### Keyboard Interactions
| Key | Description |
| ----------------- | --------------------------------------------------- |
| `Enter` / `Space` | Toggle the "Edit Profile" or "Last Orders" sections |
### ARIA Attributes
| Attribute | Element | Purpose |
| ------------------------- | ----------------- | ------------------------------------------------- |
| `aria-label="View Order"` | View order button | Labels the order detail button for screen readers |
| `htmlFor` | Form labels | Associates labels with their form inputs |
| `type="button"` | All buttons | Prevents implicit form submission |
| `type="submit"` | Save button | Submits the profile edit form |
### Screen Reader
* The popover is built on Radix UI Popover, which manages focus trapping, return focus, and escape-to-close automatically.
* Form inputs include proper `label` elements linked via `htmlFor`/`id` attributes.
* Section toggle buttons include keyboard event handlers (`onKeyDown`) for `Enter` and `Space` keys.
* The avatar image includes an `alt="User Avatar"` attribute.
### Reduced Motion
This component respects the `prefers-reduced-motion` media query via `useReducedMotion` from Motion. When reduced motion is preferred, section expand/collapse animations, progress bar animations, and height transitions are all disabled instantly.
## Props
# Wave Text (/docs/components/wave-text)
## Features
* Smooth continuous wave animation across text
* Customizable amplitude, duration, and stagger delay
* Natural bounce effect with custom easing
* GPU-accelerated for optimal performance
* Lightweight and easy to use
* Built with Motion
## Accessibility
### Reduced Motion
Uses `useReducedMotion` from `motion/react`. When enabled, the wave animation is fully disabled — characters render statically with no vertical movement and `duration: 0` transitions.
## Props
## Usage
```tsx
import WaveText from "@repo/smoothui/components/wave-text";
// Basic usage
Hello World
// Customized wave
SmoothUI Components
```
## Examples
### Large Bold Text
```tsx
SmoothUI Components
```
### Subtle Wave
```tsx
Smooth wave animations for your text!
```
# AI Integration (/docs/guides/ai-integration)
# AI Integration
SmoothUI is designed from the ground up for AI-assisted development. Whether you are an AI agent selecting components programmatically, or a developer using AI coding tools, SmoothUI provides first-class integration paths.
## Quick Start
Choose the integration that fits your workflow:
| You are... | What to use | Setup |
| --------------------------------------------- | ------------------------ | ------------------------------------------ |
| **AI coding agent** (Claude, Cursor, Copilot) | MCP Server | `npx shadcn@latest mcp init` |
| **Building custom tooling** | REST API | Call `https://smoothui.dev/api/v1/...` |
| **LLM / RAG pipeline** | Machine-readable catalog | Fetch `https://smoothui.dev/llms-full.txt` |
## For AI Agents
AI agents can discover, search, and install SmoothUI components through multiple channels.
### MCP Server (Recommended)
The fastest way to give an AI assistant full access to SmoothUI. The shadcn MCP server works out of the box with SmoothUI's registry.
```shell title="Terminal"
npx shadcn@latest mcp init --client claude
```
```shell title="Terminal"
npx shadcn@latest mcp init --client cursor
```
```shell title="Terminal"
npx shadcn@latest mcp init --client vscode
```
Once configured, your AI assistant can discover, search, and install any SmoothUI component. See the full [MCP Server guide](/docs/guides/mcp) for detailed setup and example prompts.
### REST API
For agents that need structured JSON data, SmoothUI provides a public REST API with no authentication required.
```bash title="Discover components"
curl "https://smoothui.dev/api/v1/suggest?need=animated+tab+navigation"
```
```bash title="Get component source"
curl "https://smoothui.dev/api/v1/components/animated-tabs?include=source"
```
**Key endpoints:**
| Endpoint | Purpose |
| ------------------------------------- | -------------------------------------- |
| `GET /api/v1/components` | List all components with filtering |
| `GET /api/v1/components/{name}` | Get component details and source |
| `GET /api/v1/components/search?q=...` | Keyword search |
| `GET /api/v1/suggest?need=...` | Natural-language component suggestions |
| `GET /api/v1/blocks` | List pre-built page sections |
| `GET /openapi.json` | Full OpenAPI 3.1 specification |
See the full [REST API reference](/docs/guides/api) for query parameters, response schemas, and error handling.
### Machine-Readable Catalog
For LLM context windows and RAG pipelines, SmoothUI provides machine-readable component catalogs following the `llms.txt` convention:
| URL | Description |
| -------------------------------------------------------------------- | -------------------------------------------------- |
| [`/llms.txt`](https://smoothui.dev/llms.txt) | Compact overview of all components |
| [`/llms-full.txt`](https://smoothui.dev/llms-full.txt) | Full catalog with metadata, props, and usage hints |
| [`/llms-components.json`](https://smoothui.dev/llms-components.json) | Structured JSON catalog for programmatic use |
## Agent Workflow
Here is the recommended workflow for AI agents integrating SmoothUI components:
1. **Discover** — Use `/api/v1/suggest?need=...` or the MCP server to find relevant components based on what the user needs.
2. **Inspect** — Fetch full metadata and source with `/api/v1/components/{name}?include=source` to understand props and usage.
3. **Install** — Use the `installCommand` from the metadata: `npx shadcn@latest add @smoothui/{name}`.
4. **Integrate** — Use `compositionHints` and the source code to wire the component into the project correctly.
## Learn More
Full MCP setup guide with example prompts for Claude, Cursor, and VS Code.
Complete API reference with endpoints, parameters, and response schemas.
Get started with SmoothUI in your project.
# Animated React Components (/docs/guides/animated-components)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Motion & GSAP-Powered React Components
SmoothUI provides a comprehensive collection of **animated React components** powered by [Motion](https://motion.dev) (formerly Framer Motion) and [GSAP](https://gsap.com). Every component is designed with smooth, performant animations that enhance user experience without sacrificing accessibility.
## Animation Categories
### Text Animations
Create engaging text effects that capture attention:
| Component | Animation Type | Use Case |
| --------------------------------------------------- | --------------------- | ---------------------------- |
| [Typewriter Text](/docs/components/typewriter-text) | Character reveal | Hero sections, AI interfaces |
| [Scramble Hover](/docs/components/scramble-hover) | Matrix-style scramble | Navigation, headings |
| [Wave Text](/docs/components/wave-text) | Wave motion | Decorative headings |
| [Reveal Text](/docs/components/reveal-text) | Directional reveal | Page transitions |
### Interactive Cards
Cards with smooth expand, hover, and transition animations:
| Component | Animation Type | Use Case |
| --------------------------------------------------------------- | --------------------- | ---------------------- |
| [Expandable Cards](/docs/components/expandable-cards) | Layout animation | Portfolios, galleries |
| [Glow Hover Card](/docs/components/glow-hover-card) | Cursor-following glow | Feature highlights |
| [Scrollable Card Stack](/docs/components/scrollable-card-stack) | Parallax reveal | Testimonials, profiles |
### Button Animations
Buttons with satisfying micro-interactions:
| Component | Animation Type | Use Case |
| ----------------------------------------------------------- | ----------------- | ---------------- |
| [Magnetic Button](/docs/components/magnetic-button) | Cursor attraction | CTAs, navigation |
| [Button Copy](/docs/components/button-copy) | Success feedback | Code snippets |
| [Clip Corners Button](/docs/components/clip-corners-button) | Corner animation | Unique CTAs |
### Form Inputs
Inputs with smooth focus and validation animations:
| Component | Animation Type | Use Case |
| --------------------------------------------------------- | --------------- | ------------------ |
| [Animated Input](/docs/components/animated-input) | Floating label | Forms, search |
| [Animated OTP Input](/docs/components/animated-otp-input) | Digit animation | Verification flows |
### Notification Components
Animated feedback and notification elements:
| Component | Animation Type | Use Case |
| ------------------------------------------------- | --------------- | --------------------- |
| [Dynamic Island](/docs/components/dynamic-island) | Expand/collapse | Notifications, status |
| [Basic Toast](/docs/components/basic-toast) | Slide animation | Alerts, confirmations |
## Why Motion & GSAP?
### Performance Optimized
All animations use GPU-accelerated transforms (`transform` and `opacity`) for smooth 60fps performance. No layout thrashing or paint operations.
### Accessibility First
Every component respects the `prefers-reduced-motion` media query. Users who are sensitive to motion see instant transitions instead of animations.
```tsx
// Built into every component
const shouldReduceMotion = useReducedMotion();
```
### Natural Feel
Animations use spring physics with carefully tuned parameters for natural, fluid motion that feels right:
```tsx
transition={{
type: "spring",
duration: 0.25,
bounce: 0.1
}}
```
## Getting Started
Install any animated component using the shadcn CLI:
```bash
npx shadcn@latest add @smoothui/expandable-cards
```
Or browse all components:
Explore the complete collection of 50+ animated components.
Set up SmoothUI in your React or Next.js project.
## Frequently Asked Questions
No! All animations are built-in and work out of the box. You can use components without writing any animation code. For customization, basic Motion or GSAP knowledge helps but isn't required.
Motion and GSAP are the animation dependencies. Components are tree-shakeable, so you only ship the code you use. Most components use Motion, while some use GSAP for advanced effects like the Gooey Popover.
Yes! All components are designed for Next.js App Router and work with Server Components. Client-side animations hydrate smoothly without layout shift.
Absolutely. Since you own the code (copied into your project), you can modify any animation parameters - duration, easing, spring physics, and more.
# Animation Best Practices (/docs/guides/animation-best-practices)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Why Animation Matters
Animation isn't just decoration—it's a fundamental part of user experience. Well-crafted animations:
* **Guide attention** to important elements and changes
* **Provide feedback** that actions were registered
* **Create continuity** between UI states
* **Reduce cognitive load** by showing relationships between elements
* **Delight users** with polished, professional interactions
Studies show that appropriate animation can increase user engagement by up to 400% and significantly improve perceived performance, even when actual load times remain the same.
Every SmoothUI component follows these principles. Animations are fast (200-300ms), purposeful, and always respect user preferences for reduced motion.
***
## Core Animation Principles
### Duration & Timing
The most common mistake is making animations too slow. Users perceive interfaces as sluggish when animations exceed 300-400ms.
| Animation Type | Recommended Duration |
| ------------------------------------- | -------------------- |
| Micro-interactions (hover, focus) | 100-200ms |
| Standard transitions (show/hide) | 200-300ms |
| Complex animations (page transitions) | 300-400ms |
| Decorative/ambient | Up to 1000ms |
```tsx
// Good: Fast, snappy interaction
transition={{ duration: 0.2 }}
// Bad: Feels sluggish
transition={{ duration: 0.8 }}
```
### Easing Functions
Easing determines how an animation accelerates and decelerates. The right easing makes motion feel natural.
| Easing | Use Case | CSS/Motion Value |
| --------------- | ---------------------- | -------------------------------------- |
| **ease-out** | Elements entering | `cubic-bezier(0.23, 1, 0.32, 1)` |
| **ease-in-out** | Elements moving | `cubic-bezier(0.645, 0.045, 0.355, 1)` |
| **ease** | Hover/color changes | `ease` (built-in) |
| **spring** | Natural, bouncy motion | `type: "spring"` |
```tsx
// Natural spring animation (recommended for most cases)
transition={{
type: "spring",
duration: 0.25,
bounce: 0.1 // Keep low for UI (0.1-0.2)
}}
// Cubic bezier for precise control
transition={{
duration: 0.2,
ease: [0.23, 1, 0.32, 1] // ease-out
}}
```
Never use `ease-in` for UI animations—it starts slow and feels unresponsive. Users expect immediate feedback when they interact.
### Transform vs Layout Properties
This is critical for performance. Only animate **transform** and **opacity**—these are GPU-accelerated and don't trigger layout recalculations.
```tsx
// GOOD: GPU-accelerated, smooth 60fps
animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
// BAD: Triggers layout, causes jank
animate={{ width: 200, height: 100, marginLeft: 20 }}
```
| Property | Performance | Use Instead |
| -------------------------------- | ----------- | ------------------------------ |
| `width`, `height` | Poor | `scale` or `scaleX`/`scaleY` |
| `top`, `left`, `right`, `bottom` | Poor | `x`, `y` (transform) |
| `margin`, `padding` | Poor | `x`, `y` with fixed dimensions |
| `opacity` | Excellent | Use freely |
| `transform` | Excellent | Use freely |
***
## Motion Library Essentials
SmoothUI uses [Motion](https://motion.dev) (formerly Framer Motion) for animations. Here are the key concepts.
### Spring Physics
Spring animations feel more natural than duration-based animations because they simulate real-world physics.
```tsx
import { motion } from "motion/react";
```
**Simplified spring syntax** (recommended):
```tsx
transition={{
type: "spring",
duration: 0.25, // Approximate duration
bounce: 0.1 // 0 = no bounce, 1 = very bouncy
}}
```
### Variants
Variants let you define animation states and orchestrate complex animations:
```tsx
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1 // Animate children sequentially
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
};
{items.map(item => (
{item.name}
))}
```
### Layout Animations
Motion's `layout` prop automatically animates layout changes:
```tsx
// Automatically animates position/size changes
{isExpanded ? : }
// Smooth shared element transitions
{/* This element animates between positions */}
```
### AnimatePresence
For enter/exit animations, wrap components in `AnimatePresence`:
```tsx
import { AnimatePresence, motion } from "motion/react";
{isVisible && (
Content that animates in and out
)}
```
***
## Performance Optimization
### GPU-Accelerated Properties
Always prefer these properties for smooth 60fps animations:
```tsx
// These run on the GPU (compositor thread)
transform: translateX(), translateY(), scale(), rotate()
opacity
// These trigger layout/paint (main thread) - AVOID
width, height, top, left, margin, padding, border
```
### Avoiding Layout Thrash
Layout thrash occurs when you read and write to the DOM in quick succession:
```tsx
// BAD: Forces synchronous layout
elements.forEach(el => {
const height = el.offsetHeight; // READ
el.style.height = height + 10; // WRITE
});
// GOOD: Batch reads, then writes
const heights = elements.map(el => el.offsetHeight); // All READs
elements.forEach((el, i) => {
el.style.height = heights[i] + 10; // All WRITEs
});
```
### will-change Hint
Use sparingly to hint browser optimization:
```css
.animated-element {
will-change: transform, opacity;
}
```
Only apply `will-change` to elements that will actually animate. Overuse consumes memory and can hurt performance.
### When to Use CSS vs JavaScript Animations
| Use CSS | Use JavaScript (Motion) |
| -------------------------- | -------------------------------- |
| Simple hover effects | Complex choreographed animations |
| State transitions | Physics-based motion |
| Keyframe animations | Gesture-driven animations |
| Performance-critical loops | Dynamic, data-driven animations |
***
## Accessibility Guidelines
### Respecting Reduced Motion
Always check `prefers-reduced-motion`. Users enable this for medical reasons (vestibular disorders, motion sickness) or personal preference.
```tsx
import { useReducedMotion } from "motion/react";
function AnimatedComponent() {
const shouldReduceMotion = useReducedMotion();
return (
);
}
```
Every SmoothUI component includes `useReducedMotion` support. Animations are automatically disabled or minimized for users who prefer reduced motion.
### Motion Sensitivity Guidelines
Even for users without reduced motion enabled:
* **Avoid large-scale motion** (full-screen transitions, parallax)
* **Limit simultaneous animations** (no more than 2-3 elements animating at once)
* **Keep animations brief** (under 300ms for most interactions)
* **Avoid infinite loops** (or provide controls to pause)
### Focus Management
When animating elements that affect focus:
```tsx
// Ensure focus moves appropriately after animation
{
if (isOpen) {
firstFocusableElement.current?.focus();
}
}}
>
```
***
## Common Patterns
### Enter/Exit Animations
```tsx
// Fade + slide up (most common)
const fadeSlideUp = {
initial: { opacity: 0, y: 10 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -10 },
transition: { duration: 0.2 }
};
// Scale + fade (for modals, popovers)
const scaleFade = {
initial: { opacity: 0, scale: 0.95 },
animate: { opacity: 1, scale: 1 },
exit: { opacity: 0, scale: 0.95 },
transition: { type: "spring", duration: 0.25, bounce: 0.1 }
};
```
### Hover Effects
```tsx
Click me
```
### Staggered Lists
```tsx
{items.map(item => (
{item.name}
))}
```
### Scroll-Triggered Animations
```tsx
import { useInView, motion } from "motion/react";
function ScrollReveal({ children }) {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: "-100px" });
return (
{children}
);
}
```
***
## Anti-Patterns to Avoid
### Overanimating
Not everything needs to animate. Too much motion is distracting and exhausting.
```tsx
// BAD: Everything bounces and wiggles
// GOOD: Purposeful, minimal animation
```
### Slow Animations
Animations over 300ms feel sluggish. Users shouldn't wait for your UI.
```tsx
// BAD: Too slow
transition={{ duration: 0.8 }}
// GOOD: Snappy
transition={{ duration: 0.2 }}
```
### Competing Animations
Multiple elements animating simultaneously creates visual chaos.
```tsx
// BAD: Everything animates at once
{items.map(item => (
))}
// GOOD: Stagger or animate one at a time
variants={{ visible: { transition: { staggerChildren: 0.05 } } }}
```
### Animation Without Purpose
Every animation should serve a function: feedback, guidance, or continuity.
```tsx
// BAD: Spinning logo for no reason
// GOOD: Spinner indicates loading state
{isLoading && }
```
### Ignoring Reduced Motion
Always implement reduced motion support. It's an accessibility requirement.
```tsx
// BAD: No reduced motion check
animate={{ x: 100, rotate: 360 }}
// GOOD: Respects user preference
animate={shouldReduceMotion ? { opacity: 1 } : { x: 100, rotate: 360 }}
```
***
## Quick Reference
### Recommended Defaults
```tsx
// Standard UI animation
transition={{
type: "spring",
duration: 0.25,
bounce: 0.1
}}
// Hover/tap feedback
transition={{
type: "spring",
stiffness: 400,
damping: 17
}}
// Enter/exit
transition={{ duration: 0.2, ease: [0.23, 1, 0.32, 1] }}
```
### Checklist for New Animations
* [ ] Duration under 300ms for interactions
* [ ] Only animating transform/opacity
* [ ] `useReducedMotion` implemented
* [ ] Purposeful (not decorative)
* [ ] Tested on low-end devices
***
## Further Reading
Browse all 50+ animated components in SmoothUI.
Learn about SmoothUI's design philosophy and patterns.
Official Motion (Framer Motion) documentation.
# REST API (/docs/guides/api)
# REST API
SmoothUI exposes a public REST API that lets you discover, search, and retrieve component metadata and source code programmatically. The API is designed for both human developers building tooling and AI agents that need structured component data.
## Overview
**Base URL:** `https://smoothui.dev/api/v1`
**Authentication:** None required. The API is fully public.
**CORS:** All endpoints include `Access-Control-Allow-Origin: *` headers, so you can call them from any origin.
**Format:** All responses are JSON. Errors follow a consistent `{"error": "...", "status": 400}` envelope.
**OpenAPI Spec:** A full OpenAPI 3.1 specification is available at [`/openapi.json`](https://smoothui.dev/openapi.json).
## Endpoints
### List Components
Returns a paginated list of all SmoothUI components. Supports filtering by category, complexity, animation type, and tag.
```bash title="Request"
curl "https://smoothui.dev/api/v1/components?category=navigation&pageSize=10"
```
**Query parameters:**
| Parameter | Type | Default | Description |
| --------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `category` | string | - | Filter by category: `basic-ui`, `button`, `text`, `ai`, `layout`, `feedback`, `data-display`, `navigation`, `other` |
| `complexity` | string | - | Filter by complexity: `simple`, `moderate`, `complex` |
| `animationType` | string | - | Filter by animation: `spring`, `tween`, `gesture`, `scroll`, `none` |
| `tag` | string | - | Filter by tag (case-insensitive exact match) |
| `page` | integer | `1` | Page number (1-based) |
| `pageSize` | integer | `50` | Items per page (max 100) |
```json title="Response"
{
"data": [
{
"name": "animated-tabs",
"displayName": "AnimatedTabs",
"description": "Tab navigation with smooth spring-based transitions.",
"category": "navigation",
"tags": ["animation", "tabs", "navigation"],
"useCases": ["Tab navigation with smooth transitions"],
"compositionHints": ["Combine with animated-tooltip for rich tab headers"],
"complexity": "moderate",
"animationType": "spring",
"dependencies": ["motion"],
"registryDependencies": [],
"hasReducedMotion": true,
"propsCount": 5,
"installCommand": "npx shadcn@latest add @smoothui/animated-tabs",
"docUrl": "https://smoothui.dev/docs/components/animated-tabs",
"registryUrl": "https://smoothui.dev/r/animated-tabs.json"
}
],
"total": 84,
"page": 1,
"pageSize": 10,
"totalPages": 9
}
```
### Get Component Details
Returns full metadata for a single component. Add `?include=source` to also retrieve the raw source code.
```bash title="Request"
curl "https://smoothui.dev/api/v1/components/animated-tabs"
```
```bash title="Request (with source code)"
curl "https://smoothui.dev/api/v1/components/animated-tabs?include=source"
```
```json title="Response"
{
"component": {
"name": "animated-tabs",
"displayName": "AnimatedTabs",
"description": "Tab navigation with smooth spring-based transitions.",
"category": "navigation",
"tags": ["animation", "tabs", "navigation"],
"complexity": "moderate",
"animationType": "spring",
"installCommand": "npx shadcn@latest add @smoothui/animated-tabs",
"docUrl": "https://smoothui.dev/docs/components/animated-tabs",
"registryUrl": "https://smoothui.dev/r/animated-tabs.json"
},
"source": "// --- index.tsx ---\n\"use client\";\nimport { motion } from ..."
}
```
Returns `404` if the component name does not exist.
### Search Components
Keyword-based relevance search across component names, descriptions, tags, use cases, and categories. At least one of `q`, `category`, or `tags` is required.
```bash title="Request"
curl "https://smoothui.dev/api/v1/components/search?q=modal+dialog"
```
**Query parameters:**
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | ---------------------------------------------- |
| `q` | string | No\* | Search query |
| `category` | string | No\* | Pre-filter by category |
| `tags` | string | No\* | Comma-separated required tags (all must match) |
\*At least one parameter is required.
```json title="Response"
{
"data": [
{
"name": "basic-modal",
"displayName": "BasicModal",
"description": "Accessible modal dialog with smooth enter/exit animations.",
"category": "feedback",
"tags": ["modal", "dialog", "overlay"],
"relevanceScore": 12
}
],
"total": 3,
"query": "modal dialog",
"filters": {}
}
```
Results are ranked by relevance score (higher is better). Exact name matches score highest, followed by tag matches, use case matches, description matches, and category matches.
### List Blocks
Returns a paginated list of pre-built page sections (blocks). Supports filtering by block type and tag.
```bash title="Request"
curl "https://smoothui.dev/api/v1/blocks?blockType=hero"
```
**Query parameters:**
| Parameter | Type | Default | Description |
| ----------- | ------- | ------- | ------------------------------------------------------------------------------------------------- |
| `blockType` | string | - | Filter by type: `hero`, `features`, `pricing`, `testimonials`, `cta`, `footer`, `header`, `other` |
| `tag` | string | - | Filter by tag (case-insensitive exact match) |
| `page` | integer | `1` | Page number |
| `pageSize` | integer | `50` | Items per page (max 100) |
```json title="Response"
{
"data": [
{
"name": "hero-section",
"displayName": "HeroSection",
"description": "Animated hero section with gradient text and CTA buttons.",
"blockType": "hero",
"components": ["animated-gradient-text", "magnetic-button"],
"category": "layout",
"tags": ["hero", "landing-page"],
"complexity": "moderate",
"animationType": "spring",
"installCommand": "npx shadcn@latest add @smoothui/hero-section"
}
],
"total": 5,
"page": 1,
"pageSize": 50,
"totalPages": 1
}
```
### Get Block Details
Returns full metadata for a single block. Add `?include=source` for raw source code.
```bash title="Request"
curl "https://smoothui.dev/api/v1/blocks/hero-section?include=source"
```
Returns `404` if the block name does not exist.
### Suggest Components
Given a natural-language description of what you need, returns the top 10 most relevant components and blocks. This endpoint is particularly useful for AI agents selecting components based on a user's request.
```bash title="Request"
curl "https://smoothui.dev/api/v1/suggest?need=animated+tab+navigation"
```
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | --------------------------------------------- |
| `need` | string | Yes | Natural-language description of what you need |
```json title="Response"
{
"need": "animated tab navigation",
"suggestions": [
{
"type": "component",
"name": "animated-tabs",
"displayName": "AnimatedTabs",
"description": "Tab navigation with smooth spring-based transitions.",
"category": "navigation",
"relevanceScore": 21,
"installCommand": "npx shadcn@latest add @smoothui/animated-tabs",
"docUrl": "https://smoothui.dev/docs/components/animated-tabs",
"registryUrl": "https://smoothui.dev/r/animated-tabs.json"
}
],
"total": 1
}
```
Suggestions include both components and blocks, distinguished by the `type` field. Results are ranked by keyword relevance.
## Error Handling
All error responses use a consistent JSON envelope:
```json title="Error response"
{
"error": "Component \"unknown\" not found",
"status": 404
}
```
Common status codes:
| Status | Meaning |
| ------ | ------------------------------------------- |
| `200` | Success |
| `400` | Bad request (missing or invalid parameters) |
| `404` | Resource not found |
## For AI Agents
If you are building an AI agent or coding assistant that works with SmoothUI, here is the recommended workflow:
1. **Discovery** -- Use `/api/v1/suggest?need=...` with the user's natural-language request to find relevant components.
2. **Detail** -- Fetch full metadata and source with `/api/v1/components/{name}?include=source`.
3. **Install** -- Use the `installCommand` field from the metadata to install the component.
4. **Integrate** -- Use the source code and `compositionHints` to wire the component into the user's project.
You can also fetch the full [OpenAPI specification](https://smoothui.dev/openapi.json) for automated client generation or tool registration.
For MCP-based integration with AI coding assistants, see the [MCP Server](/docs/guides/mcp) guide.
# Astro Integration (/docs/guides/astro)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Overview
Astro's [islands architecture](https://docs.astro.build/en/concepts/islands/) lets you use SmoothUI React components as interactive islands within your static Astro pages. Each SmoothUI component hydrates independently, giving you smooth animations with minimal JavaScript overhead.
SmoothUI components are standard React components. Astro renders them server-side and hydrates them on the client using directives like `client:load` and `client:visible`. No modifications to the components are needed.
## Prerequisites
Before getting started, make sure you have the following:
| Requirement | Minimum Version | Purpose |
| ------------------------------------------------------------------------------ | --------------- | --------------------------- |
| [Node.js](https://nodejs.org) | 18+ | Runtime |
| [Astro](https://astro.build) | 4.0+ | Framework |
| [@astrojs/react](https://docs.astro.build/en/guides/integrations-guide/react/) | 4.0+ | React integration for Astro |
| [Tailwind CSS](https://tailwindcss.com) | 4.0+ | Styling |
| [Motion](https://motion.dev) | 12.0+ | Animations (Framer Motion) |
## Project Setup
### Create a New Astro Project
If you're starting from scratch, create a new Astro project:
### Add the React Integration
SmoothUI components are React components, so you need `@astrojs/react`:
This automatically updates your `astro.config.mjs{:js}` to include the React integration:
```js title="astro.config.mjs"
import { defineConfig } from "astro/config";
import react from "@astrojs/react";
export default defineConfig({
integrations: [react()],
});
```
### Set Up Tailwind CSS 4
Install Tailwind CSS and the Astro integration:
Add the Tailwind CSS Vite plugin to your Astro config:
```js title="astro.config.mjs"
import { defineConfig } from "astro/config";
import react from "@astrojs/react";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
integrations: [react()],
vite: {
plugins: [tailwindcss()],
},
});
```
Create your main CSS file and import Tailwind:
```css title="src/styles/global.css"
@import "tailwindcss";
```
Import the stylesheet in your layout:
```astro title="src/layouts/Layout.astro"
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
{title}
```
### Install Motion
SmoothUI animations are powered by Motion (Framer Motion). Install it as a dependency:
### Configure Path Aliases
SmoothUI components use the `@/{:js}` path alias. Add it to your `tsconfig.json{:js}`:
```json title="tsconfig.json"
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
```
## Installing SmoothUI Components
### Using the SmoothUI CLI
### Using the shadcn CLI
If this is your first time using the shadcn CLI in an Astro project, it will prompt you to create a `components.json{:js}` file. Accept the defaults — the CLI auto-detects Astro projects and configures paths accordingly.
## Client Directives
Astro components are static by default. To make SmoothUI components interactive, you need to add a [client directive](https://docs.astro.build/en/reference/directives-reference/#client-directives) that tells Astro when to hydrate the component.
| Directive | When It Hydrates | Best For |
| ----------------------------- | ------------------------------------ | ------------------------------------------------ |
| `client:load{:astro}` | Immediately on page load | Above-the-fold components, critical interactions |
| `client:visible{:astro}` | When the component scrolls into view | Below-the-fold components, cards, animations |
| `client:idle{:astro}` | When the browser is idle | Non-critical UI, background animations |
| `client:only="react"{:astro}` | Client-only, no SSR | Components that use browser APIs on mount |
Use `client:visible{:astro}` for most SmoothUI components — it gives the best performance since components only hydrate when the user can see them. Use `client:load{:astro}` for hero sections or components that must be interactive immediately.
## Using Components in Astro Pages
Here is a complete example of using a SmoothUI component in an Astro page:
```astro title="src/pages/index.astro"
---
import Layout from "../layouts/Layout.astro";
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
---
SmoothUI in Astro
```
### Using Multiple Components
```astro title="src/pages/demo.astro"
---
import Layout from "../layouts/Layout.astro";
import { MagneticButton } from "@/components/smoothui/ui/MagneticButton";
import { ExpandableCards } from "@/components/smoothui/ui/ExpandableCards";
import { TypewriterText } from "@/components/smoothui/ui/TypewriterText";
---
Get Started
```
## Motion in Astro Islands
Motion (Framer Motion) works seamlessly inside Astro islands. Since each island is a fully hydrated React component, all animation features are available — spring physics, layout animations, gestures, and `AnimatePresence{:tsx}`.
If you create custom animated components alongside SmoothUI, follow the same pattern:
```tsx title="src/components/FadeIn.tsx"
"use client";
import { motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";
export type FadeInProps = {
children: ReactNode;
};
const FadeIn = ({ children }: FadeInProps) => {
const shouldReduceMotion = useReducedMotion();
return (
{children}
);
};
export default FadeIn;
```
```astro title="src/pages/index.astro"
---
import FadeIn from "../components/FadeIn";
---
This content fades in when scrolled into view.
```
The `"use client"{:tsx}` directive at the top of React component files is a Next.js convention. Astro ignores it, so it does no harm — and it keeps your components compatible with both frameworks.
## Troubleshooting
Make sure you added a client directive (`client:load{:astro}`, `client:visible{:astro}`, etc.) to the component. Without a client directive, Astro renders the component as static HTML with no JavaScript, so animations won't run.
Verify that your `global.css{:css}` includes `@import "tailwindcss"{:css}` and that it's imported in your layout. Also check that the Tailwind Vite plugin is configured in `astro.config.mjs{:js}`.
If a component relies on browser-only APIs (like `window{:js}` or `localStorage{:js}`) during initial render, use `client:only="react"{:astro}` instead of `client:load{:astro}`. This skips server-side rendering entirely for that component.
Motion works well with SSR by default. If you see warnings about `useLayoutEffect{:tsx}`, it's usually harmless. For components that heavily depend on browser measurements, use `client:only="react"{:astro}`.
## Next Steps
Learn about all available installation methods including the SmoothUI CLI and shadcn registry.
Explore the complete collection of 50+ animated React components.
Master animation performance, accessibility, and timing guidelines.
The simplest setup for using SmoothUI in a client-side React app.
Full-stack SSR setup with Remix and React Router v7.
Type-safe full-stack React with streaming SSR.
# Changelog (/docs/guides/changelog)
## 3.1.0
### New Components
* Added **Magnetic Button** component with cursor-following magnetic effect and shadcn-style button variants
* Added **Notification Badge** component with dot, count, and status variants
* Added **Skeleton Loader** component with shimmer, pulse, and wave animation variants
* Added **Animated Tabs** component with underline, pill, and segment style variants
* Added **Animated Toggle** component with morph and icon transition variants
### New Blocks
* Added **FAQ 3** block with searchable questions and filter animations
* Added **FAQ 4** block with categorized questions and tab navigation
* Added **Footer 3** mega footer block with newsletter signup
* Added **Footer 4** minimal single-row footer block
* Added **Logo Cloud 3** block with infinite marquee and pause on hover
* Added **Logo Cloud 4** block with interactive grid and hover effects
## 3.0.6
* Enhanced accessibility and keyboard navigation across all input components
* Added motion reduction support (`prefers-reduced-motion`) across components
* Integrated motion reduction in AI Branch component
## 3.0.5
* Added **TweetCard** component for displaying Twitter/X posts with media support
* Enhanced TweetCard security with DOMPurify sanitization
## 3.0.4
* Added **SwitchboardCard** component with animated light grid effect
* Added **Header 4** block with interactive 3D grid hero section
* Enhanced dropdown components with portal rendering and position handling
## 3.0.3
* Added **Searchable Dropdown** component with search filtering and keyboard navigation
* Improved Wave Text animation performance
## 3.0.2
* Added GlowHover component with cursor-following glow effect - generic and reusable component that works with any React element (cards, buttons, etc.)
* Added InfiniteSlider component for smooth, infinite scrolling with customizable speed and direction
* Added ReviewsCarousel component for displaying testimonials with smooth animations and keyboard navigation
* Enhanced AppleInvites component with responsive sizing and styling support
* Improved RSS feed processing for blocks with better categorization and date extraction
* Optimized image loading across components with enhanced getImageKitUrl options
* Refactored ScrollableCardStack component to use images instead of videos for better performance
* Fixed GitHub Stars Animation component issues
## 3.0.1
* Added RSS feed integration using @wandry/analytics-sdk for component registry updates
* Fixed Safari rendering bug in Siri Orb component with improved mask-radius handling
* Created dedicated sponsors page with tier-based sections (Velocity, Supporters, Own Projects)
* Enhanced sponsor display components with rotation and empty state handling
## 3.0.0
* Complete monorepo restructure with improved organization and maintainability
* Migrated to Next.js 16 with latest features and performance improvements
* Integrated Fumadocs for better documentation experience
* Added Ultracite for enhanced code quality and tooling
* Implemented Biome for fast linting and formatting
* Improved build system and package management
* Enhanced developer experience with better TypeScript configuration
* Updated all dependencies to latest stable versions
## 2.9.0
* New Logo Clouds, Stats, Team, Footer, and FAQs blocks sections
* Enhanced sidebar filter system with All, Components, and Blocks categories
* Improved navigation with conditional sidebar content
* Better content organization separating components from blocks
## 2.8.0
* New comprehensive search system with keyboard shortcuts and real-time results
* Advanced tag-based component discovery with categorized organization
* New tag pages with related components and category information
* Enhanced component search dialog with popular tags and smart suggestions
## 2.7.0
* Full compatibility with shadcn CLI v3 namespace system
* New registry system with automatic dependency management
* MCP (Model Context Protocol) support for AI assistants
* Enhanced documentation with comprehensive installation guides
## 2.6.0
* New AI section with AI-powered components
* New components: AI Input, AI Branch, Scrollable Card Stack, Rich Popover
* New AI menu in the landing page navigation
## 2.0.0
* New Design System and website look
* New mascot called Smoothy
* Props documentation
* Color switcher
* Refine shadcn/ui installation
## 1.0.0
* Initial release of SmoothUI
* Core component library with React and Framer Motion
* Basic documentation and examples
* CLI tool for component installation
# Design Principles (/docs/guides/design-principles)
## Design Philosophy
SmoothUI is built on the principles of simplicity, accessibility, and performance. Each component is designed to be beautiful by default while remaining highly customizable.
## Color System
SmoothUI uses a carefully crafted color system based on OKLCH color space for better color consistency and accessibility. The system includes both light and dark variants.
### Brand Colors
### Neutral Colors
The neutral color palette provides a range of grays that work well in both light and dark modes.
## Design System CSS
Add this CSS to your global styles to enable the full SmoothUI design system:
Expand CSS
```css title="global.css"
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-brand: var(--color-brand);
--color-brand-secondary: var(--color-brand-secondary);
--color-smooth-50: var(--color-smooth-50);
--color-smooth-100: var(--color-smooth-100);
--color-smooth-200: var(--color-smooth-200);
--color-smooth-300: var(--color-smooth-300);
--color-smooth-400: var(--color-smooth-400);
--color-smooth-500: var(--color-smooth-500);
--color-smooth-600: var(--color-smooth-600);
--color-smooth-700: var(--color-smooth-700);
--color-smooth-800: var(--color-smooth-800);
--color-smooth-900: var(--color-smooth-900);
--color-smooth-950: var(--color-smooth-950);
--color-smooth-1000: var(--color-smooth-1000);
--color-border: var(--color-smooth-500);
--color-sidebar-ring: var(--color-brand);
--color-sidebar-border: var(--color-smooth-400);
--color-sidebar-accent-foreground: var(--color-smooth-900);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--color-smooth-1000);
--color-sidebar-primary: var(--color-brand);
--color-sidebar-foreground: var(--color-smooth-1000);
--color-sidebar: var(--color-smooth-100);
--color-ring: var(--color-brand);
--color-input: var(--color-smooth-400);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--color-smooth-1000);
--color-accent: var(--color-brand);
--color-muted-foreground: var(--color-smooth-800);
--color-muted: var(--color-smooth-200);
--color-background: var(--color-smooth-50);
--color-foreground: var(--color-smooth-1000);
--color-primary: var(--color-smooth-100);
--color-primary-foreground: var(--color-smooth-950);
--color-secondary: var(--color-smooth-200);
--color-secondary-foreground: var(--color-smooth-900);
--color-popover-foreground: var(--color-smooth-1000);
--color-popover: var(--color-smooth-50);
--color-card-foreground: var(--color-smooth-1000);
--color-card: var(--color-smooth-100);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
:root {
--color-brand: oklch(0.72 0.2 352.53);
--color-brand-secondary: oklch(0.66 0.21 354.31);
--color-smooth-50: oklch(99.11% 0 0);
--color-smooth-100: oklch(97.91% 0 0);
--color-smooth-200: oklch(96.42% 0 0);
--color-smooth-300: oklch(94.61% 0 0);
--color-smooth-400: oklch(93.1% 0 0);
--color-smooth-500: oklch(91.28% 0 0);
--color-smooth-600: oklch(89.14% 0 0);
--color-smooth-700: oklch(82.97% 0 0);
--color-smooth-800: oklch(65% 0 0);
--color-smooth-900: oklch(61.67% 0 0);
--color-smooth-950: oklch(54.17% 0 0);
--color-smooth-1000: oklch(20.46% 0 0);
--border: var(--color-smooth-300);
--text-primary: var(--color-smooth-200);
--text-quaternary: var(--color-smooth-1000);
--radius: 0.625rem;
}
.dark {
--color-smooth-50: oklch(20.02% 0 0);
--color-smooth-100: oklch(22.64% 0 0);
--color-smooth-200: oklch(25.62% 0 0);
--color-smooth-300: oklch(27.68% 0 0);
--color-smooth-400: oklch(30.12% 0 0);
--color-smooth-500: oklch(32.5% 0 0);
--color-smooth-600: oklch(36.39% 0 0);
--color-smooth-700: oklch(43.13% 0 0);
--color-smooth-800: oklch(54.52% 0 0);
--color-smooth-900: oklch(59.31% 0 0);
--color-smooth-950: oklch(70.58% 0 0);
--color-smooth-1000: oklch(94.61% 0 0);
--border: var(--color-smooth-300);
--text-primary: var(--color-smooth-200);
--text-quaternary: var(--color-smooth-1000);
}
/* Component NumberFlow */
@layer utilities {
.slide-in-up {
animation: slideInUp 0.3s forwards;
}
.slide-out-up {
animation: slideOutUp 0.3s forwards;
}
.slide-in-down {
animation: slideInDown 0.3s forwards;
}
.slide-out-down {
animation: slideOutDown 0.3s forwards;
}
@keyframes slideInUp {
from {
transform: translateY(50px);
filter: blur(5px);
}
to {
transform: translateY(0px);
filter: blur(0px);
}
}
@keyframes slideOutUp {
from {
transform: translateY(0px);
filter: blur(0px);
}
to {
transform: translateY(-50px);
filter: blur(5px);
}
}
@keyframes slideInDown {
from {
transform: translateY(-50px);
filter: blur(5px);
}
to {
transform: translateY(0px);
filter: blur(0px);
}
}
@keyframes slideOutDown {
from {
transform: translateY(0px);
filter: blur(0px);
}
to {
transform: translateY(50px);
filter: blur(5px);
}
}
}
/* Component PowerOffSlide */
@layer utilities {
.loading-shimmer {
text-fill-color: transparent;
-webkit-text-fill-color: transparent;
animation-delay: 0.5s;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-name: loading-shimmer;
background: var(--text-quaternary)
gradient(
linear,
100% 0,
0 0,
from(var(--text-quaternary)),
color-stop(0.5, var(--text-primary)),
to(var(--text-quaternary))
);
background: var(--text-quaternary) -webkit-gradient(
linear,
100% 0,
0 0,
from(var(--text-quaternary)),
color-stop(0.5, var(--text-primary)),
to(var(--text-quaternary))
);
background-clip: text;
-webkit-background-clip: text;
background-repeat: no-repeat;
background-size: 50% 200%;
display: inline-block;
}
.loading-shimmer {
background-position: -100% top;
}
.loading-shimmer:hover {
-webkit-text-fill-color: var(--text-quaternary);
animation: none;
background: transparent;
}
@keyframes loading-shimmer {
0% {
background-position: -100% top;
}
to {
background-position: 250% top;
}
}
}
/* Component AppleInvites */
@layer utilities {
.gradient-mask-t-0 {
-webkit-mask-image: linear-gradient(#0000, #000);
mask-image: linear-gradient(#0000, #000);
}
}
```
## Customization
SmoothUI components are highly customizable. Here are the main ways to customize them:
Override CSS variables to customize colors, spacing, and other design tokens globally.
Use Tailwind utility classes to customize individual components or create variants.
Many components accept props for customization like size, variant, and color options.
# React Hooks (/docs/guides/hooks)
## Overview
SmoothUI provides utility hooks that help you build responsive, device-aware components. These hooks are designed for performance and work seamlessly with Server-Side Rendering (SSR).
***
## useIsMobile
Detects whether the current viewport is mobile-sized. Returns a boolean that updates in real-time as the viewport changes.
### Installation
```bash
npx shadcn@latest add @smoothui/use-mobile
```
Or copy the hook directly:
```tsx
import * as React from "react";
const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState(
undefined
);
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
};
mql.addEventListener("change", onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener("change", onChange);
}, []);
return !!isMobile;
}
```
### Usage
```tsx
import { useIsMobile } from "@/hooks/use-mobile";
function ResponsiveComponent() {
const isMobile = useIsMobile();
return (
{isMobile ? (
) : (
)}
);
}
```
### Parameters
This hook takes no parameters. The mobile breakpoint is set to **768px** by default.
### Returns
| Value | Type | Description |
| ---------- | --------- | ------------------------------------------- |
| `isMobile` | `boolean` | `true` if viewport width is less than 768px |
### Features
* **SSR Safe**: Returns `false` during server-side rendering (no hydration mismatch)
* **Real-time Updates**: Responds to viewport changes via `matchMedia` listener
* **Performance Optimized**: Uses `matchMedia` instead of resize events for better performance
* **Memory Safe**: Properly cleans up event listeners on unmount
### Common Use Cases
#### Conditional Rendering
```tsx
function Header() {
const isMobile = useIsMobile();
return (
);
}
```
#### Responsive Animations
```tsx
import { motion } from "motion/react";
import { useIsMobile } from "@/hooks/use-mobile";
function AnimatedCard() {
const isMobile = useIsMobile();
return (
Card content
);
}
```
#### Responsive Grid
```tsx
function ProductGrid({ products }) {
const isMobile = useIsMobile();
const columns = isMobile ? 1 : 3;
return (
{products.map(product => (
))}
);
}
```
### SSR Considerations
The hook returns `false` on the initial server render, then updates on the client. If you need to avoid layout shift, consider:
```tsx
function ResponsiveLayout() {
const isMobile = useIsMobile();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
// Show a loading state or neutral layout until mounted
if (!mounted) {
return ;
}
return isMobile ? : ;
}
```
### Customizing the Breakpoint
If you need a different breakpoint, create a modified version:
```tsx
const TABLET_BREAKPOINT = 1024;
export function useIsTablet() {
const [isTablet, setIsTablet] = React.useState(
undefined
);
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${TABLET_BREAKPOINT - 1}px)`);
const onChange = () => {
setIsTablet(window.innerWidth < TABLET_BREAKPOINT);
};
mql.addEventListener("change", onChange);
setIsTablet(window.innerWidth < TABLET_BREAKPOINT);
return () => mql.removeEventListener("change", onChange);
}, []);
return !!isTablet;
}
```
***
## Best Practices
### Prefer CSS for Simple Responsive Layouts
For simple responsive styling, CSS media queries are more performant than JavaScript:
```tsx
// Prefer CSS when possible
// Use hooks for complex logic or conditional rendering
const isMobile = useIsMobile();
if (isMobile) return
;
```
### Avoid Excessive Re-renders
The hook only triggers re-renders when crossing the breakpoint threshold, not on every resize event.
### Test Across Devices
Always test responsive behavior on actual devices, not just browser dev tools resizing.
***
## Related
Explore utility functions like cn() for class merging.
Learn how to create performant, accessible animations.
# Introduction (/docs/guides)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## What is SmoothUI?
SmoothUI is a modern component library that brings together the best of React, Tailwind CSS, [Motion](https://motion.dev), and [GSAP](https://gsap.com) to create beautiful, accessible, and performant user interfaces. Each component is carefully crafted with smooth animations and thoughtful design principles.
## Key Features
* **shadcn CLI Compatible**: Install components using the familiar shadcn CLI
* **MCP Support**: AI assistants can discover and install components automatically
* **TypeScript First**: Full type safety with comprehensive TypeScript support
* **Accessible by Default**: Built with accessibility best practices
* **Dark Mode Support**: All components work seamlessly in light and dark themes
* **Customizable**: Easy to customize with Tailwind CSS classes
* **Performance Optimized**: Built with performance in mind using modern React patterns
## Get Started
Learn how to install SmoothUI components using shadcn CLI or manual installation methods.
Explore all available components with live demos and documentation.
## Frequently Asked Questions
Yes! SmoothUI is completely free and open source. You can use it in personal and commercial projects.
No! All animations are built-in. You can use components without any Motion or GSAP knowledge.
Absolutely! All components are built with Tailwind CSS and can be customized using standard Tailwind classes.
Yes! SmoothUI works with any React framework including Next.js, Vite, Create React App, and more.
## Contributing
We welcome contributions to SmoothUI! Whether you want to add new components, improve existing ones, or fix bugs, your contributions help make SmoothUI better for everyone.
* Fork the repository on GitHub
* Create a new branch for your feature or bug fix
* Make your changes and test them thoroughly
* Submit a pull request with a clear description
Check out our [contributing guide](https://github.com/educlopez/smoothui/blob/main/CONTRIBUTING.md) for more detailed information.
# Installation (/docs/guides/installation)
SmoothUI works with any React-compatible framework. Check out the dedicated setup guides:
* **[Vite + React](/docs/guides/vite)** — The simplest setup for client-side React apps
* **[Astro](/docs/guides/astro)** — Islands architecture with selective hydration
* **[Remix](/docs/guides/remix)** — Full-stack SSR with React Router v7
* **[TanStack Start](/docs/guides/tanstack-start)** — Type-safe full-stack with streaming SSR
## Installation (SmoothUI CLI)
The SmoothUI CLI provides an interactive way to browse and install components with automatic dependency resolution.
### Add Components
### Add Multiple Components
### Interactive Mode
### List Available Components
Run the add command without arguments to launch an interactive picker. Search and browse components by category, then select multiple components to install at once.
***
## Installation (shadcn Registry)
SmoothUI is an official shadcn registry, so you can install components directly without any configuration. Just use the `@smoothui{:js}` namespace.
Since SmoothUI is an official registry, you don't need to add anything to your `components.json{:js}` file. Just install components directly!
### Install Components
Install SmoothUI components using the shadcn CLI with the `@smoothui{:js}` namespace:
### Install Multiple Components
### Use Components
Import and use the installed components in your React application:
```tsx
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb"
import { RichPopover } from "@/components/smoothui/ui/RichPopover"
export default function App() {
return (
)
}
```
# MCP Server (/docs/guides/mcp)
# MCP Server
MCP support for registry developers - Enable AI assistants to discover and use SmoothUI components.
The **shadcn MCP server** works out of the box with any shadcn-compatible registry. You do not need to do anything special to enable MCP support for your SmoothUI registry.
## Prerequisites
The MCP server works by requesting your registry index. Make sure you have a registry item file at the root of your registry named `registry.json{:js}`.
For example, if your registry is hosted at `https://smoothui.dev/r/[name].json{:js}`, you should have a file at `https://smoothui.dev/r/registry.json{:js}`.
This file must be a valid JSON file that conforms to the registry schema.
## Configuring MCP
Ask your registry consumers to configure your registry in their `components.json{:js}` file and install the shadcn MCP server:
**Configure your registry** in your `components.json{:js}` file:
```json title="components.json"
{
"registries": {
// [!code highlight]
"@smoothui": "https://smoothui.dev/r/{name}.json"
}
}
```
**Run the following command** in your project:
```shell title="Terminal"
pnpm dlx shadcn@latest mcp init --client claude
```
```shell title="Terminal"
npx shadcn@latest mcp init --client claude
```
```shell title="Terminal"
yarn dlx shadcn@latest mcp init --client claude
```
```shell title="Terminal"
bunx shadcn@latest mcp init --client claude
```
**Restart Claude Code** and try the following prompts:
* Show me the components in the smoothui registry
* Create a landing page using items from the smoothui registry
* Install the SiriOrb component from smoothui
**Note:** You can use `/mcp{:js}` command in Claude Code to debug the MCP server.
**Configure your registry** in your `components.json{:js}` file:
```json title="components.json"
{
"registries": {
// [!code highlight]
"@smoothui": "https://smoothui.dev/r/{name}.json"
}
}
```
**Run the following command** in your project:
```shell title="Terminal"
pnpm dlx shadcn@latest mcp init --client cursor
```
```shell title="Terminal"
npx shadcn@latest mcp init --client cursor
```
```shell title="Terminal"
yarn dlx shadcn@latest mcp init --client cursor
```
```shell title="Terminal"
bunx shadcn@latest mcp init --client cursor
```
Open **Cursor Settings** and **Enable the MCP server** for shadcn. Then try the following prompts:
* Show me the components in the smoothui registry
* Create a landing page using items from the smoothui registry
* Install the SiriOrb component from smoothui
**Configure your registry** in your `components.json{:js}` file:
```json title="components.json"
{
"registries": {
// [!code highlight]
"@smoothui": "https://smoothui.dev/r/{name}.json"
}
}
```
**Run the following command** in your project:
```shell title="Terminal"
pnpm dlx shadcn@latest mcp init --client vscode
```
```shell title="Terminal"
npx shadcn@latest mcp init --client vscode
```
```shell title="Terminal"
yarn dlx shadcn@latest mcp init --client vscode
```
```shell title="Terminal"
bunx shadcn@latest mcp init --client vscode
```
Open `.vscode/mcp.json{:js}` and click **Start** next to the shadcn server. Then try the following prompts with GitHub Copilot:
* Show me the components in the smoothui registry
* Create a landing page using items from the smoothui registry
* Install the SiriOrb component from smoothui
## Example Prompts
Once MCP is configured, you can use these prompts with your AI assistant:
Component Discovery
“Show me all available components in the smoothui registry”
“What animation components are available in smoothui?”
“List all interactive components from smoothui”
Component Installation
“Install the SiriOrb component from smoothui”
“Add the RichPopover component to my project”
“Install multiple components: SiriOrb, AnimatedInput, and ScrollableCardStack”
Component Usage
“Create a landing page using the SiriOrb component”
“Show me how to use the ScrollableCardStack component”
“Build a dashboard with smoothui components”
## Learn More
Full overview of all AI integration options — MCP, REST API, and machine-readable catalogs.
Complete API reference for programmatic access to the component catalog.
You can also read more about the shadcn MCP protocol in the shadcn MCP documentation .
# Remix (/docs/guides/remix)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Overview
[Remix](https://remix.run) is a full-stack React framework focused on web standards and progressive enhancement. As of v2, Remix has evolved into the framework mode of [React Router v7](https://reactrouter.com), bringing the same SSR-first architecture with a streamlined API. SmoothUI components work seamlessly in both Remix and React Router v7 projects.
SmoothUI components are standard React client components. Remix renders them on the server and hydrates them on the client. The `"use client"{:tsx}` directive ensures components with animations and browser APIs hydrate correctly. No special wrappers are needed for most components.
## Prerequisites
Before getting started, make sure you have the following:
| Requirement | Minimum Version | Purpose |
| -------------------------------------------------------------------- | --------------- | -------------------------- |
| [Node.js](https://nodejs.org) | 18+ | Runtime |
| [Remix](https://remix.run) / [React Router](https://reactrouter.com) | 2.0+ / 7.0+ | Framework |
| [React](https://react.dev) | 19.0+ | UI library |
| [Tailwind CSS](https://tailwindcss.com) | 4.0+ | Styling |
| [Motion](https://motion.dev) | 12.0+ | Animations (Framer Motion) |
Remix has merged into React Router v7. If you're starting a new project, use `create react-router{:bash}`. Existing Remix v2 projects can upgrade to React Router v7 — see the [migration guide](https://reactrouter.com/upgrading/remix). Both setups work identically with SmoothUI.
## Project Setup
### Create a New Project
If you're starting from scratch, create a new React Router v7 project (the successor to Remix):
Then install dependencies:
### Set Up Tailwind CSS 4
Remix and React Router v7 use Vite under the hood, so Tailwind CSS 4 setup uses the Vite plugin:
Add the Tailwind CSS Vite plugin to your Vite config:
```ts title="vite.config.ts"
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
});
```
Update your main CSS file with the Tailwind import:
```css title="app/app.css"
@import "tailwindcss";
```
### Install Motion
SmoothUI animations are powered by Motion (Framer Motion). Install it as a dependency:
### Configure Path Aliases
SmoothUI components use the `@/{:ts}` path alias. React Router v7 projects typically use `vite-tsconfig-paths{:ts}` to resolve paths from `tsconfig.json{:json}` automatically. Make sure your tsconfig includes the alias:
```json title="tsconfig.json"
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"]
}
}
}
```
React Router v7 projects include `vite-tsconfig-paths{:ts}` by default, which reads your `tsconfig.json{:json}` paths and applies them as Vite aliases. You only need to configure paths in one place — your tsconfig.
## Installing SmoothUI Components
### Using the SmoothUI CLI
### Using the shadcn CLI
If this is your first time using the shadcn CLI in a Remix / React Router project, it will prompt you to create a `components.json{:json}` file. Accept the defaults — the CLI auto-detects the project structure and configures paths accordingly.
## SSR Considerations
Remix and React Router v7 render components on the server first, then hydrate them on the client. Here's what you need to know when using SmoothUI components in an SSR environment:
| Concern | How SmoothUI Handles It |
| ------------------------ | ---------------------------------------------------------------------------------------------------- |
| `"use client"` directive | SmoothUI components include this directive. Remix respects it for client-side hydration. |
| Motion SSR | Motion handles SSR gracefully — animations simply start after hydration. No special config needed. |
| Browser APIs | Components that use `window{:ts}` or `localStorage{:ts}` during render need a `ClientOnly` wrapper. |
| Hydration mismatches | Rare with SmoothUI. If seen, ensure no random values are generated during SSR (see Troubleshooting). |
### The ClientOnly Pattern
If you use a SmoothUI component that relies on browser measurements during initial render, wrap it with a `ClientOnly` helper:
```tsx title="app/components/client-only.tsx"
"use client";
import { type ReactNode, useEffect, useState } from "react";
export type ClientOnlyProps = {
children: ReactNode;
fallback?: ReactNode;
};
const ClientOnly = ({ children, fallback = null }: ClientOnlyProps) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted ? children : fallback;
};
export default ClientOnly;
```
```tsx title="app/routes/home.tsx"
import ClientOnly from "@/components/client-only";
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
const Home = () => {
return (
}>
);
};
export default Home;
```
The majority of SmoothUI components work fine with SSR out of the box. Only use `ClientOnly` for components that access browser-only APIs like `window.matchMedia{:ts}` or `ResizeObserver{:ts}` during their initial render.
## Using Components in Routes
Here is a complete example of using SmoothUI components in a Remix / React Router v7 route:
```tsx title="app/routes/home.tsx"
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
import { MagneticButton } from "@/components/smoothui/ui/MagneticButton";
const Home = () => {
return (
SmoothUI + Remix
Get Started
);
};
export default Home;
```
### Custom Animated Components
If you create custom animated components alongside SmoothUI, follow the same pattern:
```tsx title="app/components/FadeIn.tsx"
"use client";
import { motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";
export type FadeInProps = {
children: ReactNode;
};
const FadeIn = ({ children }: FadeInProps) => {
const shouldReduceMotion = useReducedMotion();
return (
{children}
);
};
export default FadeIn;
```
## Troubleshooting
Hydration mismatches occur when the server-rendered HTML differs from what the client renders. Common causes: using `Math.random(){:ts}`, `Date.now(){:ts}`, or browser APIs during render. Use the `ClientOnly` pattern shown above for components that depend on browser-only values.
If you see `useLayoutEffect{:tsx}` warnings in your server logs, they're harmless — Motion uses `useLayoutEffect{:tsx}` for performance, and React warns when it's called during SSR. The animations will work correctly after hydration. To suppress the warning, you can use `client:only` or the `ClientOnly` wrapper.
Verify that `@tailwindcss/vite{:ts}` is included in your `vite.config.ts{:ts}` plugins array and that your `app/app.css{:css}` (or `app/root.css{:css}`) contains `@import "tailwindcss"{:css}`. Also check that the CSS file is imported in your root route.
When using `ClientOnly`, provide a `fallback{:tsx}` with the same dimensions as the component to prevent layout shifts. For example: ` }>{:tsx}`.
## Next Steps
Learn about all available installation methods including the SmoothUI CLI and shadcn registry.
Explore the complete collection of 50+ animated React components.
Master animation performance, accessibility, and timing guidelines.
The simplest setup for using SmoothUI in a client-side React app.
# SmoothUI vs shadcn/ui (/docs/guides/shadcn-alternative)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## The Best of Both Worlds
SmoothUI isn't a replacement for shadcn/ui - it's the perfect companion. While shadcn/ui excels at providing solid, accessible base components, SmoothUI extends the ecosystem with **animated, interactive components** that bring your interfaces to life.
## Why Choose SmoothUI?
### Smooth Motion Animations
Every SmoothUI component is built with **[Motion](https://motion.dev)** and **[GSAP](https://gsap.com)** animations that feel natural and polished. No more wrestling with CSS keyframes or animation libraries - animations are built-in and optimized for performance.
```tsx
// Just import and use - animations included
import { ExpandableCards } from "@/components/ui/expandable-cards"
```
### Same Workflow, Added Motion
If you're familiar with shadcn/ui, you'll feel right at home. SmoothUI uses the **same CLI installation pattern**:
```bash
npx shadcn@latest add @smoothui/expandable-cards
```
Components are copied into your project, giving you full ownership and customization control.
### Built for Modern React
SmoothUI components follow React best practices:
* **Server Components compatible** - Works with Next.js App Router
* **TypeScript first** - Full type definitions included
* **Tailwind CSS v4** - Uses the latest Tailwind features
* **Accessibility** - Respects `prefers-reduced-motion`
## Feature Comparison
| Feature | shadcn/ui | SmoothUI |
| ------------------- | ------------ | ------------ |
| Static Components | Excellent | Good |
| Animated Components | Limited | Excellent |
| Motion Animations | Manual setup | Built-in |
| Installation | shadcn CLI | shadcn CLI |
| Customization | Full control | Full control |
| TypeScript | Yes | Yes |
| Tailwind CSS | v3/v4 | v4 |
| Dark Mode | Yes | Yes |
| Accessibility | Excellent | Good |
| Component Count | 40+ | 50+ |
## When to Use Each
### Use shadcn/ui for:
* Form inputs, selects, and form validation
* Modal dialogs and alert dialogs
* Navigation menus and dropdowns
* Data tables and pagination
* Basic buttons and badges
### Use SmoothUI for:
* Animated card layouts and expandable cards
* Smooth hover effects and micro-interactions
* Dynamic content transitions
* Interactive showcases and galleries
* Animated text effects (typewriter, wave, scramble)
* Loading states with motion
## Working Together
The best approach is using both libraries together. Here's a typical setup:
```tsx
// shadcn/ui for the form structure
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Dialog, DialogContent } from "@/components/ui/dialog"
// SmoothUI for animated elements
import { AnimatedInput } from "@/components/ui/animated-input"
import { ScrambleHover } from "@/components/ui/scramble-hover"
import { ExpandableCards } from "@/components/ui/expandable-cards"
```
Both libraries use the same folder structure (`components/ui/`) and Tailwind CSS foundation, making them seamlessly compatible.
## Get Started
Set up SmoothUI in your project alongside shadcn/ui.
Explore 50+ animated components ready to use.
## Frequently Asked Questions
No! SmoothUI is designed to complement shadcn/ui, not replace it. Use shadcn/ui for forms and foundational UI, and add SmoothUI components when you need animations and interactivity.
Absolutely! Both libraries use the same installation pattern and folder structure. They work together seamlessly in the same project.
Minimal. If you're familiar with shadcn/ui, you already know the workflow. SmoothUI components follow the same patterns - just with added animations.
SmoothUI animations are built with Motion and GSAP, optimized for performance. They use GPU-accelerated transforms and respect the user's reduced motion preferences for accessibility.
# Sponsors (/docs/guides/sponsors)
# TanStack Start (/docs/guides/tanstack-start)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Overview
[TanStack Start](https://tanstack.com/start) is a modern full-stack React framework built on [Vinxi](https://vinxi.vercel.app) (a Vite-based server toolkit) and [TanStack Router](https://tanstack.com/router). It provides type-safe routing, streaming SSR, and server functions out of the box. SmoothUI components integrate smoothly with TanStack Start's architecture.
TanStack Start uses Vite under the hood via Vinxi, so SmoothUI components work the same way as in any Vite-based project. Components are server-rendered and hydrated on the client. The `"use client"{:tsx}` directive ensures animations activate after hydration.
TanStack Start is a newer framework that is evolving quickly. The setup steps below reflect the current stable release. Check the [official docs](https://tanstack.com/start/latest/docs/framework/react/overview) for the latest changes.
## Prerequisites
Before getting started, make sure you have the following:
| Requirement | Minimum Version | Purpose |
| -------------------------------------------- | --------------- | -------------------------- |
| [Node.js](https://nodejs.org) | 18+ | Runtime |
| [TanStack Start](https://tanstack.com/start) | 1.0+ | Framework |
| [React](https://react.dev) | 19.0+ | UI library |
| [Tailwind CSS](https://tailwindcss.com) | 4.0+ | Styling |
| [Motion](https://motion.dev) | 12.0+ | Animations (Framer Motion) |
## Project Setup
### Create a New TanStack Start Project
If you're starting from scratch, create a new TanStack Start project:
Then install dependencies:
### Set Up Tailwind CSS 4
TanStack Start uses Vinxi, which is built on Vite. Install Tailwind CSS and the Vite plugin:
Add the Tailwind CSS Vite plugin to your app config:
```ts title="app.config.ts"
import { defineConfig } from "@tanstack/react-start/config";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
vite: {
plugins: () => [tailwindcss()],
},
});
```
Create or update your main CSS file with the Tailwind import:
```css title="app/styles/app.css"
@import "tailwindcss";
```
Import the stylesheet in your root route (typically `app/routes/__root.tsx{:tsx}`):
```tsx title="app/routes/__root.tsx"
import { createRootRoute, Outlet } from "@tanstack/react-router";
import appCss from "@/styles/app.css?url";
export const Route = createRootRoute({
head: () => ({
links: [{ rel: "stylesheet", href: appCss }],
}),
component: RootComponent,
});
function RootComponent() {
return (
);
}
```
### Install Motion
SmoothUI animations are powered by Motion (Framer Motion). Install it as a dependency:
### Configure Path Aliases
SmoothUI components use the `@/{:ts}` path alias. Configure it in your `tsconfig.json{:json}`:
```json title="tsconfig.json"
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"]
}
}
}
```
TanStack Start resolves TypeScript paths automatically via Vinxi's Vite integration. If path aliases don't resolve at runtime, add `vite-tsconfig-paths{:ts}` to your app config:
```ts title="app.config.ts"
import { defineConfig } from "@tanstack/react-start/config";
import tailwindcss from "@tailwindcss/vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
vite: {
plugins: () => [tailwindcss(), tsconfigPaths()],
},
});
```
## Installing SmoothUI Components
### Using the SmoothUI CLI
### Using the shadcn CLI
If this is your first time using the shadcn CLI in a TanStack Start project, it will prompt you to create a `components.json{:json}` file. Configure the `aliases.components{:json}` path to match your project structure (typically `@/components{:json}`).
## SSR & Streaming
TanStack Start supports streaming SSR by default, which means components render progressively on the server. Here's what you need to know when using SmoothUI components:
| Concern | How SmoothUI Handles It |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| `"use client"` directive | SmoothUI components include this directive. TanStack Start respects it for client-side hydration. |
| Motion SSR | Motion handles SSR gracefully — animations start after hydration. No special config needed. |
| Streaming compatibility | SmoothUI components work with streaming SSR. Animations start as each component hydrates. |
| Browser APIs | Components using `window{:ts}` or `ResizeObserver{:ts}` during render need a client-only guard. |
### Client-Only Components
For the rare component that needs browser APIs during initial render, use a mounted check:
```tsx title="app/components/client-only.tsx"
"use client";
import { type ReactNode, useEffect, useState } from "react";
export type ClientOnlyProps = {
children: ReactNode;
fallback?: ReactNode;
};
const ClientOnly = ({ children, fallback = null }: ClientOnlyProps) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted ? children : fallback;
};
export default ClientOnly;
```
## Using Components in Routes
Here is a complete example of using SmoothUI components in a TanStack Start route:
```tsx title="app/routes/index.tsx"
import { createFileRoute } from "@tanstack/react-router";
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
import { MagneticButton } from "@/components/smoothui/ui/MagneticButton";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
return (
SmoothUI + TanStack Start
Get Started
);
}
```
### Custom Animated Components
If you create custom animated components alongside SmoothUI, follow the same pattern:
```tsx title="app/components/FadeIn.tsx"
"use client";
import { motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";
export type FadeInProps = {
children: ReactNode;
};
const FadeIn = ({ children }: FadeInProps) => {
const shouldReduceMotion = useReducedMotion();
return (
{children}
);
};
export default FadeIn;
```
## Troubleshooting
Verify that `@tailwindcss/vite{:ts}` is included in your `app.config.ts{:ts}` Vite plugins, and that your CSS file contains `@import "tailwindcss"{:css}`. Also check that the stylesheet is linked in your root route's `head{:tsx}` function.
TanStack Start uses Vinxi (Vite-based), which should resolve TypeScript paths. If aliases don't work, add `vite-tsconfig-paths{:ts}` to your `app.config.ts{:ts}` Vite plugins as shown in the setup section.
Hydration mismatches occur when server-rendered HTML differs from client output. Avoid using `Math.random(){:ts}`, `Date.now(){:ts}`, or browser APIs during render. For components that depend on client-only values, use the `ClientOnly` wrapper pattern.
With streaming SSR, components hydrate progressively. Animations using `initial{:tsx}` and `animate{:tsx}` props will play as each component hydrates — this is the expected behavior. If you want animations to trigger on scroll instead, use Motion's `whileInView{:tsx}` prop.
## Next Steps
Learn about all available installation methods including the SmoothUI CLI and shadcn registry.
Explore the complete collection of 50+ animated React components.
Master animation performance, accessibility, and timing guidelines.
Full-stack SSR setup with Remix and React Router v7.
# Utility Functions (/docs/guides/utilities)
## Overview
SmoothUI provides utility functions that simplify common patterns in React development. These utilities are used throughout the component library and are available for your own components.
***
## cn() - Class Name Utility
The `cn()` function merges class names intelligently, combining the power of [clsx](https://github.com/lukeed/clsx) for conditional classes and [tailwind-merge](https://github.com/dcastil/tailwind-merge) for resolving Tailwind CSS conflicts.
### Installation
The `cn()` utility is included with any SmoothUI component installation. You can also install it directly:
```bash
pnpm add clsx tailwind-merge
```
Then create the utility:
```tsx
// lib/utils.ts
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
```
### Usage
```tsx
import { cn } from "@/lib/utils";
function Button({ className, variant, ...props }) {
return (
);
}
```
### Why cn()?
#### Problem: Tailwind Class Conflicts
Without `cn()`, Tailwind classes can conflict unpredictably:
```tsx
// Without cn() - which padding wins? Unpredictable!
// If className="p-2", result is "p-4 p-2" - browser uses last one (p-2)
// But this isn't guaranteed and varies by property
```
#### Solution: Smart Merging
`cn()` intelligently resolves conflicts:
```tsx
// With cn() - consumer override always wins
// If className="p-2", result is "p-2" - clean, predictable
```
### Features
#### Conditional Classes
```tsx
cn(
"base-class",
isActive && "active-class", // Boolean condition
!isDisabled && "enabled-class", // Negated condition
error ? "error-class" : "success" // Ternary
)
```
#### Object Syntax
```tsx
cn({
"bg-blue-500": variant === "primary",
"bg-gray-500": variant === "secondary",
"opacity-50 cursor-not-allowed": isDisabled,
})
```
#### Array Syntax
```tsx
cn([
"base-styles",
conditionalStyles,
[nestedArray, "also-works"]
])
```
#### Tailwind Conflict Resolution
```tsx
// Input
cn("px-4 py-2", "px-6")
// Output: "py-2 px-6" (px-6 wins, py-2 preserved)
// Input
cn("text-red-500", "text-blue-500")
// Output: "text-blue-500" (last color wins)
// Input
cn("hover:bg-red-500", "hover:bg-blue-500")
// Output: "hover:bg-blue-500" (handles modifiers correctly)
```
### Common Patterns
#### Component Variants
```tsx
const buttonVariants = {
primary: "bg-blue-500 text-white hover:bg-blue-600",
secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
ghost: "bg-transparent hover:bg-gray-100",
};
const buttonSizes = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
};
function Button({ variant = "primary", size = "md", className, ...props }) {
return (
);
}
```
#### Forwarding className
Always accept and forward `className` as the last argument to `cn()`:
```tsx
// Correct: className last, can override anything
function Card({ className, ...props }) {
return (
);
}
// Usage
// Result: p-8 and shadow-lg override defaults
```
#### With cva (Class Variance Authority)
`cn()` pairs well with [cva](https://cva.style/docs) for complex variant systems:
```tsx
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input bg-background hover:bg-accent",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3",
lg: "h-11 px-8",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes
,
VariantProps {}
function Button({ className, variant, size, ...props }: ButtonProps) {
return (
);
}
```
### TypeScript
The `cn()` function is fully typed:
```tsx
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// ClassValue accepts:
// - string
// - number
// - boolean | null | undefined (falsy values ignored)
// - ClassValue[] (arrays)
// - { [key: string]: boolean } (objects)
```
***
## Best Practices
### Always Use cn() for Component Styling
```tsx
// Good: Allows overrides, resolves conflicts
className={cn("base-styles", className)}
// Avoid: No conflict resolution
className={`base-styles ${className}`}
```
### Keep Base Styles First
```tsx
cn(
"base-styles", // 1. Foundational styles
"variant-styles", // 2. Variant-specific
"state-styles", // 3. State-based (hover, active)
className // 4. Consumer overrides (last!)
)
```
### Use Semantic Groups
```tsx
cn(
// Layout
"flex items-center gap-2",
// Sizing
"h-10 px-4",
// Typography
"text-sm font-medium",
// Colors
"bg-blue-500 text-white",
// Interactions
"hover:bg-blue-600 focus:ring-2",
// Overrides
className
)
```
***
## Related
Explore SmoothUI hooks like useIsMobile.
Learn more about Tailwind class conflict resolution.
Learn more about conditional class name construction.
# Vite + React (/docs/guides/vite)
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Overview
[Vite](https://vite.dev) is a fast build tool that provides an excellent developer experience for React projects. SmoothUI components work out of the box with Vite — no special configuration or SSR workarounds needed. This is the simplest way to get started with SmoothUI.
Vite + React is a purely client-side setup by default. SmoothUI components render directly in the browser with full access to DOM APIs, so every animation feature — spring physics, gestures, layout animations, and `AnimatePresence{:tsx}` — works without any additional setup.
## Prerequisites
Before getting started, make sure you have the following:
| Requirement | Minimum Version | Purpose |
| --------------------------------------- | --------------- | -------------------------- |
| [Node.js](https://nodejs.org) | 18+ | Runtime |
| [Vite](https://vite.dev) | 6.0+ | Build tool |
| [React](https://react.dev) | 19.0+ | UI library |
| [Tailwind CSS](https://tailwindcss.com) | 4.0+ | Styling |
| [Motion](https://motion.dev) | 12.0+ | Animations (Framer Motion) |
## Project Setup
### Create a New Vite + React Project
If you're starting from scratch, create a new Vite project with the React TypeScript template:
Then install dependencies:
### Set Up Tailwind CSS 4
Install Tailwind CSS and the Vite plugin:
Add the Tailwind CSS Vite plugin to your Vite config:
```ts title="vite.config.ts"
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
});
```
Replace the contents of your main CSS file with the Tailwind import:
```css title="src/index.css"
@import "tailwindcss";
```
### Install Motion
SmoothUI animations are powered by Motion (Framer Motion). Install it as a dependency:
### Configure Path Aliases
SmoothUI components use the `@/{:ts}` path alias. Configure it in both `vite.config.ts{:ts}` and `tsconfig.json{:json}`:
```ts title="vite.config.ts"
import path from "node:path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
```
```json title="tsconfig.app.json"
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
```
Vite uses the `resolve.alias{:ts}` config for runtime path resolution, while TypeScript uses `paths{:json}` in `tsconfig.app.json{:json}` for type checking. You need both for everything to work correctly.
## Installing SmoothUI Components
### Using the SmoothUI CLI
### Using the shadcn CLI
If this is your first time using the shadcn CLI in a Vite project, it will prompt you to create a `components.json{:json}` file. Accept the defaults — the CLI auto-detects Vite + React projects and configures paths accordingly.
## Using Components
Here is a complete example of using SmoothUI components in your Vite + React app:
```tsx title="src/App.tsx"
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
import { MagneticButton } from "@/components/smoothui/ui/MagneticButton";
const App = () => {
return (
SmoothUI + Vite
Get Started
);
};
export default App;
```
### Custom Animated Components
If you create custom animated components alongside SmoothUI, follow the same pattern:
```tsx title="src/components/FadeIn.tsx"
"use client";
import { motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";
export type FadeInProps = {
children: ReactNode;
};
const FadeIn = ({ children }: FadeInProps) => {
const shouldReduceMotion = useReducedMotion();
return (
{children}
);
};
export default FadeIn;
```
The `"use client"{:tsx}` directive at the top of component files is a Next.js convention. Vite ignores it, so it does no harm — and it keeps your components compatible if you ever migrate to a framework with SSR support.
## Troubleshooting
Make sure the `@tailwindcss/vite{:ts}` plugin is included in your `vite.config.ts{:ts}` plugins array, and that your `src/index.css{:css}` contains `@import "tailwindcss"{:css}`. Also verify that `index.css{:css}` is imported in your `src/main.tsx{:tsx}` entry file.
You need path aliases configured in **two places**: `resolve.alias{:ts}` in `vite.config.ts{:ts}` for Vite's bundler, and `paths{:json}` in `tsconfig.app.json{:json}` for TypeScript. If only one is configured, you'll get either build errors or type errors.
Vite's HMR requires that React components are exported as named or default exports from files that only export components. If HMR breaks, try restarting the dev server with `pnpm dev`. Also ensure `@vitejs/plugin-react{:ts}` is in your Vite config.
Motion supports tree-shaking, so only the features you use are included in your bundle. If you want to verify, run `pnpm build` and inspect the output. For the smallest bundle, import directly from `motion/react{:ts}` rather than `framer-motion{:ts}`.
## Next Steps
Learn about all available installation methods including the SmoothUI CLI and shadcn registry.
Explore the complete collection of 50+ animated React components.
Master animation performance, accessibility, and timing guidelines.
Learn how to use SmoothUI components in Astro with islands architecture.