` instead of `
`:
```tsx
```
## API Reference
### PixelParagraph Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | — | The paragraph text to render |
| `pixelWords` | `string[]` | `[]` | Words/phrases to render in a pixel font. Matching is case-sensitive, longest-first |
| `as` | `"p" \| "span" \| "div"` | `"p"` | The wrapper element to render |
| `font` | `"square" \| "grid" \| "circle" \| "triangle" \| "line"` | `"square"` | The pixel font for highlighted words |
| `pixelWordClassName` | `string` | — | Extra className applied to each pixel-word span |
| `className` | `string` | — | Additional CSS classes applied to the wrapper element |
### Available Pixel Fonts
| Font Name | Tailwind Class | CSS Variable |
| ---------- | --------------------- | ----------------------------- |
| `square` | `font-pixel-square` | `--font-geist-pixel-square` |
| `grid` | `font-pixel-grid` | `--font-geist-pixel-grid` |
| `circle` | `font-pixel-circle` | `--font-geist-pixel-circle` |
| `triangle` | `font-pixel-triangle` | `--font-geist-pixel-triangle` |
| `line` | `font-pixel-line` | `--font-geist-pixel-line` |
## How It Works
1. The `text` string is split into alternating segments: **plain** (normal font) and **pixel** (highlighted words)
2. Plain segments render as standard `` elements in the inherited font
3. Pixel segments render as `` elements with the chosen pixel font class applied
4. Matching is **longest-first** — if `pixelWords` contains both `"ui"` and `"shadcn/ui"`, the longer phrase takes priority
5. Matching is **case-sensitive** — `"React"` will not match `"react"`
## Features
- **Static font assignment** — pixel words get the chosen font, no hover/cycling complexity
- **Five pixel font variants** — Square, Grid, Circle, Triangle, and Line
- **Longest-match-first** — multi-word phrases match correctly without partial collisions
- **Server-compatible** — no `"use client"` needed, no state or effects
- **Composable** — renders as ``, ``, or `` with full className support
## Pixel Paragraph vs Pixel Paragraph Inverse
| Feature | Pixel Paragraph | Pixel Paragraph Inverse |
| --- | --- | --- |
| Base text | Normal font (sans) | Pixel font |
| Highlighted words | Pixel font | Sans/mono font |
| Best for | Accenting key words in normal body text | Full pixel paragraphs with readable escapes |
---
# Pixel Paragraph Inverse
> Paragraph for React and shadcn/ui set in a Geist pixel font, with chosen words switching to a sans or mono typeface.
Source: https://www.cult-ui.com/docs/components/pixel-paragraph-words-inverse
## Example
```tsx title="pixel-paragraph-words-inverse-demo.tsx"
"use client"
import { useState } from "react"
import { PixelParagraphInverse } from "@/components/ui/pixel-paragraph-words-inverse"
/* ─── Constants ─── */
const PIXEL_FONTS = ["square", "grid", "circle", "triangle", "line"] as const
type PixelFont = (typeof PIXEL_FONTS)[number]
const PLAIN_FONTS = ["sans", "mono"] as const
type PlainFont = (typeof PLAIN_FONTS)[number]
const WRAPPER_TAGS = ["p", "span", "div"] as const
const DEFAULT_TEXT =
"54+ animated components and effects. Free, open source, and built to drop into any shadcn/ui project."
const DEFAULT_PLAIN_WORDS = "animated,shadcn/ui"
/* ─── Demo ─── */
export default function PixelParagraphWordsInverseDemo() {
const [text, setText] = useState(DEFAULT_TEXT)
const [plainWordsInput, setPlainWordsInput] = useState(DEFAULT_PLAIN_WORDS)
const [pixelFont, setPixelFont] = useState
("square")
const [plainFont, setPlainFont] = useState("sans")
const [wrapperTag, setWrapperTag] =
useState<(typeof WRAPPER_TAGS)[number]>("p")
const plainWords = plainWordsInput
.split(",")
.map((w) => w.trim())
.filter(Boolean)
return (
{/* ── Preview ── */}
{/* ── Controls ── */}
{/* Text */}
{/* Plain Words */}
setPlainWordsInput(e.target.value)}
className="border-input placeholder:text-muted-foreground focus-visible:ring-ring h-9 w-full rounded-md border bg-transparent px-3 text-sm shadow-sm focus-visible:ring-1 focus-visible:outline-none"
placeholder="e.g. animated,shadcn/ui,open source"
/>
These words escape the pixel font and render in sans/mono
{/* Pixel Font */}
{PIXEL_FONTS.map((f) => (
setPixelFont(f)}
className={`rounded-md px-3 py-1.5 text-xs font-medium transition-colors ${
pixelFont === f
? "bg-foreground text-background"
: "bg-muted text-muted-foreground hover:bg-muted/80"
}`}
>
{f}
))}
{/* Plain Font */}
{PLAIN_FONTS.map((f) => (
setPlainFont(f)}
className={`rounded-md px-3 py-1.5 text-xs font-medium transition-colors ${
plainFont === f
? "bg-foreground text-background"
: "bg-muted text-muted-foreground hover:bg-muted/80"
}`}
>
{f}
))}
{/* Wrapper Tag */}
setWrapperTag(e.target.value as (typeof WRAPPER_TAGS)[number])
}
className="border-input focus-visible:ring-ring h-9 w-full rounded-md border bg-transparent px-3 text-sm shadow-sm focus-visible:ring-1 focus-visible:outline-none"
>
{WRAPPER_TAGS.map((t) => (
{"<" + t + ">"}
))}
)
}
/* ─── Shared control primitives ─── */
function ControlGroup({
label,
children,
className,
}: {
label: string
children: React.ReactNode
className?: string
}) {
return (
{label}
{children}
)
}
```
Pixel Paragraph Inverse is a React text component for shadcn/ui using Geist pixel fonts. Use it for landing page intros, taglines, or callouts where a few key terms should read as plain text.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/pixel-paragraph-words-inverse
```
### Manual
**Install the `geist` font package.**
```bash
npm install geist
```
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/pixel-paragraph-words-inverse.json
**Update the import paths to match your project setup.**
## Font Setup
This component requires the **Geist Pixel fonts**. Follow these steps to configure them in your project.
### 1. Register font variables in your root layout
Import the pixel font variants from `geist/font/pixel` and apply their CSS variable classes to ``:
```tsx title="app/layout.tsx"
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import {
GeistPixelSquare,
GeistPixelGrid,
GeistPixelCircle,
GeistPixelTriangle,
GeistPixelLine,
} from "geist/font/pixel";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
### 2. Map CSS variables in your Tailwind CSS theme
```css title="globals.css (Tailwind v4)"
@theme {
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-pixel-square: var(--font-geist-pixel-square);
--font-pixel-grid: var(--font-geist-pixel-grid);
--font-pixel-circle: var(--font-geist-pixel-circle);
--font-pixel-triangle: var(--font-geist-pixel-triangle);
--font-pixel-line: var(--font-geist-pixel-line);
}
```
For **Tailwind v3**, use the `extend` key in `tailwind.config.ts`:
```ts title="tailwind.config.ts (Tailwind v3)"
export default {
theme: {
extend: {
fontFamily: {
sans: ["var(--font-geist-sans)"],
mono: ["var(--font-geist-mono)"],
"pixel-square": ["var(--font-geist-pixel-square)"],
"pixel-grid": ["var(--font-geist-pixel-grid)"],
"pixel-circle": ["var(--font-geist-pixel-circle)"],
"pixel-triangle": ["var(--font-geist-pixel-triangle)"],
"pixel-line": ["var(--font-geist-pixel-line)"],
},
},
},
};
```
## Usage
```tsx
import { PixelParagraphInverse } from "@/components/ui/pixel-paragraph-words-inverse";
```
### Basic
Base text in pixel, specific words escape to sans:
```tsx
```
### Mono Escape
Use mono instead of sans for the escaped words:
```tsx
```
### Different Pixel Fonts
Change the base pixel font:
```tsx
```
### Styling Plain Words
Apply custom classes to the escaped words:
```tsx
```
### As a Different Element
Render as a `` or `` instead of `
`:
```tsx
```
## API Reference
### PixelParagraphInverse Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | — | The paragraph text to render |
| `plainWords` | `string[]` | `[]` | Words/phrases to render in plain (sans/mono) font. Matching is case-sensitive, longest-first |
| `as` | `"p" \| "span" \| "div"` | `"p"` | The wrapper element to render |
| `pixelFont` | `"square" \| "grid" \| "circle" \| "triangle" \| "line"` | `"square"` | The pixel font used for the base text |
| `plainFont` | `"sans" \| "mono"` | `"sans"` | The font for escaped words |
| `plainWordClassName` | `string` | — | Extra className applied to each plain-word span |
| `className` | `string` | — | Additional CSS classes applied to the wrapper element |
## How It Works
1. The `text` string is split into alternating segments: **pixel** (base) and **plain** (escaped words)
2. Pixel segments render in the chosen pixel font as static text
3. Plain segments render as `` elements with the chosen plain font class (`font-sans` or `font-mono`)
4. Matching is **longest-first** — if `plainWords` contains both `"ui"` and `"shadcn/ui"`, the longer phrase takes priority
5. Matching is **case-sensitive** — `"React"` will not match `"react"`
## Features
- **Inverse design** — base text is pixel, highlighted words are sans/mono
- **Static font assignment** — no hover/cycling complexity, just clean font mixing
- **Server-compatible** — no `"use client"` needed, no state or effects
- **Five pixel font variants** — Square, Grid, Circle, Triangle, and Line
- **Longest-match-first** — multi-word phrases match correctly without partial collisions
- **Composable** — renders as ``, ``, or `` with full className support
## Pixel Paragraph vs Pixel Paragraph Inverse
| Feature | Pixel Paragraph | Pixel Paragraph Inverse |
| --- | --- | --- |
| Base text | Normal font (sans) | Pixel font |
| Highlighted words | Pixel font | Sans/mono font |
| Best for | Accenting key words in normal body text | Full pixel paragraphs with readable escapes |
---
# Poll Widget
> Composable poll widget for React and shadcn/ui shown inline, in a popover, or in a dialog, with results and keyboard support.
Source: https://www.cult-ui.com/docs/components/poll-widget
## Example
```tsx title="poll-widget-demo.tsx"
"use client"
import { useCallback, useMemo, useState } from "react"
import { PollWidget } from "../ui/poll-widget"
const DEMO_OPTIONS = [
{ id: "speed", label: "Faster builds" },
{ id: "dx", label: "Better DX" },
{ id: "docs", label: "Clearer docs" },
] as const
const BASE_VOTES = { speed: 41, dx: 28, docs: 30 }
function usePollState() {
const [votes, setVotes] = useState
>(BASE_VOTES)
const [hasVoted, setHasVoted] = useState(false)
const onVote = useCallback((selectedIds: string[]) => {
setVotes((prev) => {
const next: Record = { ...prev }
for (const id of selectedIds) {
next[id] = (next[id] ?? 0) + 1
}
return next
})
setHasVoted(true)
}, [])
return { votes, hasVoted, onVote }
}
export default function PollWidgetDemo() {
return (
)
}
function InlineDemo() {
const { votes, hasVoted, onVote } = usePollState()
const options = useMemo(
() => DEMO_OPTIONS.map((o) => ({ id: o.id, label: o.label })),
[]
)
return (
Inline (default)
{options.map((opt) => (
{opt.label}
))}
)
}
function PopoverDemo() {
const { votes, hasVoted, onVote } = usePollState()
const options = useMemo(
() => DEMO_OPTIONS.map((o) => ({ id: o.id, label: o.label })),
[]
)
return (
Popover mode
{options.map((opt) => (
{opt.label}
))}
)
}
function DialogDemo() {
const { votes, hasVoted, onVote } = usePollState()
const options = useMemo(
() => DEMO_OPTIONS.map((o) => ({ id: o.id, label: o.label })),
[]
)
return (
Dialog mode
{options.map((opt) => (
{opt.label}
))}
)
}
```
Poll Widget is a composable React voting component for shadcn/ui, animated with Motion. Use it to collect quick feedback in blog posts, product updates, community pages, or in-app surveys.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/poll-widget
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/poll-widget.json
**Update the import paths to match your project setup.**
## Usage
Use `PollWidget` with `question`, `options`, and `votes` / `hasVoted` / `onVote` for results. Choose `mode`: `"inline"`, `"popover"`, or `"dialog"`. For popover and dialog, add `PollWidget.Trigger` and wrap content in `PollWidget.Content`. Inside content use `PollWidget.Question`, `PollWidget.Options`, `PollWidget.Option` (with `PollWidget.Indicator`, `PollWidget.Label`, `PollWidget.Percentage`), `PollWidget.Results`, and `PollWidget.Submit`.
### Inline poll
```tsx
import { useCallback, useMemo, useState } from "react";
import { PollWidget } from "@/registry/default/ui/poll-widget";
const options = [
{ id: "speed", label: "Faster builds" },
{ id: "dx", label: "Better DX" },
{ id: "docs", label: "Clearer docs" },
];
export default function Example() {
const [votes, setVotes] = useState({ speed: 0, dx: 0, docs: 0 });
const [hasVoted, setHasVoted] = useState(false);
const onVote = useCallback((selectedIds: string[]) => {
setVotes((prev) => {
const next = { ...prev };
for (const id of selectedIds) next[id] = (next[id] ?? 0) + 1;
return next;
});
setHasVoted(true);
}, []);
return (
{options.map((opt) => (
{opt.label}
))}
);
}
```
## Features
- **Modes**: `inline` (default), `popover`, or `dialog` for different layouts
- **Results**: Pass `votes`, `hasVoted`, and `onVote` to show progress and success state after voting
- **Compound API**: PollWidget, Trigger, Content, Question, Options, Option, Indicator, Label, Percentage, Results, Submit, Success, Dialog
- **Animations**: Voting → results → success transitions; optional auto-collapse in popover/dialog
---
# Popover
> Composable animated popover for React and shadcn/ui that morphs a button into a panel, with form, textarea, and footer parts.
Source: https://www.cult-ui.com/docs/components/popover
## Example
```tsx title="popover-demo.tsx"
"use client"
import React from "react"
import { Image as ImageIcon, Paintbrush, Plus } from "lucide-react"
import {
PopoverBody,
PopoverButton,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverForm,
PopoverHeader,
PopoverLabel,
PopoverRoot,
PopoverSubmitButton,
PopoverTextarea,
PopoverTrigger,
} from "../ui/popover"
function PopoverInput() {
const handleSubmit = (note: string) => {
console.log("Submitted note:", note)
}
return (
Add Feedback
Add Feedback
)
}
const ColorPickerPopover = () => {
const colors = [
"#FF5733",
"#33FF57",
"#3357FF",
"#FF33F1",
"#33FFF1",
"#F1FF33",
]
return (
Choose Color
Pick a Color
{colors.map((color) => (
console.log(`Selected color: ${color}`)}
/>
))}
)
}
const QuickActionsPopover = () => {
const actions = [
{
icon: ,
label: "New File",
action: () => console.log("New File"),
},
{
icon: ,
label: "Upload Image",
action: () => console.log("Upload Image"),
},
{
icon: ,
label: "Edit Colors",
action: () => console.log("Edit Colors"),
},
]
return (
Quick Actions
Quick Actions
{actions.map((action, index) => (
{action.icon}
{action.label}
))}
)
}
const ImagePreviewPopover = () => {
return (
Preview Image
Image Preview
Image preview description goes here.
)
}
export default function PopoverExamples() {
return (
)
}
```
Popover is a composable React component for shadcn/ui built with Motion layout animations. Use it for quick notes, inline feedback, or small menus that open in place and close on click outside.
## References
Inspiration
motion primitives
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/popover
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/popover.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverForm,
PopoverLabel,
PopoverRoot,
PopoverSubmitButton,
PopoverTextarea,
PopoverTrigger,
} from "@/components/Popover";
```
```typescriptreact
export default function PopoverDemo() {
const handleSubmit = (note: string) => {
console.log("Submitted note:", note);
};
return (
Headless Composable Popover Demo
Add Note
Add Note
);
}
```
## Popover Component
The Popover component is a headless, composable component that provides a flexible and customizable popover functionality. It uses Framer Motion for smooth animations and React context for state management.
### PopoverRoot
The `PopoverRoot` component is the main wrapper for the Popover. It provides the context and configuration for all child components.
```typescriptreact
{/* Other Popover components */}
```
### PopoverTrigger
The `PopoverTrigger` component is used to trigger the opening of the popover. It can wrap any clickable element.
```typescriptreact
Add Note
```
### PopoverContent
The `PopoverContent` component contains the main content of the popover. It handles the animation and positioning of the popover.
```typescriptreact
{/* Popover content */}
```
### PopoverForm
The `PopoverForm` component is used to create a form within the popover. It handles form submission and provides an `onSubmit` prop for custom submission logic.
```typescriptreact
{/* Form fields */}
```
### PopoverLabel
The `PopoverLabel` component is used to add a label to the popover content. It animates with the popover opening and closing.
```typescriptreact
Add Note
```
### PopoverTextarea
The `PopoverTextarea` component provides a textarea input for the popover form.
```typescriptreact
```
### PopoverFooter
The `PopoverFooter` component is used to create a footer section in the popover, typically containing action buttons.
```typescriptreact
{/* Footer content */}
```
### PopoverCloseButton
The `PopoverCloseButton` component provides a button to close the popover.
```typescriptreact
```
### PopoverSubmitButton
The `PopoverSubmitButton` component provides a submit button for the popover form.
```typescriptreact
```
## Customization
The Popover component is highly customizable. You can modify the styles of each sub-component by passing className props or by wrapping them in your own styled components. The animation behavior can be adjusted by modifying the `TRANSITION` object in the component's source code.
## Accessibility
The Popover component includes basic accessibility features such as:
- Keyboard navigation support (Escape key to close)
- Proper ARIA attributes
- Focus management
However, depending on your specific use case, you may need to add additional accessibility features to ensure full compliance with WCAG guidelines.
---
# Popover Form
> Animated popover form for React and shadcn/ui that expands from a button with a success state, for feedback and waitlists.
Source: https://www.cult-ui.com/docs/components/popover-form
## Example
```tsx title="popover-form-demo.tsx"
"use client"
import { useEffect, useState } from "react"
import { Mail, Monitor, Moon, Sun } from "lucide-react"
import {
PopoverForm,
PopoverFormButton,
PopoverFormCutOutLeftIcon,
PopoverFormCutOutRightIcon,
PopoverFormSeparator,
PopoverFormSuccess,
} from "@/components/ui/popover-form"
type FormState = "idle" | "loading" | "success"
export function NewsletterSignupExample() {
const [formState, setFormState] = useState("idle")
const [open, setOpen] = useState(false)
const [email, setEmail] = useState("")
function submit() {
setFormState("loading")
setTimeout(() => {
setFormState("success")
}, 1500)
setTimeout(() => {
setOpen(false)
setFormState("idle")
setEmail("")
}, 3300)
}
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setOpen(false)
}
}
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [])
return (
{
e.preventDefault()
if (!email) return
submit()
}}
className="p-4"
>
Email address
Sick content to your mailbox every week!
}
successChild={
}
/>
)
}
type Theme = "light" | "dark" | "system"
export function ColorThemeSwitcherExample() {
const [theme, setTheme] = useState("system")
const [systemTheme, setSystemTheme] = useState<"light" | "dark">("light")
const [open, setOpen] = useState(false)
const themes: Theme[] = ["light", "dark", "system"]
useEffect(() => {
const systemPrefersDark = window.matchMedia("(prefers-color-scheme: dark)")
const updateSystemTheme = () => {
setSystemTheme(systemPrefersDark.matches ? "dark" : "light")
}
updateSystemTheme()
systemPrefersDark.addEventListener("change", updateSystemTheme)
return () => {
systemPrefersDark.removeEventListener("change", updateSystemTheme)
}
}, [])
useEffect(() => {
const root = window.document.documentElement
root.classList.remove("light", "dark")
if (theme === "system") {
root.classList.add(systemTheme)
} else {
root.classList.add(theme)
}
}, [theme, systemTheme])
return (
Theme
{themes.map((t) => {
const isSelected = theme === t
const effectiveTheme = t === "system" ? systemTheme : t
return (
setTheme(t)}
className={`flex w-full items-center rounded-md px-3 py-2 text-sm ${
isSelected
? `bg-primary ${
effectiveTheme === "light"
? "text-white"
: "text-black"
}`
: `hover:bg-gray-100 hover:text-black dark:text-white dark:hover:bg-gray-800 dark:hover:text-white`
}`}
>
{t === "light" && }
{t === "dark" && }
{t === "system" && }
{t}
)
})}
}
/>
)
}
export function FeedbackFormExample() {
const [formState, setFormState] = useState("idle")
const [open, setOpen] = useState(false)
const [feedback, setFeedback] = useState("")
function submit() {
setFormState("loading")
setTimeout(() => {
setFormState("success")
}, 1500)
setTimeout(() => {
setOpen(false)
setFormState("idle")
setFeedback("")
}, 3300)
}
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setOpen(false)
}
if (
(event.ctrlKey || event.metaKey) &&
event.key === "Enter" &&
open &&
formState === "idle"
) {
submit()
}
}
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [open, formState])
return (
{
e.preventDefault()
if (!feedback) return
submit()
}}
className=""
>
}
successChild={
}
/>
)
}
export function ContactFormExample() {
const [formState, setFormState] = useState("idle")
const [open, setOpen] = useState(false)
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [message, setMessage] = useState("")
function submit() {
setFormState("loading")
setTimeout(() => {
setFormState("success")
}, 1500)
setTimeout(() => {
setOpen(false)
setFormState("idle")
setName("")
setEmail("")
setMessage("")
}, 3300)
}
return (
)
}
export default function PopoverFormExamples() {
return (
)
}
```
PopoverForm is a React component for shadcn/ui built with Motion layout animations that closes on click outside. Use it for feedback forms, waitlist signups, or quick contact inputs that should stay out of the way until needed.
## References
Inspiration
Animations dot dev
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/popover-form
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/popover-form.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
PopoverForm,
PopoverFormButton,
PopoverFormCutOutLeftIcon,
PopoverFormCutOutRightIcon,
PopoverFormSeparator,
PopoverFormSuccess,
} from "@/registry/default/ui/popover-form";
```
```typescriptreact
export default function PopoverFormFeedbackExample() {
const [formState, setFormState] = useState("idle");
const [open, setOpen] = useState(false);
const [feedback, setFeedback] = useState("");
function submit() {
setFormState("loading");
setTimeout(() => {
setFormState("success");
}, 1500);
setTimeout(() => {
setOpen(false);
setFormState("idle");
setFeedback("");
}, 3300);
}
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setOpen(false);
}
if (
(event.ctrlKey || event.metaKey) &&
event.key === "Enter" &&
open &&
formState === "idle"
) {
submit();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [open, formState]);
return (
{
e.preventDefault();
if (!feedback) return;
submit();
}}
>
}
successChild={
}
/>
);
}
```
---
# Prompt Composer
> AI chat prompt input for React and shadcn/ui with a gradient rim, Warp shader avatar, loading line, and attach and send buttons.
Source: https://www.cult-ui.com/docs/components/prompt-composer
## Example
```tsx title="prompt-composer-demo.tsx"
"use client"
import { useState } from "react"
import { PromptComposer } from "@/components/ui/prompt-composer"
const MOCK_LOADING_MS = 4200
const LOADING_STAGGER_SEC = 0.044
const LOADING_CHAR_DURATION_SEC = 0.42
export default function PromptComposerDemo() {
const [value, setValue] = useState("")
const [isLoading, setIsLoading] = useState(false)
return (
Animated composer
Multi-line message field with send on ⌘ or Ctrl+Enter, loading line
animation while sending, and an optional attachment affordance.
{
// Demo: no-op; replace with file input / dialog.
}}
onChange={(e) => setValue(e.target.value)}
onSend={() => {
if (!value.trim() || isLoading) {
return
}
setIsLoading(true)
window.setTimeout(() => setIsLoading(false), MOCK_LOADING_MS)
}}
value={value}
/>
)
}
```
Prompt Composer is a React prompt input for shadcn/ui built on Base UI and @paper-design/shaders-react. Use it as the message input in AI chat apps and agent interfaces.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/prompt-composer
```
### Manual
**Install the required dependencies:**
```bash
npm install @base-ui/react @paper-design/shaders-react lucide-react motion
```
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/prompt-composer.json
**Update the import paths to match your project setup.**
---
# Prompt Library
> Prompt template library for React and shadcn/ui to browse, search, insert, and save AI prompts by category from a popover.
Source: https://www.cult-ui.com/docs/components/prompt-library
## Example
```tsx title="prompt-library-demo.tsx"
"use client"
import { useState } from "react"
import {
PromptLibrary,
PromptLibraryContent,
PromptLibraryCreateDialog,
PromptLibraryCreateTrigger,
PromptLibraryEmpty,
PromptLibraryFooter,
PromptLibraryGroup,
PromptLibraryItem,
PromptLibraryList,
PromptLibrarySearch,
PromptLibraryTrigger,
type Prompt,
} from "../ui/prompt-library"
const SAMPLE_PROMPTS: Prompt[] = [
{
id: "code-review",
title: "Code Review",
description: "Review code for best practices and potential issues",
prompt:
"Please review the following code for:\n- Best practices and coding standards\n- Potential bugs or edge cases\n- Performance optimizations\n- Security vulnerabilities\n- Suggestions for improvement",
category: "Development",
},
{
id: "explain-code",
title: "Explain Code",
description: "Get a detailed explanation of how code works",
prompt:
"Please explain this code in detail:\n- What does each part do?\n- How do the components interact?\n- What are the key concepts being used?\n- Are there any important patterns or techniques?",
category: "Development",
},
{
id: "write-tests",
title: "Write Tests",
description: "Generate unit tests for the given code",
prompt:
"Please write comprehensive unit tests for this code:\n- Cover all public methods and functions\n- Include edge cases and error scenarios\n- Use descriptive test names\n- Add comments explaining what each test validates",
category: "Testing",
},
{
id: "refactor",
title: "Refactor Code",
description: "Suggest improvements and cleaner implementations",
prompt:
"Please refactor this code to:\n- Improve readability and maintainability\n- Follow SOLID principles\n- Reduce complexity and duplication\n- Use modern language features where appropriate",
category: "Development",
},
{
id: "document",
title: "Generate Documentation",
description: "Create documentation for code or APIs",
prompt:
"Please generate documentation for this code including:\n- Overview and purpose\n- Parameters and return values\n- Usage examples\n- Any important notes or caveats",
category: "Documentation",
},
]
function PromptLibraryExample() {
const [prompts, setPrompts] = useState(SAMPLE_PROMPTS)
const [lastSelected, setLastSelected] = useState(null)
return (
Browse and insert prompt templates. Click a prompt to copy it to
clipboard, or pass onInsert to put it straight into your input.
{prompts
.filter((p) => p.category === "Development")
.map((prompt) => (
))}
{prompts
.filter((p) => p.category === "Testing")
.map((prompt) => (
))}
{prompts
.filter((p) => p.category === "Documentation")
.map((prompt) => (
))}
{prompts
.filter((p) => p.isCustom)
.map((prompt) => (
))}
{lastSelected && (
Last copied:
{lastSelected.title}
{lastSelected.prompt}
)}
)
}
export default PromptLibraryExample
```
Prompt Library is a composable React component for shadcn/ui built on shadcn Command, Popover, Dialog, and Hover Card. Use it beside AI chat inputs so users can reuse saved prompts instead of retyping them.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/prompt-library
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/prompt-library.json
**Update the import paths to match your project setup.**
## Usage
Use `PromptLibrary` with `prompts`, `onPromptsChange`, and `onSelect`. Compose `PromptLibraryTrigger`, `PromptLibraryContent` (with `PromptLibrarySearch`, `PromptLibraryList`, `PromptLibraryEmpty`, `PromptLibraryGroup`, `PromptLibraryItem`), `PromptLibraryFooter`, `PromptLibraryCreateTrigger`, and `PromptLibraryCreateDialog` for the full experience.
### Basic example
```tsx
import { useState } from "react"
import {
PromptLibrary,
PromptLibraryContent,
PromptLibraryCreateDialog,
PromptLibraryCreateTrigger,
PromptLibraryEmpty,
PromptLibraryFooter,
PromptLibraryGroup,
PromptLibraryItem,
PromptLibraryList,
PromptLibrarySearch,
PromptLibraryTrigger,
type Prompt,
} from "@/registry/default/ui/prompt-library"
const prompts: Prompt[] = [
{
id: "code-review",
title: "Code Review",
description: "Review code for best practices",
prompt: "Please review this code for best practices and potential issues.",
category: "Development",
},
]
export default function Example() {
const [promptsState, setPromptsState] = useState(prompts)
const [lastSelected, setLastSelected] = useState(null)
return (
{promptsState
.filter((p) => p.category === "Development")
.map((prompt) => (
))}
)
}
```
### With AI Elements PromptInput
Pass `onInsert` to put the selected prompt into your chat input instead of the clipboard. With [AI Elements](https://ai-sdk.dev/elements), read the controller inside `PromptInputProvider`:
```tsx
import { usePromptInputController } from "@/components/ai-elements/prompt-input"
function PromptPicker() {
const controller = usePromptInputController()
return (
{/* trigger and content */}
)
}
```
## Features
- **Categories**: Group prompts with `PromptLibraryGroup` and optional `heading`
- **Custom prompts**: Add and remove custom prompts via `onPromptsChange`, `PromptLibraryCreateTrigger`, and `PromptLibraryCreateDialog`
- **Copy or insert**: Selecting a prompt copies it to the clipboard, or calls `onInsert` with the prompt text when you pass it
- **Search**: `PromptLibrarySearch` filters the list
- **Compound API**: PromptLibrary, PromptLibraryTrigger, PromptLibraryContent, PromptLibrarySearch, PromptLibraryList, PromptLibraryEmpty, PromptLibraryGroup, PromptLibraryItem, PromptLibraryFooter, PromptLibraryCreateTrigger, PromptLibraryCreateDialog
---
# Rolling Number
> Animated number counter for React and shadcn/ui that rolls to new values with a Motion spring and custom formatting.
Source: https://www.cult-ui.com/docs/components/rolling-number
## Example
```tsx title="rolling-number-demo.tsx"
"use client"
import { useState } from "react"
import { Minus, Plus } from "lucide-react"
import { toast } from "sonner"
import { Button } from "@/components/ui/button"
import { Slider } from "@/components/ui/slider"
import { GradientHeading } from "@/components/ui/gradient-heading"
import { RollingNumber } from "@/components/ui/rolling-number"
import {
TextureCardContent,
TextureCardHeader,
TextureCardStyled,
} from "@/components/ui/texture-card"
function PrecisionExample() {
const [value, setValue] = useState(14.5678)
return (
Precision
setValue(value + 13.456)}
>
)
}
function FormatExample() {
const [value, setValue] = useState(10)
const customFormat = (num: number) => `$${num.toFixed(2)}`
return (
Format
)
}
function HooksExample() {
const [value, setValue] = useState(10)
const handleAnimationStart = () => {
toast("🏁 Animation started ")
}
const handleAnimationComplete = () => {
toast("✅ Animation completed ")
}
return (
Callbacks
)
}
function CustomSpringExample() {
const [value, setValue] = useState(1000)
const [mass, setMass] = useState(1)
const [stiffness, setStiffness] = useState(100)
const [damping, setDamping] = useState(40)
const handleValueChange =
(setter: (value: number) => void, minValue: number) =>
(values: number[]) => {
const newValue = Math.max(values[0], minValue)
setter(newValue)
}
return (
Custom Spring Properties
setValue(value + 500)}
>
Increase
setValue(value - 300)}
>
Decrease
Mass: {mass}
Stiffness: {stiffness}
Damping: {damping}
)
}
export default function RollingNumberExamples() {
return (
)
}
```
Rolling Number is a React component for shadcn/ui animated with a Motion spring. Use it for dashboard metrics, counters, prices, or live stats that change over time.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/rolling-number
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/rolling-number.json
**Update the import paths to match your project setup.**
## Usage
```tsx
function BasicExample() {
const [value, setValue] = useState(1000);
return (
setValue(value + 1000)}
>
);
}
```
---
# Security Checkpoint
> Security checkpoint illustration for React and shadcn/ui with a verification card and animated line layers, for bot checks.
Source: https://www.cult-ui.com/docs/components/security-checkpoint
## Example
```tsx title="security-checkpoint-demo.tsx"
"use client"
import { SecurityCheckpoint } from "@/components/ui/security-checkpoint"
function SecurityCheckpointDemo() {
return (
)
}
export default SecurityCheckpointDemo
```
Security Checkpoint is a React illustration component for shadcn/ui with a customizable title, subtitle, and message. Use it for bot check pages, loading gates, or security and firewall feature sections on marketing sites.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/security-checkpoint
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/security-checkpoint.json
**Update the import paths to match your project setup.**
---
# Shader Lens Blur
> Interactive WebGL lens blur shader for React and shadcn/ui built on Three.js, with pointer-reactive distortion and gradients.
Source: https://www.cult-ui.com/docs/components/shader-lens-blur
## Example
```tsx title="shader-lens-blur-demo.tsx"
"use client"
import { useCallback, useId } from "react"
import { useAtom } from "jotai"
import { Circle, CircleOff, Sliders, Square, Triangle } from "lucide-react"
import { motion } from "motion/react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
import { Slider } from "@/components/ui/slider"
import { Switch } from "@/components/ui/switch"
import ColorPicker from "../ui/color-picker"
import ShaderLensBlur, { configAtom } from "../ui/shader-lens-blur"
function ShaderLensBlurDemo() {
return (
)
}
function ShaderBlurConfig() {
const [config, setConfig] = useAtom(configAtom)
const id = useId()
const variationId = `${id}-variation`
const enableHoverId = `${id}-enable-hover`
const invertMouseId = `${id}-invert-mouse`
const widthId = `${id}-width`
const heightId = `${id}-height`
const handleVariationChange = useCallback(
(value: string) => {
setConfig((prev) => ({ ...prev, variation: parseInt(value) }))
},
[setConfig]
)
const handleColorChange = useCallback(
(key: "color1" | "color2" | "color3" | "color4", value: string) => {
setConfig((prev) => ({ ...prev, [key]: value }))
},
[setConfig]
)
const handleDimensionChange = useCallback(
(key: "width" | "height", value: number) => {
setConfig((prev) => ({ ...prev, [key]: value }))
},
[setConfig]
)
const variationIcons = [
{ icon: Square, label: "Square" },
{ icon: Circle, label: "Solid Circle" },
{ icon: CircleOff, label: "Hollow Circle" },
{ icon: Triangle, label: "Triangle" },
]
return (
Shader Configuration
Variation
{variationIcons.map((variation, index) => (
- {variation.label}
))}
Colors
{(["color1", "color2", "color3", "color4"] as const).map(
(color) => (
{color}
handleColorChange(color, value)
}
/>
)
)}
Options
Enable Hover
setConfig((prev) => ({ ...prev, enableHover: checked }))
}
/>
Invert Mouse
setConfig((prev) => ({ ...prev, invertMouse: checked }))
}
/>
Dimensions
Width
{config.width}px
handleDimensionChange("width", value)
}
/>
Height
{config.height}px
handleDimensionChange("height", value)
}
/>
)
}
export default ShaderLensBlurDemo
```
ShaderLensBlur is a React component for shadcn/ui built on Three.js. Pointer movement distorts its shapes and color gradients, which suits landing page heroes, product backdrops, and creative portfolio sections.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/shader-lens-blur
```
### Manual
**Install the required dependencies:**
```bash
npm install jotai three motion next-themes
```
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/shader-lens-blur.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import ShaderLensBlur from "@/components/ui/shader-lens-blur";
```
### Basic
Render the default interactive lens blur canvas:
```tsx
```
### Programmatic Configuration
`ShaderLensBlur` reads its configuration from the exported `configAtom`. This makes it easy to drive from your own controls, presets, or app state.
```tsx
"use client";
import { useAtom } from "jotai";
import ShaderLensBlur, { configAtom } from "@/components/ui/shader-lens-blur";
export function ShaderLensBlurControlled() {
const [config, setConfig] = useAtom(configAtom);
return (
setConfig((prev) => ({
...prev,
variation: (prev.variation + 1) % 4,
invertMouse: !prev.invertMouse,
}))
}
>
Toggle Preset
Current variation: {config.variation}
);
}
```
### Set Explicit Dimensions
The component supports custom canvas dimensions through the same atom config:
```tsx
setConfig((prev) => ({
...prev,
width: "100%",
height: "520px",
}));
```
## API Reference
### Exports
| Export | Type | Description |
| --- | --- | --- |
| `ShaderLensBlur` | `React.ComponentType` | Main interactive shader component |
| `configAtom` | `Atom` | Shared state for colors, variation, interaction mode, and dimensions |
### `ShaderConfig` Shape
| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `variation` | `number` | `3` | Shape mode (`0` square-ish frame, `1` solid circle, `2` ring, `3` triangle) |
| `color1` | `string` | `#D5F981` | Gradient color stop 1 |
| `color2` | `string` | `#A1BBE7` | Gradient color stop 2 |
| `color3` | `string` | `#F2BAE2` | Gradient color stop 3 |
| `color4` | `string` | `#68E8FA` | Gradient color stop 4 |
| `enableHover` | `boolean` | `true` | Enables pointer tracking without requiring press/drag |
| `invertMouse` | `boolean` | `true` | Inverts the pointer influence mask |
| `width` | `string` | `"100%"` | Container width style |
| `height` | `string` | `"400px"` | Container height style |
## Behavior Notes
- Uses a `THREE.WebGLRenderer` with an orthographic full-screen quad and custom fragment shader.
- Pointer input is translated from container-space into shader-space and corrected for device pixel ratio.
- Shader colors and variation update live when `configAtom` changes.
- Theme awareness is built in (`next-themes`) and influences subtle color mixing behavior in the shader.
## Accessibility & UX Tips
- Provide a visible parent label or heading so users understand what interaction is expected.
- Keep height at least `280px` for comfortable pointer/touch interaction.
- For touch-first contexts, consider `enableHover: false` so effects primarily respond during active touch/press.
## Troubleshooting
### Blur hotspot feels offset from pointer
- Ensure you are using the latest component version (includes DPR-correct pointer mapping).
- Avoid additional CSS transforms (for example `scale(...)`) on the canvas container.
- If wrapping inside animated layout components, verify the container reports stable bounds (`getBoundingClientRect`).
### Nothing renders
- Confirm WebGL is available in the browser/environment.
- Confirm required dependencies are installed and import paths were updated.
- If using theme features, ensure your app is wrapped with `ThemeProvider` from `next-themes`.
## References
Inspiration
codrops-sdf-lensblur
---
# Shadow Card
> Composable marketing card for React and shadcn/ui with an extruded edge, bevel layers, vertical text, and a pixel gradient mesh.
Source: https://www.cult-ui.com/docs/components/shadow-card
## Example
```tsx title="shadow-card-demo.tsx"
"use client"
import {
ShadowCard,
ShadowCardBackdrop,
ShadowCardBevel,
ShadowCardFooter,
ShadowCardGlow,
ShadowCardPixelGradient,
ShadowCardVerticalText,
} from "@/components/ui/shadow-card"
const CARD1_DOT_PATTERN = [
1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0,
]
function ShadowCardDemoCard1() {
return (
Where Design And Product Teams
Build The Future Of Software
{CARD1_DOT_PATTERN.map((visible, i) => (
))}
THE DESIGN
ZEPXEN
)
}
function ShadowCardDemoCard2() {
return (
Where Design And Product Teams Build The Future Of Software.
Design Is About To Have Its
GitHub Moment.
)
}
function ShadowCardDemo() {
return (
)
}
export { ShadowCardDemo }
export default ShadowCardDemo
```
Shadow Card is a composable React card component for shadcn/ui. Use it for feature highlights, pricing tiers, or marketing sections on landing pages.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/shadow-card
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/shadow-card.json
**Install `@radix-ui/react-slot` if you use the root `asChild` prop.**
**Update the import paths to match your project setup.**
## Parts
| Part | Role |
|------|------|
| `ShadowCard` | Root container; supports `asChild` to merge props onto a child (e.g. `Link`). |
| `ShadowCardBevel` | L-shaped bars for the 3D edge; use `barClassName` for thickness or color. |
| `ShadowCardBackdrop` | Absolutely positioned layer for gradients or images (`className` sets geometry). |
| `ShadowCardGlow` | Optional blurred accent blob. |
| `ShadowCardPixelGradient` | Row/column color mesh; pass `colors` to customize the grid. |
| `ShadowCardVerticalText` | Vertical writing mode wrapper for edge copy. |
| `ShadowCardFooter` | Bottom-left absolute region for logos or metadata. |
Place `ShadowCardBevel` after other layers so its `z-10` stack paints on top.
---
# Shift Card
> Hover reveal card for React and shadcn/ui that shifts its header to show extra content, for product grids and team pages.
Source: https://www.cult-ui.com/docs/components/shift-card
## Example
```tsx title="shift-card-demo.tsx"
"use client"
import { motion } from "motion/react"
import { ShiftCard } from "@/components/ui/shift-card"
import { TextureButton } from "@/components/ui/texture-button"
export default function ShiftCardDemo() {
// Content for the top part of the card
const topContent = (
Screen Capture
Screen Capture
)
// Content that animates into top from the middle
const topAnimateContent = (
<>
>
)
// Content that animates from the top to the middle
const middleContent = (
)
// Content for the bottom part of the card that shows more details on hover
const bottomContent = (
Share your image to build that audience. Inspired by{" "}
@mrncst
Twitter
Post on Twitter
Loom
{" "}
Open in Loom{" "}
)
return (
)
}
```
ShiftCard is a React card component for shadcn/ui, animated with Motion. Use it for product grids, feature lists, or team pages where a short header should expand into more detail on hover.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/shift-card
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/shift-card.json
**Update the import paths to match your project setup.**
## Usage
```tsx
function ShiftCardDemo() {
// Content for the top part of the card
const topContent = (
);
// Content that animates into top from the middle
const topAnimateContent = (
<>
>
);
// Content that animates from the top to the middle
const middleContent = (
);
// Content for the bottom part of the card that shows more details on hover
const bottomContent = (
Share your image to build that audience. Inspired by{" "}
@mrncst
Post on Twitter
{" "}
Open in Loom{" "}
);
return (
);
}
```
---
# Side Panel
> Expanding side panel for React and shadcn/ui that animates open to reveal extra content or a video, with a custom toggle.
Source: https://www.cult-ui.com/docs/components/side-panel
## Example
```tsx title="side-panel-demo.tsx"
"use client"
import React, {
createContext,
forwardRef,
ReactNode,
useContext,
useState,
} from "react"
import { AnimatePresence, motion, MotionConfig } from "motion/react"
import ReactPlayer from "react-player/lazy"
import useMeasure from "react-use-measure"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { SidePanel } from "../ui/side-panel"
// Theme Context for Styling
type ThemeContextType = {
panelClass?: string
}
// Create the context with the type
const ThemeContext = createContext({})
// Custom Hook for Measuring and Animations
const useCustomMeasure = () => {
const [ref, bounds] = useMeasure()
const animateProps = {
animate: { height: bounds.height > 0 ? bounds.height : 0.1 },
transition: { type: "spring", bounce: 0.02, duration: 0.65 },
}
return { ref, animateProps }
}
// ResizablePanel Component
type ResizablePanelProps = {
children: ReactNode
className?: string
}
// ResizablePanel Component
const ResizablePanel = forwardRef(
({ children, className, ...props }, ref) => {
const transition = {
type: "tween" as const,
ease: [0.42, 0, 0.58, 1] as const,
duration: 0.4,
}
return (
)
}
)
ResizablePanel.displayName = "ResizablePanel"
// VideoPowerButton Component
type VideoPowerButtonProps = {
handleVideoOpen: () => void
}
// VideoPowerButton Component
const VideoPowerButton = forwardRef(
({ handleVideoOpen }, ref) => {
return (
{/* SVG path here */}
)
}
)
VideoPowerButton.displayName = "VideoPowerButton"
type YoutubeVideoProps = {
videoOpen: boolean
url: string
}
const YoutubeVideo = forwardRef(
({ videoOpen, url }, ref) => {
return (
{videoOpen && (
)}
)
}
)
YoutubeVideo.displayName = "YoutubeVideo"
type VideoContentProps = {
url: string
videoOpen: boolean
}
// Define the VideoContent component
const VideoContent: React.FC = ({ url, videoOpen }) => {
// Define the animation variants
const videoVariants = {
hidden: { opacity: 0, scale: 0.9, y: 30 },
visible: { opacity: 1, scale: 1, y: 0 },
}
// Define transition properties
const transition = {
duration: 0.2,
ease: [0.04, 0.62, 0.23, 0.98] as const, // Custom cubic-bezier easing
delay: 0.3,
}
return (
{videoOpen && (
)}
)
}
// Define the main VideoSection component
type VideoSectionProps = {
videoOpen: boolean
handleVideoOpen: () => void
className?: string
videoUrl: string
children?: ReactNode // Add this line
}
const VideoSection: React.FC = ({
videoOpen,
handleVideoOpen,
className,
videoUrl,
children,
}) => {
const theme = useContext(ThemeContext)
return (
{children ? (
children
) : (
<>
>
)}
)
}
export {
VideoContent,
VideoSection,
YoutubeVideo,
VideoPowerButton,
ResizablePanel,
useCustomMeasure,
}
export default function VideoSectionDemo() {
const [videoOpen, setVideoOpen] = useState(false)
const handleVideoOpen = () => {
setVideoOpen(!videoOpen)
}
const renderVideoButton = (handleToggle: () => void) => (
video
{videoOpen ? "close" : "open"}
)
return (
)
}
```
SidePanel is a React panel component for shadcn/ui, animated with Motion. Use it for product video previews, learn more sections, or secondary details on marketing pages.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/side-panel
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/side-panel.json
**Update the import paths to match your project setup.**
## Usage
```tsx
export default function SidePanelExample() {
const [isOpen, setIsOpen] = useState(false);
const handleIsOpen = () => {
setIsOpen(!isOpen);
};
const renderOpenButton = (handleToggle: () => void) => (
Open
{isOpen ? "close" : "open"}
);
return (
);
}
```
---
# Sortable List
> Drag to reorder list for React and shadcn/ui built on Motion Reorder, with checkboxes and item deletion, for task lists.
Source: https://www.cult-ui.com/docs/components/sortable-list
## Example
```tsx title="sortable-list-demo.tsx"
"use client"
import { useCallback, useState } from "react"
import { Plus, RepeatIcon, Settings2Icon, XIcon } from "lucide-react"
import { AnimatePresence, LayoutGroup, motion } from "motion/react"
import { toast } from "sonner"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Slider } from "@/components/ui/slider"
import { DirectionAwareTabs } from "@/components/ui/direction-aware-tabs"
import SortableList, { Item, SortableListItem } from "../ui/sortable-list"
const initialState = [
{
text: "Gather Data",
checked: false,
id: 1,
description:
"Collect relevant marketing copy from the user's website and competitor sites to understand the current market positioning and identify potential areas for improvement.",
},
{
text: "Analyze Copy",
checked: false,
id: 2,
description:
"As an AI language model, analyze the collected marketing copy for clarity, persuasiveness, and alignment with the user's brand voice and target audience. Identify strengths, weaknesses, and opportunities for optimization.",
},
{
text: "Create Suggestions",
checked: false,
id: 3,
description:
"Using natural language generation techniques, create alternative versions of the marketing copy that address the identified weaknesses and leverage the opportunities for improvement. Ensure the generated copy is compelling, on-brand, and optimized for the target audience.",
},
{
text: "Recommendations",
checked: false,
id: 5,
description:
"Present the AI-generated marketing copy suggestions to the user, along with insights on why these changes were recommended. Provide a user-friendly interface for the user to review, edit, and implement the optimized copy on their website.",
},
]
function SortableListDemo() {
const [items, setItems] = useState- (initialState)
const [openItemId, setOpenItemId] = useState
(null)
const [tabChangeRerender, setTabChangeRerender] = useState(1)
const [topP, setTopP] = useState([10])
const [temp, setTemp] = useState([10])
const [tokens, setTokens] = useState([10])
const handleCompleteItem = (id: number) => {
setItems((prevItems) =>
prevItems.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
)
)
}
const handleAddItem = () => {
setItems((prevItems) => [
...prevItems,
{
text: `Item ${prevItems.length + 1}`,
checked: false,
id: Date.now(),
description: "",
},
])
}
const handleResetItems = () => {
setItems(initialState)
}
const handleCloseOnDrag = useCallback(() => {
setItems((prevItems) => {
const updatedItems = prevItems.map((item) =>
item.checked ? { ...item, checked: false } : item
)
return updatedItems.some(
(item, index) => item.checked !== prevItems[index].checked
)
? updatedItems
: prevItems
})
}, [])
const renderListItem = (
item: Item,
order: number,
onCompleteItem: (id: number) => void,
onRemoveItem: (id: number) => void
) => {
const isOpen = item.id === openItemId
const tabs = [
{
id: 0,
label: "Title",
content: (
Short title for your agent task
{
const text = e.target.value
setItems((prevItems) =>
prevItems.map((i) =>
i.id === item.id ? { ...i, text } : i
)
)
}}
/>
),
},
{
id: 1,
label: "Prompt",
content: (
Prompt{" "}
instructing your agent how to {item.text.slice(0, 20)}
),
},
{
id: 2,
label: "Settings",
content: (
AI settings for the{" "}
{item.text.slice(0, 20)} stage
),
},
]
return (
(
setOpenItemId(!isOpen ? item.id : null)}
key="collapse"
className={cn(
isOpen
? "absolute top-3 right-3 z-10"
: "relative z-10 mr-3 ml-auto"
)}
>
{isOpen ? (
) : (
)}
{isOpen ? (
setTabChangeRerender(tabChangeRerender + 1)
}
/>
Changes
{
setOpenItemId(null)
toast.info("Changes saved")
}}
className="h-7 rounded-lg bg-[#13EEE3]/80 text-black hover:bg-[#13EEE3] hover:text-black"
>
Apply Changes
) : null}
)}
/>
)
}
return (
5} onClick={handleAddItem}>
)
}
export default SortableListDemo
```
SortableList is an animated React list component for shadcn/ui built on the Motion Reorder API. Use it for task lists, playlist editors, onboarding checklists, or any interface where users rearrange items.
## References
Inspiration
Dynamic Settings by Mariana Castilho
Follow @mrncst on Twitter
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/sortable-list
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/sortable-list.json
**Update the import paths to match your project setup.**
## Usage
```tsx
export default function Example() {
const [items, setItems] = useState- ([
{ text: "Item 1", checked: false, id: 1, description: "Description 1" },
{ text: "Item 2", checked: false, id: 2, description: "Description 2" },
{ text: "Item 3", checked: false, id: 3, description: "Description 3" },
]);
const handleAddItem = () => {
const newItem: Item = {
text: `Item ${items.length + 1}`,
checked: false,
id: items.length + 1,
description: `Description ${items.length + 1}`,
};
setItems([...items, newItem]);
};
const handleResetItems = () => {
setItems([]);
};
const handleCompleteItem = (id: number) => {
setItems((prevItems) =>
prevItems.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
)
);
};
const handleRemoveItem = (id: number) => {
setItems((prevItems) => prevItems.filter((item) => item.id !== id));
};
const renderItem = (
item: Item,
onCompleteItem: (id: number) => void,
onRemoveItem: (id: number) => void
) => (
{}}
/>
);
return (
Sortable List Example
);
}
```
---
# Speech Bubble
> Composable chat speech bubble for React and shadcn/ui with an SVG tail and optional cursor, for AI messages and product tours.
Source: https://www.cult-ui.com/docs/components/speech-bubble
## Example
```tsx title="speech-bubble-demo.tsx"
"use client"
import { SpeechBubble } from "@/components/ui/speech-bubble"
function SpeechBubbleDemo() {
return (
Hello, how are you?
No cursor variant
)
}
export default SpeechBubbleDemo
```
Speech Bubble is a composable React component for shadcn/ui. Use it for AI assistant messages, onboarding hints, product tours, or character dialogue on landing pages.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/speech-bubble
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/speech-bubble.json
**Update the import paths to match your project setup.**
---
# Squiggle Arrow
> Hand-drawn squiggly SVG arrow for React and shadcn/ui in wavy, bouncy, or smooth variants, for pointing at calls to action.
Source: https://www.cult-ui.com/docs/components/squiggle-arrow
## Example
```tsx title="squiggle-arrow-demo.tsx"
import SquigglyArrow from "@/components/ui/squiggle-arrow"
function SquigglyArrowDemo() {
return (
)
}
export default SquigglyArrowDemo
```
SquiggleArrow is a React SVG component for shadcn/ui. Use it to point at a call to action, annotate screenshots, or add informal callouts to landing pages and product walkthroughs.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/squiggle-arrow
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/squiggle-arrow.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import SquigglyArrow from "@/components/ui/squiggle-arrow";
```
### Basic Usage
```tsx
```
### Variants
The component supports three different variants with unique squiggle patterns:
```tsx
// Wavy variant (default)
// Bouncy variant
// Smooth variant
```
### Directions
Control the arrow direction:
```tsx
// Right (default)
// Left
// Up
// Down
```
### Custom Sizes
Adjust the arrow dimensions and stroke width:
```tsx
// Small arrow
// Medium arrow
// Large arrow
```
### Custom Colors
Use Tailwind classes to customize the arrow color:
```tsx
```
## API Reference
### SquigglyArrow Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `width` | `number` | `200` | Width of the SVG arrow |
| `height` | `number` | `100` | Height of the SVG arrow |
| `strokeWidth` | `number` | `2.5` | Thickness of the arrow stroke |
| `className` | `string` | - | Additional CSS classes |
| `direction` | `"right" \| "left" \| "up" \| "down"` | `"right"` | Direction the arrow points |
| `variant` | `"wavy" \| "bouncy" \| "smooth"` | `"wavy"` | Visual style of the squiggle pattern |
## Examples
### Variant Showcase
```tsx
```
### Direction Showcase
```tsx
```
### Size Variations
```tsx
```
### Call-to-Action Arrow
```tsx
Learn more
```
### Decorative Arrow
```tsx
Check this out
Scroll down for more
```
---
# Stripe Background Guides
> Stripe-style animated grid line background for React and shadcn/ui with glowing highlights, column count, and direction options.
Source: https://www.cult-ui.com/docs/components/stripe-bg-guides
## Example
```tsx title="stripe-bg-guides-demo.tsx"
"use client"
import { StripeBgGuides } from "../ui/stripe-bg-guides"
export default function StripeBgGuidesDemo() {
return (
{/* Modified StripeBgGuides to work within a container */}
{/* Content to demonstrate the background effect */}
Stripe Background Guides
Animated background guides with glowing effects, inspired by
Stripe's design system.
)
}
```
StripeBgGuides is a React background component for shadcn/ui, animated with Motion. Configure column count, direction, easing, and randomized highlights for landing page heroes, marketing sections, or documentation headers.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/stripe-bg-guides
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/stripe-bg-guides.json
**Update the import paths to match your project setup.**
## Usage
```tsx
export default function StripeBgGuideDemo() {
return (
{/* Modified StripeBgGuides to work within a container */}
{/* Content to demonstrate the background effect */}
Stripe Background Guides
Animated background guides with glowing effects, inspired by
Stripe's design system.
);
}
```
---
# SVG Bands
> Wide SVG section dividers for React and shadcn/ui, including battlements, zigzags, hero bands, wavy footers, and notched frames.
Source: https://www.cult-ui.com/docs/components/svg-bands
## Example
```tsx title="svg-bands-demo.tsx"
"use client"
import type { ReactNode } from "react"
import {
AngledCornerPanelSvg,
ArrowChevronBandSvg,
BandShape,
BottomBandSteepRightSvg,
castleWallBandPath,
CastleWallBandSvg,
CenterTabBandSvg,
CenterTabTopBandSvg,
DiagonalSlashBandSvg,
DualSideNotchFrameSvg,
FlatSingleAngleBandSvg,
NotchedTopBandSvg,
PyramidStepBandLotsOfStepsSvg,
PyramidStepBandSvg,
StaircaseBandSvg,
SteppedInsetFrameSvg,
SteppedLeftSteepRightBandSvg,
SymmetricVBandSvg,
TallAngledHeroPanelSvg,
TopBandSteepBothSvg,
TopBandSteepLeftSvg,
TopBandSteepRightSvg,
WavyBottomEdgeSvg,
zigzagSawtoothBandPath,
ZigzagSawtoothBandSvg,
} from "@/components/ui/svg-bands"
function DemoSection({
title,
children,
}: {
title: string
children: ReactNode
}) {
return (
)
}
function BandTile({
label,
tall,
children,
}: {
label: string
tall?: boolean
children: ReactNode
}) {
return (
)
}
export default function SvgBandsDemo() {
return (
SVG bands
Wide (2400-based) section ornaments: battlement and zigzag trims,
steep hero headers, and frame panels. Styling follows{" "}
currentColor
{" "}
with light fill tints — pair with your background and type color.
)
}
```
SVG Bands is a set of React SVG components for shadcn/ui. Use them as dividers between landing page sections, hero bottoms, or footer edges, with flip options for orientation.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/svg-bands
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/svg-bands.json
**Update the import paths to match your project setup.**
## Usage
Use preset components for common silhouettes, or **`BandShape`** with your own closed **`path`** and **`viewBox`**. Several exports expose **path builder functions** (`castleWallBandPath`, `zigzagSawtoothBandPath`) if you need different merlon or tooth counts without duplicating geometry.
```tsx
import {
TopBandSteepRightSvg,
BandShape,
castleWallBandPath,
} from "@/components/ui/svg-bands";
export function SectionDivider() {
return (
);
}
export function CustomBattlement() {
return (
);
}
```
### `BandShape` props
| Prop | Type | Default |
| --- | --- | --- |
| `path` | `string` | (required) |
| `viewBox` | `string` | (required) |
| `flip` | `"none"` \| `"vertical"` \| `"horizontal"` \| `"both"` | `"none"` |
| `className` | `string` | — |
| `title` | `string` | `"Decorative section shape"` |
Preset components are mostly **`className` only**; **`ZigzagSawtoothBandSvg`** accepts optional **`teeth`** (default `12`, must be ≥ 2).
### Path helpers
| Export | Notes |
| --- | --- |
| `castleWallBandPath(n)` | `n` ≥ 1 merlons along the 2400-wide grid |
| `zigzagSawtoothBandPath(n)` | `n` ≥ 2 teeth; pairs with default zigzag viewBox height |
## Notes
- Ornamental exports (`DualSideNotchFrameSvg`, `WavyBottomEdgeSvg`, `CenterTabTopBandSvg`) bundle responsive Tailwind classes for small breakpoints; override with `className` if needed.
- **`BottomBandSteepRightSvg`** already applies a vertical flip so the steep geometry reads as a footer trim.
---
# SVG Shapes
> Static SVG shapes for React and shadcn/ui, including polygons, sacred geometry, and Platonic wireframes styled with currentColor.
Source: https://www.cult-ui.com/docs/components/svg-shapes
## Example
```tsx title="svg-shapes-demo.tsx"
"use client"
import type { ReactNode } from "react"
import {
ArrowPentagonShapeSvg,
CircleShapeSvg,
CubeWireshapeSvg,
DodecahedronWireshapeSvg,
EllipseShapeSvg,
FlowerOfLifeSvg,
GoldenSpiralSvg,
GothicArchPanelSvg,
IcosahedronWireshapeSvg,
KiteShapeSvg,
MetatronsCubeSvg,
OctahedronWireshapeSvg,
PennantShapeSvg,
QuarterCircleShapeSvg,
RightTriangleShapeSvg,
RoundedSquareShapeSvg,
RoundedTriangleShapeSvg,
SeedOfLifeSvg,
SemicircleShapeSvg,
SingleBracketShapeSvg,
SquircleShapeSvg,
SriYantraSvg,
TetrahedronWireshapeSvg,
TorusTubeSvg,
TriangleShapeSvg,
VesicaPiscisSvg,
} from "@/components/ui/svg-shapes"
function DemoSection({
title,
children,
}: {
title: string
children: ReactNode
}) {
return (
)
}
function ShapeTile({
label,
tall,
children,
}: {
label: string
tall?: boolean
children: ReactNode
}) {
return (
)
}
export default function SvgShapesDemo() {
return (
SVG shapes
Wide viewBox line-art panels for landing pages: polygons,
architectural silhouettes, sacred geometry, and Platonic wire
projections. Shape via{" "}
currentColor
{" "}
and light fill tints; pass{" "}
flip to
mirror without editing paths.
Flip prop
{(
[
["none", "none"],
["vertical", "vertical"],
["horizontal", "horizontal"],
["both axes", "both"],
] as const
).map(([label, flip]) => (
))}
)
}
```
SVG Shapes is a library of static React SVG components for shadcn/ui. Use them as hero art, section backgrounds, or decorative frames that inherit your text color.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/svg-shapes
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/svg-shapes.json
**Update the import paths to match your project setup.**
## Usage
Import only the shapes you need. Each export is a React component that renders an `` with a shared wide viewBox (typically 2400×800–1200).
```tsx
import {
TriangleShapeSvg,
GothicArchPanelSvg,
FlowerOfLifeSvg,
} from "@/components/ui/svg-shapes";
export function HeroStrip() {
return (
);
}
```
### Props
| Prop | Type | Default |
| --- | --- | --- |
| `className` | `string` | — |
| `flip` | `"none"` \| `"vertical"` \| `"horizontal"` \| `"both"` | `"none"` |
Stroke and fill use `currentColor` (and tinted fills on filled shapes) so you control appearance with Tailwind classes on a wrapper.
## Notes
- Line-art exports (e.g. bracket, wire solids) use `fill-none` so only strokes show.
- For localized `fixed` or transform behavior, wrap in a container with `transform-gpu` / `isolate` like other full-bleed effects.
---
# SVG Shapes Animated
> Scroll-animated SVG shapes and panels for React and shadcn/ui whose strokes draw in with Motion, with reduced motion support.
Source: https://www.cult-ui.com/docs/components/svg-shapes-animated
## Example
```tsx title="svg-shapes-animated-demo.tsx"
"use client"
import { useState, type ReactNode } from "react"
import { Button } from "@/components/ui/button"
import {
ArrowPentagonShapeSvg,
ChevronShapeSvg,
CircleShapeSvg,
CrossShapeSvg,
CubeWireframeSvg,
DiamondShapeSvg,
DodecahedronWireframeSvg,
DoubleNotchTechShapeSvg,
EllipseShapeSvg,
FlowerOfLifeSvg,
GoldenSpiralSvg,
GothicArchPanelSvg,
HourglassBowtiePanelSvg,
IcosahedronWireframeSvg,
InvertedArchPanelSvg,
KiteShapeSvg,
MetatronsCubeSvg,
OctagonShapeSvg,
OctahedronWireframeSvg,
OgeeOnionDomePanelSvg,
ParallelogramShapeSvg,
PennantShapeSvg,
PillShapeSvg,
QuarterCircleShapeSvg,
RibbonBannerShapeSvg,
RightTriangleShapeSvg,
RoundedRectCornerBiteShapeSvg,
RoundedSquareShapeSvg,
RoundedTriangleShapeSvg,
ScallopedTopPanelSvg,
SeedOfLifeSvg,
SemicircleShapeSvg,
SquircleShapeSvg,
SriYantraSvg,
TetrahedronWireframeSvg,
TorusTubeSvg,
TrapezoidShapeSvg,
TriangleShapeSvg,
VesicaPiscisSvg,
} from "@/components/ui/svg-shapes-animated"
function DemoSection({
title,
children,
}: {
title: string
children: ReactNode
}) {
return (
)
}
function ShapeTile({
label,
tall,
children,
}: {
label: string
tall?: boolean
children: ReactNode
}) {
return (
)
}
export default function SvgShapesAnimatedDemo() {
const [replayKey, setReplayKey] = useState(0)
return (
SVG shapes (animated)
Same wide viewBox geometry as the static set, with paths drawn via{" "}
motion/react
{" "}
when each graphic scrolls into view. Respects{" "}
prefers-reduced-motion
.
setReplayKey((k) => k + 1)}
>
Replay animations
setReplayKey((k) => k + 1)}
>
Replay animations
setReplayKey((k) => k + 1)}
>
Replay animations
setReplayKey((k) => k + 1)}
>
Replay animations
setReplayKey((k) => k + 1)}
>
Replay animations
setReplayKey((k) => k + 1)}
>
Replay animations
Flip prop
{(
[
["none", "none"],
["vertical", "vertical"],
["horizontal", "horizontal"],
["both axes", "both"],
] as const
).map(([label, flip]) => (
))}
)
}
```
SVG Shapes Animated is a library of React SVG shapes for shadcn/ui, animated with Motion. Use it for landing page heroes, section backgrounds, and scroll-driven storytelling where geometry should animate into view.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/svg-shapes-animated
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/svg-shapes-animated.json
**Update the import paths to match your project setup.**
## Usage
Each export is a client component (`"use client"`) that wraps stroked paths in `motion` elements. Animations use `whileInView` with `pathLength` from 0 → 1 so lines draw when the SVG enters the viewport. `useReducedMotion()` skips the animation and shows finished paths immediately.
```tsx
import { GothicArchPanelSvg } from "@/components/ui/svg-shapes-animated";
export function Hero() {
return (
);
}
```
### Props
| Prop | Type | Default |
| --- | --- | --- |
| `className` | `string` | — |
| `flip` | `"none"` \| `"vertical"` \| `"horizontal"` \| `"both"` | `"none"` |
Requires the **motion** package (`motion/react`), declared as a registry dependency when installing via CLI.
### Compared to SVG Shapes
- **Animated**: scroll-draw strokes, slightly different default stroke emphasis, extra shapes (diamond, trapezoid, octagon, ribbon panels, wireframe naming `*WireframeSvg`, etc.).
- **Static** (`svg-shapes`): no Motion dependency, passive SVGs suitable anywhere including server components.
## Notes
- Sacred-geometry and multi-path shapes stagger short delays per sub-path for a sequential draw feel.
- Ensure the preview or page scrolls enough for elements to cross the in-view threshold; embed in a tall section or scroll container as needed.
---
# Tabs Illustration Vercel
> Auto-advancing feature tabs for React and shadcn/ui with Vercel-style animated illustrations and a progress indicator.
Source: https://www.cult-ui.com/docs/components/tabs-illustration-vercel
## Example
```tsx title="tabs-illustration-vercel-demo.tsx"
"use client"
import GenerateAnything from "@/components/ui/tabs-illustration-vercel"
function TabsIllustrationVercelDemo() {
return (
)
}
export default TabsIllustrationVercelDemo
```
Tabs Illustration Vercel is a React tabs component for shadcn/ui. Use it on AI product landing pages or feature sections that showcase several capabilities.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/tabs-illustration-vercel
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/tabs-illustration-vercel.json
**Update the import paths to match your project setup.**
---
# Terminal Animation
> Composable animated terminal for React and shadcn/ui that plays typed commands and output, with tabbed scenarios for CLI docs.
Source: https://www.cult-ui.com/docs/components/terminal-animation
## Example
```tsx title="terminal-animation-demo.tsx"
"use client"
import { useState } from "react"
import { cn } from "@/lib/utils"
import {
TerminalAnimationBackgroundGradient,
TerminalAnimationBlinkingCursor,
TerminalAnimationCommandBar,
TerminalAnimationContainer,
TerminalAnimationContent,
TerminalAnimationOutput,
TerminalAnimationRoot,
TerminalAnimationTabList,
TerminalAnimationTabTrigger,
TerminalAnimationTrailingPrompt,
TerminalAnimationWindow,
type TabContent,
type TerminalLine,
} from "../ui/terminal-animation"
export interface TerminalAnimationDemoProps {
/** Tab content for each command; defaults to defaultTerminalTabs */
tabs?: TabContent[]
/** Background image URL; when unset, BackgroundGradient is used */
backgroundImage?: string
/** Force dark mode for the terminal regardless of page theme */
alwaysDark?: boolean
}
const backgroundImage =
"/component-images/terminal-animation/terminal-animation-bg-2.png"
const tabs: TabContent[] = [
{
label: "install",
command: "npm install",
lines: [
{ text: "", delay: 80 },
{
text: "added 1,247 packages in 12s",
color: "text-[#6FF7CC]",
delay: 400,
},
{ text: "", delay: 80 },
{
text: " Cult UI is looking for funding",
color: "text-slate-400",
delay: 150,
},
{
text: " run `npm fund cult-ui` for details",
color: "text-slate-500",
delay: 100,
},
{
text: " +-----------------------+",
color: "text-[#ED42B5]",
delay: 120,
},
{
text: " | CULT UI |",
color: "text-[#ED42B5]",
delay: 120,
},
{
text: " | Shadcn expanded |",
color: "text-[#ED42B5]",
delay: 120,
},
{
text: " +-----------------------+",
color: "text-[#ED42B5]",
delay: 160,
},
{ text: "", delay: 80 },
{
text: " found 0 vulnerabilities",
color: "text-[#ADFA1F]",
delay: 250,
},
],
},
{
label: "build",
command: "npm run build",
lines: [
{ text: "", delay: 80 },
{
text: " ▲ Next.js 16.1.6",
color: "text-slate-300",
delay: 300,
},
{ text: "", delay: 80 },
{
text: " Creating an optimized production build...",
color: "text-slate-400",
delay: 250,
},
{
text: " ✓ Compiled successfully",
color: "text-[#6FF7CC]",
delay: 200,
},
{
text: " ✓ Linting and checking validity of types",
color: "text-[#6FF7CC]",
delay: 150,
},
{
text: " ✓ Generating static pages (12/12)",
color: "text-[#6FF7CC]",
delay: 150,
},
{
text: " Route (app) / 142 kB | First Load JS 198 kB",
color: "text-slate-500",
delay: 150,
},
{
text: " Route (app) /blog 61 kB | First Load JS 57 kB",
color: "text-slate-500",
delay: 150,
},
{
text: " Route (app) /about 75 kB | First Load JS 92 kB",
color: "text-slate-500",
delay: 150,
},
{ text: "", delay: 80 },
{
text: " ✓ Build completed in 4.2s",
color: "text-[#6FF7CC]",
delay: 300,
},
],
},
{
label: "deploy",
command: "vercel deploy --prod",
lines: [
{ text: "", delay: 80 },
{ text: " Vercel CLI 39.2.0", color: "text-slate-400", delay: 200 },
{ text: "", delay: 80 },
{
text: " > Deploying to production...",
color: "text-[#ED42B5]",
delay: 300,
},
{ text: "", delay: 80 },
{ text: " ✓ Building", color: "text-[#6FF7CC]", delay: 250 },
{ text: " ✓ Uploading", color: "text-[#6FF7CC]", delay: 200 },
{ text: " ✓ Finalizing", color: "text-[#6FF7CC]", delay: 200 },
{ text: "", delay: 80 },
{
text: " Production: https://aisdkagents.vercel.app",
color: "text-[#ED42B5]",
delay: 400,
},
{ text: "", delay: 80 },
{
text: " ✓ Deployment complete",
color: "text-[#6FF7CC]",
delay: 250,
},
],
},
{
label: "test",
command: "npm test",
lines: [
{ text: "", delay: 80 },
{
text: " PASS src/components/Button.test.tsx",
color: "text-slate-400",
delay: 200,
},
{
text: " ✓ renders correctly",
color: "text-[#ADFA1F]",
delay: 100,
},
{
text: " ✓ handles click events",
color: "text-[#ADFA1F]",
delay: 100,
},
{
text: " PASS src/utils/format.test.ts",
color: "text-slate-400",
delay: 150,
},
{
text: " ✓ formats currency",
color: "text-[#ADFA1F]",
delay: 100,
},
{
text: " ✓ formats dates",
color: "text-[#ADFA1F]",
delay: 100,
},
{ text: "", delay: 80 },
{
text: " Test Suites: 2 passed, 2 total",
color: "text-[#ADFA1F]",
delay: 200,
},
{
text: " Tests: 4 passed, 4 total",
color: "text-[#ADFA1F]",
delay: 150,
},
{ text: " Time: 1.234 s", color: "text-slate-500", delay: 100 },
],
},
]
export function TerminalAnimationDemo() {
const [animationKey, setAnimationKey] = useState(0)
return (
setAnimationKey((prev) => prev + 1)}
type="button"
>
Refresh
Download BG
{!backgroundImage && }
$
}
/>
{
if (!visible) {
return null
}
return (
{line.text || "\u00A0"}
)
}}
/>
$
{tabs.map((tab, i) => (
{tab.label}
))}
)
}
```
Terminal Animation is a set of composable React primitives for shadcn/ui with controlled or uncontrolled state. Use it for CLI documentation, developer tool landing pages, or install guides.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/terminal-animation
```
### Manual
**Install the required dependencies.**
```bash
npm install @radix-ui/react-slot @radix-ui/react-use-controllable-state
```
**Copy and paste the component source into your project.**
Full source: https://www.cult-ui.com/r/terminal-animation.json
**Optionally copy the demo as a starting point.**
Full source: https://www.cult-ui.com/r/terminal-animation-demo.json
**Add this cursor blink utility to your global stylesheet (for example,{" "}
globals.css).**
```css
@keyframes caret-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
@utility animate-caret-blink {
animation: caret-blink 1s step-end infinite;
}
```
**Update import paths to match your project setup.**
## Usage
```tsx
import {
TerminalAnimationRoot,
TerminalAnimationContainer,
TerminalAnimationWindow,
TerminalAnimationContent,
TerminalAnimationCommandBar,
TerminalAnimationOutput,
TerminalAnimationTabList,
TerminalAnimationTabTrigger,
type TabContent,
} from "@/components/ui/terminal-animation";
const tabs: TabContent[] = [
{
label: "dev",
command: "npm run dev",
lines: [
{ text: " ▲ Next.js 16.1.6", delay: 300 },
{
text: " - Local: http://localhost:3000",
delay: 200,
color: "text-cyan-400",
},
],
},
];
```
```tsx
{tabs.map((tab, index) => (
{tab.label}
))}
```
## Controlled Tabs
Use `activeTab` + `onActiveTabChange` when tab state is managed externally:
```tsx
const [activeTab, setActiveTab] = useState(1)
{/* terminal layout */}
```
## Custom Output Rendering
Use `renderLine` to fully control how each output line appears:
```tsx
{
if (!visible) return null;
return (
{String(index + 1).padStart(2, "0")}
{line.text || "\u00A0"}
);
}}
/>
```
## API Reference
### Data Types
#### `TerminalLine`
| Property | Type | Description |
| -------- | -------- | ----------------------------------------- |
| `text` | `string` | Text content for an output line |
| `color` | `string` | Optional utility class for line color |
| `delay` | `number` | Delay (ms) before revealing the next line |
#### `TabContent`
| Property | Type | Description |
| --------- | ---------------- | ------------------------------------------ |
| `label` | `string` | Tab label |
| `command` | `string` | Command text to animate in the prompt |
| `lines` | `TerminalLine[]` | Output lines revealed after command typing |
### `TerminalAnimationRoot`
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tabs` | `TabContent[]` | - | Data source for command tabs and output |
| `defaultActiveTab` | `number` | `0` | Initial tab index (uncontrolled mode) |
| `activeTab` | `number` | - | Controlled active tab index |
| `onActiveTabChange` | `(index: number) => void` | - | Called when tab changes |
| `backgroundImage` | `string` | - | Optional full-bleed background image URL |
| `alwaysDark` | `boolean` | `false` | Forces dark terminal theme regardless of page theme |
### `TerminalAnimationWindow`
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `backgroundColor` | `string` | - | Inline background color override |
| `minHeight` | `string` | `"28rem"` | Minimum height of the terminal area |
| `animateOnVisible` | `boolean` | `true` | Plays slide-up transition when entering viewport |
### `TerminalAnimationCommandBar`
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `cursor` | `ReactNode` | block cursor | Custom cursor while command is typing |
### `TerminalAnimationOutput`
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `renderLine` | `(line, index, visible) => ReactNode` | - | Custom renderer for each output line |
### `TerminalAnimationTabTrigger`
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `index` | `number` | - | Target tab index |
| `asChild` | `boolean` | `false` | Renders via `Slot` to merge props into a custom child |
## Exports
The component includes a full set of primitives for composition:
- `TerminalAnimationRoot`
- `TerminalAnimationBackgroundGradient`
- `TerminalAnimationContainer`
- `TerminalAnimationWindow`
- `TerminalAnimationContent`
- `TerminalAnimationBlinkingCursor`
- `TerminalAnimationCommandBar`
- `TerminalAnimationOutput`
- `TerminalAnimationOutputLine`
- `TerminalAnimationTrailingPrompt`
- `TerminalAnimationTabList`
- `TerminalAnimationTabTrigger`
- `useTerminalAnimation`
- `defaultTerminalTabs`
---
# Text Animate
> Text animation for React and shadcn/ui built with Motion, with presets like fadeIn, rollIn, and whipInUp for headlines.
Source: https://www.cult-ui.com/docs/components/text-animate
## Example
```tsx title="text-animate-demo.tsx"
"use client"
import { useRef, useState } from "react"
import { motion, useInView } from "motion/react"
import { FadeIn } from "@/components/fade-in"
import TextAnimate from "../ui/text-animate"
// @ts-ignore
const AnimationDemo = ({ type, children }) => {
const ref = useRef(null)
const isInView = useInView(ref, { once: true })
const [count, setCount] = useState(0)
return (
{children}
setCount(count + 1)} />
setCount(count + 1)} />
)
}
const button = {
rest: { scale: 1 },
hover: { scale: 1.1 },
pressed: { scale: 0.95 },
}
const arrow = {
rest: { rotate: 0 },
hover: { rotate: 360, transition: { duration: 0.4 } },
}
// @ts-ignore
const Refresh = ({ onClick }) => {
return (
)
}
export default function TextAnimationDemo() {
return (
)
}
```
TextAnimate is a React text animation component for shadcn/ui built with Motion. Use it for landing page headlines, section titles, and announcement banners that should animate when scrolled into view.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/text-animate
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/text-animate.json
**Update the import paths to match your project setup.**
## Usage
```tsx
```
---
# Text GIF
> Display text for React and shadcn/ui clipped to an animated GIF background, with size, weight, and font variants.
Source: https://www.cult-ui.com/docs/components/text-gif
## Example
```tsx title="text-gif-demo.tsx"
"use client"
import { useState } from "react"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { TextGif } from "../ui/text-gif"
export default function TextGifDemo() {
const [text, setText] = useState("TextGif")
const [size, setSize] = useState("xl")
const [weight, setWeight] = useState("bold")
const gifUrls = [
"https://media.giphy.com/media/3zvbrvbRe7wxBofOBI/giphy.gif",
"https://media.giphy.com/media/fnglNFjBGiyAFtm6ke/giphy.gif",
"https://media.giphy.com/media/9Pmfazv34l7aNIKK05/giphy.gif",
"https://media.giphy.com/media/4bhs1boql4XVJgmm4H/giphy.gif",
]
const [selectedGif, setSelectedGif] = useState(gifUrls[0])
return (
{/* Preview */}
{/* Controls */}
Text
setText(e.target.value)}
placeholder="Enter text"
/>
GIF Background
{gifUrls.map((gif, index) => (
GIF {index + 1}
))}
Text Size
{["sm", "md", "lg", "xl", "xxl"].map((s) => (
{s}
))}
Font Weight
{["normal", "medium", "semi", "bold"].map((w) => (
{w}
))}
{/* Examples */}
)
}
```
TextGif is a React text component for shadcn/ui with a fallback color. Use it for landing page headlines, event promos, or playful brand titles.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/text-gif
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/text-gif.json
**Update the import paths to match your project setup.**
---
# Texture Button
> Tactile neumorphic button for React and shadcn/ui with layered borders and gradients, multiple variants and sizes, and asChild.
Source: https://www.cult-ui.com/docs/components/texture-button
## Example
```tsx title="texture-button-demo.tsx"
"use client"
import { ChevronLeft, Trash, X } from "lucide-react"
import { TextureButton } from "@/components/ui/texture-button"
export default function TextureButtonDemo() {
return (
Secondary
Secondary
Secondary
Destructive
Destructive
Destructive
)
}
```
TextureButton is a React button component for shadcn/ui built on Radix Slot. Use it for primary and secondary actions in forms, dialogs, and dashboards with a tactile visual style.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/texture-button
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/texture-button.json
**Update the import paths to match your project setup.**
## Usage
```tsx
{/* GithubIcon */}
Github
```
---
# Texture Card
> Composable neumorphic card for React and shadcn/ui with layered borders and header, content, footer, and separator parts.
Source: https://www.cult-ui.com/docs/components/texture-card
## Example
```tsx title="texture-card-demo.tsx"
"use client"
import { ArrowRight, Merge } from "lucide-react"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { TextureButton } from "@/components/ui/texture-button"
import {
TextureCardContent,
TextureCardFooter,
TextureCardHeader,
TextureCardStyled,
TextureCardTitle,
TextureSeparator,
} from "@/components/ui/texture-card"
export default function TextureCardDemo() {
return (
Create your account
Welcome! Please fill in the details to get started.
{/* Google Icon */}
Google
{/* GithubIcon */}
Github
or
Already have an account?{" "}
Sign in
)
}
```
TextureCard is a composable React card component for shadcn/ui. Use it for settings panels, sign-in forms, pricing tiers, or dashboard widgets that need a tactile surface.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/texture-card
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/texture-card.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
TextureCard,
TextureCardContent,
TextureCardFooter,
TextureCardHeader,
TextureCardTitle,
TextureSeparator,
} from "@/components/ui/texture-card";
```
```tsx
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum eos quia
incidunt perspiciatis, ut, deleniti fugit a aliquam sequi, voluptatum
pariatur quaerat. Temporibus sed facere at, voluptas dolorem officiis
incidunt!
```
---
# Texture Overlay
> CSS texture overlay for React and shadcn/ui with dots, grid, crosshatch, halftone, and paper grain patterns at any opacity.
Source: https://www.cult-ui.com/docs/components/texture-overlay
## Example
```tsx title="texture-overlay-demo.tsx"
"use client"
import { TextureOverlay } from "@/components/ui/texture-overlay"
export default function TextureOverlayDemo() {
return (
Texture Overlay Showcase
Explore different texture patterns using CSS gradients for adding
visual texture to backgrounds and surfaces.
{[
{ type: "dots" as const, name: "Dots Pattern" },
{ type: "grid" as const, name: "Grid Pattern" },
{ type: "noise" as const, name: "Noise Pattern" },
{ type: "crosshatch" as const, name: "Crosshatch Pattern" },
{ type: "diagonal" as const, name: "Diagonal Pattern" },
{ type: "scatteredDots" as const, name: "Scattered Dots" },
{ type: "halftone" as const, name: "Halftone Pattern" },
{ type: "triangular" as const, name: "Triangular Pattern" },
{ type: "chevron" as const, name: "Chevron Pattern" },
{ type: "paperGrain" as const, name: "Paper Grain" },
{ type: "horizontalLines" as const, name: "Horizontal Lines" },
{ type: "verticalLines" as const, name: "Vertical Lines" },
].map((texture) => (
{texture.name}
{texture.type} texture
))}
Opacity Variations
See how different opacity values affect the texture appearance
{[0.3, 0.6, 1.0].map((opacity) => (
Dots Pattern
Opacity: {opacity}
))}
Custom Styling
Combine with custom classes for unique effects
Grid with Blend Mode
mix-blend-overlay
Noise with Custom Opacity
opacity-50 class
)
}
```
Texture Overlay is a React component for shadcn/ui built on pure CSS gradients. Use it to add subtle texture to hero backgrounds, cards, and section dividers.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/texture-overlay
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/texture-overlay.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import { TextureOverlay } from "@/components/ui/texture-overlay";
```
### Basic Usage
```tsx
```
### With Custom Styling
```tsx
Content with texture overlay
```
## API Reference
### Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `texture` | `TextureType` | - | The type of texture pattern to display |
| `opacity` | `number` | `defaultOpacities[texture]` | The opacity of the texture overlay (0-1) |
| `className` | `string` | - | Additional CSS classes to apply |
### TextureType
The `TextureType` union includes the following options:
- `"dots"` - Small circular dots pattern
- `"grid"` - Grid lines pattern
- `"noise"` - Random noise pattern
- `"crosshatch"` - Crosshatch pattern
- `"diagonal"` - Diagonal lines pattern
- `"radialDots"` - Radial dots from center
- `"scatteredDots"` - Scattered dots pattern
- `"halftone"` - Halftone dots pattern
- `"triangular"` - Triangular pattern
- `"chevron"` - Chevron pattern
- `"paperGrain"` - Paper grain texture
- `"horizontalLines"` - Horizontal lines
- `"verticalLines"` - Vertical lines
- `"none"` - No texture (component returns null)
## Examples
### Card with Texture Background
```tsx
Card Title
Card content with subtle texture
```
### Hero Section with Texture
```tsx
Hero Title
Hero subtitle with texture background
```
### Multiple Texture Layers
```tsx
Layered Textures
Combining multiple texture patterns
```
## Styling
The component uses CSS gradients to create texture patterns. You can customize the appearance by:
- Adjusting the `opacity` prop for transparency
- Adding custom `className` for additional styling
- Using CSS blend modes like `mix-blend-overlay` or `mix-blend-multiply`
- Combining multiple `TextureOverlay` components for layered effects
## Performance
The component is optimized for performance by:
- Using CSS gradients instead of images
- Minimal DOM footprint (single div element)
- No JavaScript animations or calculations
- Lightweight and fast rendering
## Accessibility
The component is designed to be accessible:
- Uses `pointer-events-none` to ensure it doesn't interfere with user interactions
- Positioned absolutely to not affect document flow
- Can be safely used as decorative elements
---
# 3D Carousel
> 3D image carousel for React and shadcn/ui that rotates cards around a cylinder with drag momentum and click to expand.
Source: https://www.cult-ui.com/docs/components/three-d-carousel
## Example
```tsx title="three-d-carousel-demo.tsx"
import ThreeDPhotoCarousel from "../ui/three-d-carousel"
export default function ThreeDPhotoCarouselDemo() {
return (
)
}
```
3D Carousel is a React image carousel for shadcn/ui built with Motion. Use it for photo galleries, portfolio showcases, or product image browsing on landing pages.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/three-d-carousel
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/three-d-carousel.json
**Update the import paths to match your project setup.**
## Usage
```tsx
export default function ThreeDPhotoCarouselDemo() {
return (
);
}
```
---
# Timer
> Compound timer for React and shadcn/ui with variants, sizes, and time formats, plus a useTimer hook for elapsed time.
Source: https://www.cult-ui.com/docs/components/timer
## Example
```tsx title="timer-demo.tsx"
"use client"
import { Pause, Play, RotateCcw } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Timer,
TimerDisplay,
TimerIcon,
TimerRoot,
useTimer,
} from "@/components/ui/timer"
export default function TimerExamples() {
return (
{/* Basic Timer */}
Basic Timer
{/* Variants */}
{/* Sizes */}
{/* Compound Components */}
Compound Components
{/* Custom Timer with Controls */}
)
}
function CustomTimerExample() {
const { formattedTime, isRunning, start, stop, reset } = useTimer({
format: "MM:SS",
})
return (
Custom Timer with Controls
)
}
```
Timer is a React timer component system for shadcn/ui with compound parts and a useTimer hook. Use it for AI generation progress, recording indicators, workouts, or any interface that shows how long something has been running.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/timer
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/timer.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Timer,
TimerDisplay,
TimerIcon,
TimerRoot,
useTimer,
} from "@/components/ui/timer";
```
### Basic Timer
```tsx
```
### Compound Components
For more flexibility, you can use the individual components:
```tsx
```
### Timer with Custom Format
```tsx
```
### Timer with Callback
```tsx
const [isLoading, setIsLoading] = useState(false);
const handleTick = (seconds: number, milliseconds: number) => {
console.log(`Elapsed: ${seconds}.${milliseconds}s`);
};
return (
setIsLoading(!isLoading)}>
{isLoading ? "Stop" : "Start"}
);
```
### Different Formats
```tsx
// Seconds with milliseconds
// Minutes:Seconds
// Hours:Minutes:Seconds
```
## API Reference
### Timer Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `loading` | `boolean` | `false` | Whether the timer is running/loading |
| `onTick` | `(seconds: number, milliseconds: number) => void` | - | Callback called on each timer tick |
| `resetOnLoadingChange` | `boolean` | `true` | Whether to reset timer when loading changes |
| `format` | `"SS.MS" \| "MM:SS" \| "HH:MM:SS"` | `"SS.MS"` | Time display format |
| `variant` | `"default" \| "outline" \| "ghost" \| "destructive"` | `"default"` | Timer variant |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Timer size |
| `className` | `string` | - | Additional CSS classes |
### TimerRoot Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "outline" \| "ghost" \| "destructive"` | `"default"` | Timer container variant |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Timer size |
| `loading` | `boolean` | `false` | Whether the timer is running |
| `className` | `string` | - | Additional CSS classes |
### TimerIcon Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Icon size |
| `loading` | `boolean` | `false` | Whether to show loading state |
| `icon` | `React.ComponentType<{ className?: string }>` | `Clock` | Custom icon component |
| `className` | `string` | - | Additional CSS classes |
### TimerDisplay Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `time` | `string` | - | Time value to display |
| `label` | `string` | - | Optional label for accessibility |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Display size |
| `className` | `string` | - | Additional CSS classes |
### useTimer Hook
The Timer component uses the `useTimer` hook internally, which you can also use directly:
```tsx
const {
elapsedTime,
milliseconds,
formattedTime,
isRunning,
reset,
start,
stop,
} = useTimer({
loading: true,
onTick: (seconds, ms) => console.log(`${seconds}.${ms}s`),
format: "SS.MS",
});
```
#### useTimer Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `loading` | `boolean` | `false` | Whether the timer is running |
| `onTick` | `(seconds: number, milliseconds: number) => void` | - | Callback called on each timer tick |
| `resetOnLoadingChange` | `boolean` | `true` | Whether to reset timer when loading changes |
| `format` | `"SS.MS" \| "MM:SS" \| "HH:MM:SS"` | `"SS.MS"` | Time display format |
#### useTimer Return
| Property | Type | Description |
| --------------- | ------------ | -------------------------------------- |
| `elapsedTime` | `number` | Elapsed time in seconds |
| `milliseconds` | `number` | Current milliseconds (0-999) |
| `formattedTime` | `object` | Formatted time object with display |
| `isRunning` | `boolean` | Whether the timer is currently running |
| `reset` | `() => void` | Reset the timer to 0 |
| `start` | `() => void` | Start the timer |
| `stop` | `() => void` | Stop the timer |
## Examples
### Loading State Timer
```tsx
const [isLoading, setIsLoading] = useState(false);
return (
setIsLoading(!isLoading)}>
{isLoading ? "Stop" : "Start"}
);
```
### Timer with Progress Tracking
```tsx
const [isLoading, setIsLoading] = useState(false);
const [elapsedTime, setElapsedTime] = useState(0);
const handleTick = (seconds: number, milliseconds: number) => {
setElapsedTime(seconds);
// Update progress bar, send analytics, etc.
};
return (
Elapsed: {elapsedTime} seconds
setIsLoading(!isLoading)}>
{isLoading ? "Stop" : "Start"}
);
```
### Different Time Formats
```tsx
// For short durations (seconds with milliseconds)
// For medium durations (minutes:seconds)
// For long durations (hours:minutes:seconds)
```
### Custom Timer with Compound Components
```tsx
function CustomTimer() {
const { formattedTime, isRunning, start, stop, reset } = useTimer({
format: "MM:SS",
});
return (
);
}
```
### Custom Icon
```tsx
```
### Using the Hook Directly
```tsx
function CustomTimer() {
const { elapsedTime, formattedTime, isRunning, start, stop, reset } =
useTimer({
loading: false,
format: "MM:SS",
});
return (
{formattedTime.display}
{isRunning ? "Stop" : "Start"}
Reset
);
}
```
---
# Toolbar Expandable
> Expandable toolbar for React and shadcn/ui that opens into a scrollable step-by-step panel, for onboarding and feature tours.
Source: https://www.cult-ui.com/docs/components/toolbar-expandable
## Example
```tsx title="toolbar-expandable-demo.tsx"
"use client"
import { useState } from "react"
import {
CheckCircle,
Code,
Database,
Download,
Palette,
Rocket,
Settings,
Upload,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import ToolbarExpandable from "@/components/ui/toolbar-expandable"
const deploymentSteps = [
{
id: "setup",
title: "Project Setup",
description:
"Initialize your project with the required dependencies and configuration.",
icon: Settings,
content: (
),
},
{
id: "configure",
title: "Configuration",
description:
"Set up environment variables and project settings for optimal performance.",
icon: Database,
content: (
),
},
{
id: "customize",
title: "Customize Design",
description:
"Personalize your application's appearance and branding elements.",
icon: Palette,
content: (
),
},
{
id: "upload",
title: "Upload Assets",
description:
"Upload your project files, images, and other assets to the platform.",
icon: Upload,
content: (
Drag and drop files here
Choose Files
Asset Description
Upload Assets
),
},
{
id: "deploy",
title: "Deploy",
description:
"Deploy your application to production with automatic scaling and monitoring.",
icon: Rocket,
content: (
Ready to Deploy
All checks passed. Your application is ready for production
deployment.
Deployment Region
US East (Virginia)
US West (California)
Europe (Frankfurt)
Asia Pacific (Singapore)
Deploy to Production
),
},
]
const downloadSteps = [
{
id: "format",
title: "Choose Format",
description: "Select the download format that best suits your needs.",
icon: Download,
content: (
),
},
{
id: "options",
title: "Download Options",
description: "Configure additional options for your download.",
icon: Settings,
content: (
),
},
]
export default function DynamicToolbarDemo() {
const [controlledExpanded, setControlledExpanded] = useState(false)
const [controlledActiveStep, setControlledActiveStep] = useState<
string | null
>(null)
return (
Component Demo
Dynamic Toolbar Expandable
Sophisticated expandable toolbar with smooth animations and
step-based navigation for complex workflows.
Controlled Usage
External state management for programmatic control
{
setControlledExpanded(!controlledExpanded)
if (!controlledExpanded && !controlledActiveStep) {
setControlledActiveStep("setup")
}
}}
>
{controlledExpanded ? "Collapse" : "Expand"}
{
setControlledExpanded(true)
setControlledActiveStep("configure")
}}
>
Configuration
{
setControlledExpanded(true)
setControlledActiveStep("deploy")
}}
>
Deploy
{
setControlledExpanded(false)
setControlledActiveStep(null)
}}
>
Reset
expanded: {controlledExpanded.toString()} • active:{" "}
{controlledActiveStep || "null"}
Deployment Workflow
5-step process with internal state management
Download Configuration
2-step process for download preferences
Features
Key capabilities and technical highlights
Smooth Animations
Framer Motion with spring transitions
Responsive Design
Fully responsive with mobile optimization
Enhanced Scrolling
Automatic fade masks and touch support
Performance Optimized
Memoized components and callbacks
Controllable State
Controlled and uncontrolled patterns
TypeScript Support
Fully typed with comprehensive interfaces
)
}
```
Toolbar Expandable is a React toolbar component for shadcn/ui built on Motion and Radix Scroll Area. Use it for onboarding flows, feature tours, or compact settings menus that open from a single bar.
## References
Inspiration
Toolbar Expandable by Motion Primitives
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/toolbar-expandable
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/toolbar-expandable.json
**Update the import paths to match your project setup.**
## Usage
```tsx
import ToolbarExpandable from "@/components/ui/toolbar-expandable";
```
### Basic Usage
```tsx
const steps = [
{
id: "step1",
title: "First Step",
description: "Description of the first step",
icon: Settings,
content: Step 1 content
,
},
// ... more steps
]
```
### Controlled Usage
```tsx
const [expanded, setExpanded] = useState(false)
const [activeStep, setActiveStep] = useState(null)
```
## API Reference
### Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `steps` | `DynamicStep[]` | - | Array of step objects defining the toolbar content |
| `badgeText` | `string` | - | Text displayed in the badge |
| `className` | `string` | - | Additional CSS classes |
| `expanded` | `boolean` | - | Controlled expanded state |
| `onExpandedChange` | `(expanded: boolean) => void` | - | Callback for expanded state changes |
| `activeStep` | `string \| null` | - | Controlled active step ID |
| `onActiveStepChange` | `(stepId: string \| null) => void` | - | Callback for active step changes |
### DynamicStep Interface
```tsx
interface DynamicStep {
id: string;
title: string;
description: string;
icon:
| React.ComponentType<{ className?: string }>
| React.ReactElement<{ className?: string }>;
content: React.ReactNode;
}
```
| Property | Type | Description |
| --- | --- | --- |
| `id` | `string` | Unique identifier for the step |
| `title` | `string` | Display title for the step |
| `description` | `string` | Description text for the step |
| `icon` | `React.ComponentType` or `React.ReactElement` | Icon component or element |
| `content` | `React.ReactNode` | Content to display when step is active |
## Examples
### Deployment Workflow
```tsx
const deploymentSteps = [
{
id: "setup",
title: "Project Setup",
description: "Initialize your project with the required dependencies.",
icon: Settings,
content: ,
},
{
id: "configure",
title: "Configuration",
description: "Set up environment variables and project settings.",
icon: Database,
content: ,
},
{
id: "deploy",
title: "Deploy",
description: "Deploy your application to production.",
icon: Rocket,
content: ,
},
]
```
### Download Configuration
```tsx
const downloadSteps = [
{
id: "format",
title: "Choose Format",
description: "Select the download format that best suits your needs.",
icon: Download,
content: ,
},
{
id: "options",
title: "Download Options",
description: "Configure additional options for your download.",
icon: Settings,
content: ,
},
]
```
## Dependencies
The component requires the following dependencies:
- `motion` - For animations
- `@radix-ui/react-scroll-area` - For enhanced scrolling
- `@/components/ui/badge` - For the badge component
## Accessibility
The component includes several accessibility features:
- Proper ARIA roles and descriptions
- Keyboard navigation support
- Screen reader friendly
- Focus management
- Touch-friendly interactions
## Performance
The component is optimized for performance with:
- Memoized components to prevent unnecessary re-renders
- Efficient state management
- Optimized animations
- Lazy loading of content
- Minimal DOM updates
## Browser Support
The component supports all modern browsers and includes:
- Touch device optimization
- Fallbacks for older browsers
- Progressive enhancement
- Responsive design patterns
---
# Tweet Grid
> Masonry grid of embedded tweets for React and shadcn/ui from a list of tweet IDs, for testimonial walls and social proof.
Source: https://www.cult-ui.com/docs/components/tweet-grid
## Example
```tsx title="tweet-grid-demo.tsx"
"use client"
import * as React from "react"
import { GradientHeading } from "../ui/gradient-heading"
import { TweetGrid } from "../ui/tweet-grid"
// Grab tweet ids
const exampleTweets = [
"1742983975340327184",
"1743049700583116812",
"1754067409366073443",
"1753968111059861648",
"1754174981897118136",
"1743632296802988387",
"1754110885168021921",
"1760248682828419497",
"1760230134601122153",
"1760184980356088267",
]
export default function TweetGridDemo({}) {
return (
)
}
```
Tweet Grid is a React layout component for shadcn/ui with configurable columns and spacing. Use it for testimonial walls, social proof sections, or community pages.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/tweet-grid
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/tweet-grid.json
**Update the import paths to match your project setup.**
## Usage
```tsx
// Grab tweet ids
const exampleTweets = [
"1742983975340327184",
"1743049700583116812",
"1754067409366073443",
"1753968111059861648",
"1754174981897118136",
"1743632296802988387",
"1754110885168021921",
"1760248682828419497",
"1760230134601122153",
"1760184980356088267",
];
export default function TweetGridDemo() {
return ;
}
```
---
# Typewriter
> Typewriter text effect for React and shadcn/ui that types a phrase, then loops rotating words with a blinking cursor.
Source: https://www.cult-ui.com/docs/components/typewriter
## Example
```tsx title="typewriter-demo.tsx"
"use client"
import { ReactNode } from "react"
import { Typewriter } from "../ui/typewriter"
const texts = [
"Testing 124",
"Look at newcult.co",
"and check gnow.io",
"Sick af",
]
export default function TypewriterDemo() {
return (
)
}
function IosOgShellCard({ children }: { children: ReactNode }) {
return (
iMessage
Today 11:29
Hey!
Whats up bretheren?!
{children}
Delivered
)
}
```
Typewriter is a React text component for shadcn/ui built with Motion. Use it for landing page headlines, hero taglines, or AI product intros.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/typewriter
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/typewriter.json
**Update the import paths to match your project setup.**
## Usage
```tsx
const texts = [
"Testing 124",
"Look at newcult.co",
"and check gnow.io",
"Sick af",
];
export default function TypewriterDemo() {
return (
);
}
```
---
# Vote Tally
> Composable upvote list for React and shadcn/ui with accessible vote buttons, sorting by count, and controlled or uncontrolled state.
Source: https://www.cult-ui.com/docs/components/vote-tally
## Example
```tsx title="vote-tally-demo.tsx"
"use client"
import { useState } from "react"
import { ArrowUp } from "lucide-react"
import { cn } from "@/lib/utils"
import { VoteTally, type VoteTallyValue } from "../ui/vote-tally"
/* -----------------------------------------------------------------------------
* Example: Styled Vote Tally Widget
* Demonstrates usage of the headless VoteTally primitive
* -------------------------------------------------------------------------- */
const FEATURES = [
{
id: "dark-mode",
title: "Dark Mode",
description: "Add system-wide dark mode support with automatic detection",
},
{
id: "keyboard-shortcuts",
title: "Keyboard Shortcuts",
description: "Customizable keyboard shortcuts for power users",
},
{
id: "export-pdf",
title: "Export to PDF",
description: "Export documents and reports as PDF files",
},
{
id: "api-access",
title: "API Access",
description: "Public API for third-party integrations",
},
{
id: "mobile-app",
title: "Mobile App",
description: "Native iOS and Android applications",
},
] as const
export function VoteTallyExample() {
const [votes, setVotes] = useState({
"dark-mode": 142,
"keyboard-shortcuts": 89,
"export-pdf": 67,
"api-access": 203,
"mobile-app": 156,
})
const [votedItems, setVotedItems] = useState>(
new Set(["dark-mode"])
)
return (
{FEATURES.map((feature) => (
{feature.title}
{feature.description}
))}
)
}
/* -----------------------------------------------------------------------------
* Example: Minimal/Compact variant
* -------------------------------------------------------------------------- */
export function VoteTallyCompact() {
return (
{[
{ id: "feature-a", title: "Inline editing" },
{ id: "feature-b", title: "Batch operations" },
{ id: "feature-c", title: "Auto-save drafts" },
].map((feature) => (
{feature.title}
))}
)
}
export default function VoteTallyDemo() {
return (
)
}
```
Vote Tally is a composable React component for shadcn/ui. Use it for feature request boards, polls, roadmap feedback, or community idea lists.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/vote-tally
```
### Manual
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/vote-tally.json
**Update the import paths to match your project setup.**
## Usage
Use `VoteTally.Root` with `VoteTally.Item` for each item; each item can include Trigger, Title, Description, and Count. Supports controlled or uncontrolled vote counts and voted-items state, and optional sorting via `VoteTally.Group`.
### Basic list
```tsx
import { VoteTally } from "@/registry/default/ui/vote-tally";
export default function Example() {
return (
Dark mode
System-aware dark theme
Vote
Keyboard shortcuts
Customizable hotkeys
Vote
);
}
```
### Controlled votes and voted items
Wire `value` / `onValueChange` and `votedItems` / `onVotedItemsChange` to sync with your backend or state.
```tsx
import { useState } from "react";
import { VoteTally } from "@/registry/default/ui/vote-tally";
export default function Example() {
const [votes, setVotes] = useState({
"dark-mode": 10,
"keyboard-shortcuts": 5,
});
const [voted, setVoted] = useState(new Set());
return (
Dark mode
Vote
Keyboard shortcuts
Vote
);
}
```
### With sorting by votes
Wrap items in `VoteTally.Group` with `sortBy="votes-desc"` or `sortBy="votes-asc"` to reorder by vote count.
```tsx
import { VoteTally } from "@/registry/default/ui/vote-tally";
export default function Example() {
return (
Option A
Vote
Option B
Vote
Option C
Vote
);
}
```
## Features
- Vote and unvote per item via Trigger; one vote per user per item (tracked by `votedItems`)
- Controlled or uncontrolled: `value` / `defaultValue` and `onValueChange` for vote counts; `votedItems` / `defaultVotedItems` and `onVotedItemsChange` for which items the user voted for
- Optional sorting: `VoteTally.Group` with `sortBy="votes-asc"` or `sortBy="votes-desc"`
- Compound API: Root, Group, Item, Trigger, Count, Title, Description
- `useVoteTally()` hook for external access to vote state and actions
---
# Wizard Expandable
> Expandable multi-step wizard for React and shadcn/ui with Motion transitions, keyboard navigation, and step validation.
Source: https://www.cult-ui.com/docs/components/wizard-expandable
## Example
```tsx title="wizard-expandable-demo.tsx"
"use client"
import { useCallback, useMemo, useRef, useState } from "react"
import { Building2, CheckCircle2, Layers, User, UserCircle } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import WizardExpandable, {
type WizardNavigateContext,
} from "@/components/ui/wizard-expandable"
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const WHITESPACE_SPLIT = /\s+/
export default function WizardExpandableDemo() {
const [submitted, setSubmitted] = useState(false)
const [controlledExpanded, setControlledExpanded] = useState(false)
const [controlledActiveStep, setControlledActiveStep] = useState<
string | null
>(null)
const [accountName, setAccountName] = useState("")
const [accountEmail, setAccountEmail] = useState("")
const [headline, setHeadline] = useState("")
const [bio, setBio] = useState("")
const [organization, setOrganization] = useState("")
const [planTier, setPlanTier] = useState("")
const accountNameRef = useRef(null)
const accountEmailRef = useRef(null)
const headlineRef = useRef(null)
const bioRef = useRef(null)
const organizationRef = useRef(null)
const planSelectRef = useRef(null)
const canNavigateToStep = useCallback(
(ctx: WizardNavigateContext) => {
if (ctx.fromStepId === "account") {
const nameOk = accountName.trim().length > 0
const emailOk = EMAIL_PATTERN.test(accountEmail.trim())
return nameOk && emailOk
}
if (ctx.fromStepId === "profile") {
return (
headline.trim().length > 0 &&
bio.trim().split(WHITESPACE_SPLIT).filter(Boolean).length >= 3
)
}
if (ctx.fromStepId === "organization") {
return organization.trim().length > 0
}
if (ctx.fromStepId === "plan") {
return planTier.length > 0
}
return true
},
[accountEmail, accountName, bio, headline, organization, planTier]
)
const onNavigateBlocked = useCallback(
(ctx: WizardNavigateContext) => {
if (ctx.fromStepId === "account") {
if (!accountName.trim()) {
accountNameRef.current?.focus()
return
}
if (!EMAIL_PATTERN.test(accountEmail.trim())) {
accountEmailRef.current?.focus()
}
return
}
if (ctx.fromStepId === "profile") {
if (!headline.trim()) {
headlineRef.current?.focus()
return
}
bioRef.current?.focus()
return
}
if (ctx.fromStepId === "organization") {
organizationRef.current?.focus()
return
}
if (ctx.fromStepId === "plan") {
planSelectRef.current?.focus()
}
},
[accountEmail, accountName, headline]
)
const validatedSteps = useMemo(
() => [
{
id: "account",
title: "Account",
description:
"Your sign-in details. Forward navigation requires a name and valid email.",
icon: User,
content: (
),
},
{
id: "profile",
title: "Profile",
description:
"A short public profile. Requires a headline and at least three words in the bio.",
icon: UserCircle,
content: (
),
},
{
id: "organization",
title: "Organization",
description:
"Where you work. Pick a name your team will recognize on invoices and invites.",
icon: Building2,
content: (
),
},
{
id: "plan",
title: "Plan",
description:
"Choose a starting tier. You can change this anytime after signup.",
icon: Layers,
content: (
Product tier
setPlanTier(e.target.value)}
ref={planSelectRef}
value={planTier}
>
Select a plan…
Starter — individuals
Pro — small teams
Business — orgs & SSO
),
},
{
id: "review",
title: "Review",
description: "Confirm your answers before finishing.",
icon: CheckCircle2,
content: (
Name
{accountName.trim() || "—"}
Email
{accountEmail.trim() || "—"}
Headline
{headline.trim() || "—"}
Organization
{organization.trim() || "—"}
Plan
{planTier === "starter" && "Starter"}
{planTier === "pro" && "Pro"}
{planTier === "business" && "Business"}
{!planTier && "—"}
{submitted ? (
onComplete ran. Reset
the controlled demo to clear this message.
) : (
Use Finish in the bottom bar to run{" "}
onComplete.
)}
),
},
],
[
accountEmail,
accountName,
bio,
headline,
organization,
planTier,
submitted,
]
)
return (
Validated wizard
Five steps with footer navigation,{" "}
canNavigateToStep, and{" "}
onNavigateBlocked{" "}
focusing the first invalid field. Profile step includes a hook
demo button.
setSubmitted(true)}
onNavigateBlocked={onNavigateBlocked}
steps={validatedSteps}
/>
)
}
```
Wizard Expandable is a React multi-step wizard for shadcn/ui built with Motion. Use it for onboarding, checkout, or account setup flows that need guided steps.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/wizard-expandable
```
### Manual
**Install the required dependencies:**
```bash
npm install @base-ui/react @radix-ui/react-use-controllable-state motion
```
**Add shadcn/ui components used by this block (`badge` and `button`).**
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/wizard-expandable.json
**Update the import paths to match your project setup.**
---
# YouTube Video Player
> YouTube video player for React and shadcn/ui with a custom thumbnail and play button that expands into an animated modal.
Source: https://www.cult-ui.com/docs/components/youtube-video-player
## Example
```tsx title="youtube-video-player-demo.tsx"
"use client"
import { YouTubePlayer } from "@/components/ui/youtube-video-player"
export default function YouTubeVideoPlayerDemo() {
return (
YouTube Video Player Examples
A collection of YouTube video player examples showcasing different
configurations and styling options.
{/* Custom Thumbnail */}
Custom Thumbnail
Player with a custom thumbnail image instead of the default YouTube
thumbnail.
{/* Custom Styling */}
Custom Styling
Player with custom styling classes for different elements.
{/* Multiple Players Grid */}
Multiple Players
A grid of multiple video players with different content.
{/* Different Aspect Ratios */}
Different Sizes
Players in different container sizes to show responsive behavior.
{/* Small */}
{/* Medium */}
{/* Large */}
{/* URL Formats */}
Different URL Formats
The player can handle different YouTube URL formats automatically.
{/* Regular video ID */}
Video ID: "dQw4w9WgXcQ"
{/* Full YouTube URL */}
Full URL: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
{/* Short URL */}
Short URL: "https://youtu.be/dQw4w9WgXcQ"
{/* Feature Highlights */}
Features
✨ Key Features
• Expandable full-screen mode
• Custom thumbnails support
• Smooth animations with Framer Motion
• Keyboard shortcuts (ESC to close)
• Responsive design
• Accessible controls
• Multiple URL format support
🎨 Customization
• Fully customizable styling
• Custom play button designs
• Thumbnail overlay effects
• Control button positioning
• Container and backdrop styling
• Title and text customization
)
}
```
YouTube Video Player is a React video component for shadcn/ui, animated with Motion. Use it for product demos, tutorials, or video testimonials on landing pages.
## Installation
### CLI
```bash
npx shadcn@latest add @cult-ui/youtube-video-player
```
### Manual
**Install the required dependencies:**
```bash
npm install framer-motion lucide-react
```
**Copy and paste the following code into your project.**
Full source: https://www.cult-ui.com/r/youtube-video-player.json
**Update the import paths to match your project setup.**
## Usage
```tsx
export default function Example() {
return (
);
}
```
## Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| videoId | `string` | - | YouTube video ID or full URL |
| title | `string` | - | Video title displayed on thumbnail |
| defaultExpanded | `boolean` | `false` | Start in expanded modal view |
| customThumbnail | `string` | - | Custom thumbnail image URL |
| className | `string` | - | Additional CSS classes for container |
| containerClassName | `string` | - | CSS classes for video container |
| expandedClassName | `string` | - | CSS classes for expanded modal view |
| thumbnailClassName | `string` | - | CSS classes for thumbnail container |
| thumbnailImageClassName | `string` | - | CSS classes for thumbnail image |
| playButtonClassName | `string` | - | CSS classes for play button |
| playIconClassName | `string` | - | CSS classes for play icon |
| titleClassName | `string` | - | CSS classes for video title |
| controlsClassName | `string` | - | CSS classes for controls container |
| expandButtonClassName | `string` | - | CSS classes for expand/minimize button |
| backdropClassName | `string` | - | CSS classes for modal backdrop |
| playerClassName | `string` | - | CSS classes for player container |
## Examples
### Basic Player
```tsx
```
### With Custom Thumbnail
```tsx
```
### Default Expanded
```tsx
```
### Custom Styling
```tsx
```
### Multiple Players Grid
```tsx
```
## URL Format Support
The component automatically handles different YouTube URL formats:
```tsx
// Video ID
// Full YouTube URL
// Short URL
```
## Advanced Usage
### With All Customization Options
```tsx
export default function AdvancedPlayer() {
return (
);
}
```
## Component Architecture
The YouTube Player consists of two main sub-components:
### YouTubePlayer (Main Component)
The primary component that handles the video player logic, state management, and rendering. It includes:
- Thumbnail display with custom image support
- Play button with customizable styling
- Expandable modal functionality
- URL parsing for different YouTube formats
- Keyboard shortcuts (ESC to close modal)
### YouTubePlayerControls
A separate component for the expand/minimize controls that can be used independently:
```tsx
{}}
controlsClassName="custom-controls"
expandButtonClassName="custom-expand-btn"
/>
```
## Features
### Core Features
- **Expandable Modal**: Full-screen modal view with smooth animations
- **Custom Thumbnails**: Support for custom thumbnail images
- **URL Flexibility**: Handles video IDs, full URLs, and short URLs
- **Responsive Design**: Works seamlessly across all device sizes
- **Keyboard Navigation**: ESC key closes expanded view
### User Experience
- **Smooth Animations**: Powered by Framer Motion for fluid transitions
- **Hover Effects**: Interactive controls that appear on hover
- **Loading States**: Graceful handling of video loading
- **Accessibility**: ARIA labels and keyboard support
### Customization
- **Extensive Styling**: 12+ className props for complete customization
- **Flexible Layout**: Works in grids, cards, and custom containers
- **Theme Integration**: Follows your design system colors and spacing
- **Component Composition**: Sub-components can be used independently
## Accessibility
The YouTube Player includes comprehensive accessibility features:
- **ARIA Labels**: Proper labeling for screen readers
- **Keyboard Navigation**: Full keyboard support including ESC to close
- **Focus Management**: Proper focus handling in modal states
- **Semantic HTML**: Uses appropriate HTML elements and structure
- **High Contrast**: Respects system preferences for reduced motion
## Browser Support
- Modern browsers with ES2018+ support
- Requires JavaScript enabled for video playback
- Falls back gracefully when YouTube embed is blocked
- Mobile-first responsive design
## Performance
- **Lazy Loading**: Videos only load when play button is clicked
- **Optimized Animations**: GPU-accelerated transforms with Framer Motion
- **Efficient Re-renders**: Minimal state updates and memoization
- **Lightweight Bundle**: Only includes necessary dependencies