# HextaUI > Ready-to-use foundation components/blocks built on top of shadcn/ui. Accessible React components on Base UI and Tailwind CSS v4 that you copy into your project and own. HextaUI keeps the shadcn/ui API and adds the details that make an interface feel finished: interruptible motion, reduced-motion fallbacks, keyboard support, right-to-left layouts and touch-sized targets. Every component is tested against its edge cases before it ships. - Install a component with the shadcn CLI: `npx shadcn@latest add https://hextaui.com/r/.json`. It adds the source, the HextaUI theme tokens and any HextaUI components it depends on. `https://hextaui.com/r/all.json` installs every component. - The code is then owned by the project, like shadcn/ui. There is no HextaUI npm package. HextaUI is MIT licensed and free for personal and commercial use. - Behavior and accessibility come from Base UI (`@base-ui/react`). Compose with the `render` prop, not `asChild`. - Styling uses Tailwind CSS v4 with theme tokens. Merge classes with `cn` from the `cn` package. - Icons come from `@tabler/icons-react`. - Import components from `@/components/ui/`, hooks from `@/hooks/` and utilities from `@/lib/`. - Each component page lists its dependencies, the full source to copy, usage, examples, keyboard support and an API reference. - The shadcn registry index is https://hextaui.com/r/registry.json. Register it as a namespace in components.json with `"registries": { "@hextaui": "https://hextaui.com/r/{name}.json" }` to run `npx shadcn@latest add @hextaui/` or browse it through the shadcn MCP server. - Without the CLI, add the theme tokens from https://hextaui.com/docs/installation#theme before copying component files. - Append `.md` to any docs URL for its Markdown, for example https://hextaui.com/docs/button.md. ## Docs - [Introduction](https://hextaui.com/docs.md): What HextaUI is, what it is built on and the principles behind it. - [Installation](https://hextaui.com/docs/installation.md): Add HextaUI with the shadcn CLI or by hand, register the @hextaui namespace, the theme tokens, and setup for AI assistants. - [MCP server](https://hextaui.com/docs/mcp.md): Connect Claude Code, Cursor, VS Code and other AI assistants to HextaUI, so they search the docs, read each API and install components for you. ## Components - [Accordion](https://hextaui.com/docs/accordion.md): Stacked headings that each reveal a panel, with height motion you can reverse mid-way and panels that stay searchable while closed. - [Alert](https://hextaui.com/docs/alert.md): Inline messages for status and feedback, with a neutral surface, a colored icon and a dismiss that collapses smoothly. - [Alert dialog](https://hextaui.com/docs/alert-dialog.md): A confirmation dialog for destructive or important actions that waits for async work, and becomes a bottom sheet on phones. - [Aspect ratio](https://hextaui.com/docs/aspect-ratio.md): A box that holds its shape before media loads, shimmers while loading, fades the media in and falls back when it fails. - [Attachment](https://hextaui.com/docs/attachment.md): File and image cards for uploads, with progress, states, actions, a full-card trigger and names that keep their extension. - [Avatar](https://hextaui.com/docs/avatar.md): User photos with an initials fallback, status badges and stacked groups that collapse into a count. - [Badge](https://hextaui.com/docs/badge.md): Status labels with colored dots, removable tags that slide closed and counts that roll to their new value. - [Breadcrumb](https://hextaui.com/docs/breadcrumb.md): A trail of links to the current page that wraps safely, mirrors in right-to-left layouts and expands collapsed segments in place. - [Bubble](https://hextaui.com/docs/bubble.md): Chat message bubbles with variants, grouped corners, reactions and room for interactive content. - [Button](https://hextaui.com/docs/button.md): Buttons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests. - [Button group](https://hextaui.com/docs/button-group.md): Buttons joined into one control, with shared seams, separators, text addons, nesting and vertical stacks. - [Calendar](https://hextaui.com/docs/calendar.md): A date grid for single, range and multiple selection, with sliding months, range previews and touch-sized days. - [Card](https://hextaui.com/docs/card.md): A surface for grouping content, with three variants, edge-to-edge media, concentric radii and whole-card links. - [Carousel](https://hextaui.com/docs/carousel.md): Native scroll-snap slides with momentum on touch, mouse dragging, arrow keys, dots, thumbnails and an autoplay that pauses when it should. - [Chart](https://hextaui.com/docs/chart.md): Recharts charts with themed colors, a tooltip and legend that read labels from one config, and keyboard navigation with a visible focus ring. - [Checkbox](https://hextaui.com/docs/checkbox.md): A checkbox whose check draws in, with indeterminate parents, groups and labels that share its hover. - [Collapsible](https://hextaui.com/docs/collapsible.md): A panel that shows and hides with height motion you can reverse mid-way, without jumping the layout. - [Combobox](https://hextaui.com/docs/combobox.md): A filterable select with chips, groups and async results, in a popup that resizes as you type. - [Command](https://hextaui.com/docs/command.md): A searchable list of actions, inline or as a ⌘K palette, with pages, shortcuts and highlighted matches. - [Context menu](https://hextaui.com/docs/context-menu.md): A menu of actions on right click or long press, with submenus, checkbox and radio items, and hold feedback on touch. - [Data table](https://hextaui.com/docs/data-table.md): A table for real data, with sorting, search, row selection, pinned columns, a sticky header and pagination. - [Date picker](https://hextaui.com/docs/date-picker.md): A button that opens a calendar in a popover, or a bottom sheet on phones, for single dates and ranges. - [Dialog](https://hextaui.com/docs/dialog.md): A window over the page for forms and focused tasks, with a pinned header and footer, nesting, and a swipeable bottom sheet on phones. - [Drawer](https://hextaui.com/docs/drawer.md): A panel that slides in from any edge and follows your finger, with snap points, a working handle and nested drawers that stack. - [Dropdown menu](https://hextaui.com/docs/dropdown-menu.md): A menu of actions and options behind a button, with groups, submenus, checkbox and radio items, and shortcuts. - [Empty](https://hextaui.com/docs/empty.md): A placeholder for screens with nothing to show yet, with an icon, a message and the next action. - [Field](https://hextaui.com/docs/field.md): Labels, descriptions and errors wired to their control, with validation states and layouts for forms. - [Hover card](https://hextaui.com/docs/hover-card.md): A preview card that opens when a link is hovered or focused, for content sighted users can glance at. - [Input](https://hextaui.com/docs/input.md): A text input with three sizes, invalid and read-only states, native validation styling and a 16px touch font so phones never zoom in. - [Input group](https://hextaui.com/docs/input-group.md): An input with icons, text, buttons or a keyboard hint attached, sharing one border and focus ring. - [Input OTP](https://hextaui.com/docs/input-otp.md): One-time code slots that take typing, paste and SMS autofill, with an opt-in animation that cascades codes in and a status for verifying. - [Item](https://hextaui.com/docs/item.md): A row of media, text and actions for lists, settings and pickers, with a grouped surface and a hover highlight that glides between rows. - [Kbd](https://hextaui.com/docs/kbd.md): Key caps for shortcuts that show the right symbols on every platform, read them out properly and press down with the real keys. - [Label](https://hextaui.com/docs/label.md): A label that follows its control, dimming when it's disabled and marking it required or optional on its own. - [Marker](https://hextaui.com/docs/marker.md): Quiet notes between content, like date dividers and system events, with sticky dates and times that read as Today or Yesterday. - [Menubar](https://hextaui.com/docs/menubar.md): A desktop-style bar of menus with one highlight that slides between open menus and instant switching as you sweep across. - [Message](https://hextaui.com/docs/message.md): A chat message row with an avatar, name, bubbles and status, where new messages rise in from the sender's side. - [Message scroller](https://hextaui.com/docs/message-scroller.md): A chat scroll area that follows new messages, keeps your place while you read back, and counts what you missed on the jump button. - [Native select](https://hextaui.com/docs/native-select.md): The browser's own select, styled to match Input, with a quiet placeholder, Field support and the OS picker on every device. - [Navigation menu](https://hextaui.com/docs/navigation-menu.md): Site navigation with dropdowns, or one full-width panel that stays open while its content slides between menus. - [Number flow](https://hextaui.com/docs/number-flow.md): Animated numbers where only the changed digits spin, with any Intl format and locale. - [Pagination](https://hextaui.com/docs/pagination.md): Page navigation that keeps every button in place, glides to the current page and jumps to any page from the ellipsis. - [Popover](https://hextaui.com/docs/popover.md): A floating panel anchored to a trigger that resizes smoothly with its content and follows the trigger’s direction. - [Progress](https://hextaui.com/docs/progress.md): A bar or ring that shows how far a task has come, eases between updates and slides while the total is unknown. - [Questionnaire](https://hextaui.com/docs/questionnaire.md): A step-by-step form that moves between questions with a sense of direction, shows real progress and answers from the keyboard. - [Radio group](https://hextaui.com/docs/radio-group.md): Pick one option from a set, with a dot that hands off between choices and cards whose selection ring slides to the new pick. - [Resizable](https://hextaui.com/docs/resizable.md): Panels you can drag apart, with a quiet divider that wakes up on hover, sizes that glide on reset or collapse, and layouts that persist. - [Scroll area](https://hextaui.com/docs/scroll-area.md): Native scrolling with a minimal scrollbar, edges that fade only where there’s more to see, and an optional peek that cuts the last item in half. - [Select](https://hextaui.com/docs/select.md): Pick one or more options from a list that opens on the current value, with typeahead, groups and form support. - [Separator](https://hextaui.com/docs/separator.md): A hairline that divides content horizontally or vertically, with an optional label and a decorative mode for purely visual lines. - [Sheet](https://hextaui.com/docs/sheet.md): A panel that slides in from any edge, with swipe-to-dismiss, scroll lock and stacked nesting. - [Sidebar](https://hextaui.com/docs/sidebar.md): An app sidebar that collapses to icons or off canvas, stays pinned under your header, and becomes a swipeable sheet on phones. - [Skeleton](https://hextaui.com/docs/skeleton.md): Placeholders that wait 150ms before showing, take the exact size of the content they wrap, and fade it in without moving anything. - [Slider](https://hextaui.com/docs/slider.md): Pick a value or a range by dragging, tapping the track or using the keys, with jumps that glide and an optional value bubble. - [Spinner](https://hextaui.com/docs/spinner.md): A loading indicator with Apple-style ticks or a breathing ring that can wait before showing and stay long enough not to flicker. - [Switch](https://hextaui.com/docs/switch.md): An on/off toggle you can tap, press or drag, with a thumb that stretches under your finger and an optional pending state for saves. - [Table](https://hextaui.com/docs/table.md): A responsive table with a surface style, wrapping or compact cells, sticky headers, pinned columns and scroll hints. - [Tabs](https://hextaui.com/docs/tabs.md): Switch between views with an indicator that slides to the active tab, as a segmented control or an underline, and lists that scroll when they overflow. - [Textarea](https://hextaui.com/docs/textarea.md): Multi-line text that grows smoothly with what you write between a minimum and maximum number of rows, with counters, validation and a submit shortcut. - [Toast](https://hextaui.com/docs/toast.md): Brief messages that stack neatly, expand on hover, swipe away, and turn a loading state into success or error in place. - [Toggle](https://hextaui.com/docs/toggle.md): A button that stays on or off, with a fill that settles in when pressed, a clear hover-to-on step and icons that can fill with the state. - [Toggle group](https://hextaui.com/docs/toggle-group.md): A row of toggles for one choice or many, with a fill that slides to the picked item, joined or spaced items and arrow-key focus. - [Tooltip](https://hextaui.com/docs/tooltip.md): A short hint on hover or keyboard focus that opens after a brief rest, switches instantly between neighbours and shows shortcuts. - [Tree](https://hextaui.com/docs/tree.md): Nested, expandable rows for files and hierarchies, with indent guides, connector lines, cascading checkboxes, typeahead and full keyboard support. - [Video player](https://hextaui.com/docs/video-player.md): A video player with a scrubbable seek bar, auto-hiding controls, keyboard shortcuts, speed, picture in picture and full screen. ## Hooks - [useAutosize](https://hextaui.com/docs/use-autosize.md): Grows a textarea with what you write between its minimum and maximum height, animating each change without ever touching the text. - [useButtonFeedback](https://hextaui.com/docs/use-button-feedback.md): Runs an async action through loading, success and error, skipping the spinner for fast requests and holding an error while you read it. - [useComposedRef](https://hextaui.com/docs/use-composed-ref.md): Keeps a ref to your own element while still forwarding it to whatever ref the parent passed in. - [useDelayedLoading](https://hextaui.com/docs/use-delayed-loading.md): Shows a loading state only when work is actually slow, then keeps it up long enough that it never flickers. - [useHeldKeys](https://hextaui.com/docs/use-held-keys.md): The keys someone is holding down right now, shared by every subscriber through a single set of window listeners. - [useInvalidShake](https://hextaui.com/docs/use-invalid-shake.md): Shakes a form control when a submit attempt finds it invalid, and never while someone is still typing. - [useMergedRef](https://hextaui.com/docs/use-merged-ref.md): Combines any number of callback and object refs into one, with React 19 ref cleanup for each of them. - [usePagination](https://hextaui.com/docs/use-pagination.md): Turns a page and a page count into the list of pages and ellipses to render, keeping its length steady as the page moves. - [useToday](https://hextaui.com/docs/use-today.md): Today's date that rolls over at midnight and when the tab comes back, without a hydration mismatch. ## Utilities - [Hairline](https://hextaui.com/docs/hairline.md): Borders and rings exactly one device pixel wide on every screen, through one variable the whole theme reads. - [Hotkey](https://hextaui.com/docs/hotkey.md): Parse, label, announce and match keyboard shortcuts, with ⌘ on Apple platforms and Ctrl everywhere else. - [Motion](https://hextaui.com/docs/motion.md): The easing curves, durations and reduced-motion check every component animates with, plus hooks for size morphs and sliding highlights. - [Scroll fade](https://hextaui.com/docs/scroll-fade.md): Edges of a scroll area that fade only when there's more to see, driven by the scroll position in CSS alone. - [Shimmer](https://hextaui.com/docs/shimmer.md): A band of light that sweeps across text for in-progress states, tinted from the text color and still under reduced motion. ## Optional - [Full documentation](https://hextaui.com/llms-full.txt): Every docs page in one file, with usage, installation, keyboard support and API reference. Component source and example code live in each component's Markdown page. --- # Introduction > Ready-to-use foundation components/blocks built on top of shadcn/ui. Docs: https://hextaui.com/docs Markdown: https://hextaui.com/docs.md ## What is HextaUI HextaUI is a collection of 65 components, 9 hooks and 5 utilities for React. You add them with the shadcn CLI, the source lands in your project, and from then on it's your code to read and change. Every component keeps the shadcn/ui API you already know. What's different is the work you would otherwise do yourself: the states, the edge cases and the small details that make an interface feel finished. ## Quick start In a project set up with shadcn/ui, add a component: ```bash npx shadcn@latest add https://hextaui.com/r/button.json ``` Then import it from `@/components/ui`. ```tsx import { Button } from "@/components/ui/button" export function SaveButton() { return ( ) } ``` Requirements, adding everything at once and the `@hextaui` namespace are covered in [Installation](https://hextaui.com/docs/installation). ## What you get These hold for every component, so you don't have to check each one. ### Motion Animations use the theme's easing curves and can be interrupted and reversed mid-way. When reduced motion is on, movement turns into a simple fade. ### Keyboard and screen readers Everything works with the keyboard alone, and focus goes where you expect when popups open and close. Changes are announced too: toasts, loading and saved states, selections and slide changes. ### Touch and small screens Tap targets grow on touch screens, text inputs stay at 16px so iOS doesn't zoom in, and popups stay on screen down to 320px wide. Layouts mirror for right-to-left languages. ### High contrast Focus rings, control edges and selected states stay visible in Windows high contrast mode, and the default theme meets WCAG AA contrast in light and dark. ## Built on - shadcn/ui with the `base-vega` style - Base UI primitives for behavior and accessibility - Tailwind CSS v4, with theme tokens for color, radius and easing - Tabler icons ## AI assistants Every page has a Markdown version, and `/llms.txt` lists them all. Assistants that support MCP can connect to the [HextaUI MCP server](https://hextaui.com/docs/mcp) to search the docs, read component APIs and get install commands. ## License HextaUI is open source under the [MIT license](https://github.com/preetsuthar17/hextaui/blob/main/LICENSE), free for personal and commercial projects. --- # Installation > Add HextaUI to a React project with the shadcn CLI, or copy the files in by hand. Docs: https://hextaui.com/docs/installation Markdown: https://hextaui.com/docs/installation.md ## Requirements A React 19 project with Tailwind CSS v4 and a `components.json`. If you don't have one yet, set it up with the shadcn CLI. ```bash npx shadcn@latest init ``` ## Add components Pass a component's registry URL to the CLI. It copies the source into your project, installs its dependencies, adds the theme tokens and pulls in any HextaUI components it builds on. ```bash npx shadcn@latest add https://hextaui.com/r/button.json ``` Add every component at once with: ```bash npx shadcn@latest add https://hextaui.com/r/all.json ``` The files are yours from then on. Import them from `@/components/ui` and edit them like any other code. ## Namespace Register HextaUI in `components.json` to add components by name. ```json title="components.json" { "registries": { "@hextaui": "https://hextaui.com/r/{name}.json" } } ``` ```bash npx shadcn@latest add @hextaui/button ``` ## Theme Components use a few tokens on top of shadcn/ui: status colors, easing curves, a hairline border width and the keyframes they animate with. The CLI adds them for you. When copying files by hand, add this to your global CSS once. ```css title="app/globals.css" @import "tw-animate-css"; @import "shadcn/tailwind.css"; @theme inline { --color-destructive-foreground: var(--destructive-foreground); --color-success: var(--success); --color-success-foreground: var(--success-foreground); --color-info: var(--info); --color-info-foreground: var(--info-foreground); --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); --color-focus-ring: color-mix(in oklab, var(--foreground) 40%, transparent); --color-canvas: Canvas; --color-canvas-text: CanvasText; --color-highlight: Highlight; --color-highlight-text: HighlightText; --color-gray-text: GrayText; --font-weight-book: 520; --ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1); --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); --ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); --ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1); --ease-spring: linear(0, 0.015 2%, 0.0532 4%, 0.1065 6%, 0.1686 8%, 0.2351 10%, 0.3363 13%, 0.4332 16%, 0.5495 20%, 0.648 24%, 0.7287 28%, 0.807 33%, 0.8646 38%, 0.9128 44%, 0.9447 50%, 0.968 57%, 0.9834 65%, 0.9924 74%, 0.9975 85%, 1); --default-border-width: var(--hairline); --text-xs: 0.6875rem; --text-sm: 0.8125rem; --text-base: 0.9375rem; --text-lg: 1.0625rem; --text-xl: 1.1875rem; --text-2xl: 1.4375rem; --text-3xl: 1.8125rem; --text-4xl: 2.1875rem; --text-5xl: 2.9375rem; --text-6xl: 3.6875rem; --text-7xl: 4.4375rem; --text-8xl: 5.9375rem; --text-9xl: 7.9375rem; --text-touch: max(16px, 1rem); --animate-shimmer: shimmer 1.6s ease-in-out infinite; --animate-skeleton-pulse: skeleton-pulse 2s ease-in-out infinite; --animate-button-state-in: button-state-in 220ms cubic-bezier(0.2, 0, 0, 1) both; --animate-button-state-out: button-state-out 220ms cubic-bezier(0.2, 0, 0, 1) both; --animate-button-draw-circle: button-draw 240ms cubic-bezier(0.2, 0, 0, 1) both; --animate-button-draw-check: button-draw 180ms cubic-bezier(0.2, 0, 0, 1) 180ms both; --animate-spinner-rotate: spinner-rotate 0.9s linear infinite; --animate-spinner-dash: spinner-dash 1.5s ease-in-out infinite; --animate-spinner-ticks: spinner-rotate 0.8s steps(8, end) infinite; --animate-spinner-pulse: spinner-pulse 1.6s ease-in-out infinite; --animate-progress-slide: progress-slide 1.6s cubic-bezier(0.65, 0, 0.35, 1) infinite; --animate-checkbox-draw: button-draw 200ms cubic-bezier(0.2, 0, 0, 1) 40ms both; --animate-button-shake: button-shake 320ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both; --animate-avatar-badge-pulse: avatar-badge-pulse 700ms cubic-bezier(0.215, 0.61, 0.355, 1) both; --animate-menu-blink: menu-blink 120ms steps(1, end) both; --animate-input-otp-wave: input-otp-wave 1.2s ease-in-out infinite; --animate-input-otp-pulse: input-otp-pulse 1.2s ease-in-out infinite; --animate-input-otp-pop: input-otp-pop 360ms cubic-bezier(0.23, 1, 0.32, 1) both; --animate-empty-in: empty-in 480ms cubic-bezier(0.23, 1, 0.32, 1) var(--empty-delay, 0ms) both; --animate-message-in: message-in 420ms linear(0, 0.015 2%, 0.0532 4%, 0.1065 6%, 0.1686 8%, 0.2351 10%, 0.3363 13%, 0.4332 16%, 0.5495 20%, 0.648 24%, 0.7287 28%, 0.807 33%, 0.8646 38%, 0.9128 44%, 0.9447 50%, 0.968 57%, 0.9834 65%, 0.9924 74%, 0.9975 85%, 1) both; --animate-video-player-flash: video-player-flash 650ms cubic-bezier(0.23, 1, 0.32, 1) both; } :root { --destructive-foreground: oklch(0.985 0 0); --success: oklch(0.527 0.154 150); --success-foreground: oklch(0.985 0 0); --info: oklch(0.546 0.215 262); --info-foreground: oklch(0.985 0 0); --warning: oklch(0.555 0.163 49); --warning-foreground: oklch(0.985 0 0); } .dark { --destructive-foreground: oklch(0.205 0 0); --success: oklch(0.792 0.209 151.7); --success-foreground: oklch(0.205 0 0); --info: oklch(0.707 0.165 254.6); --info-foreground: oklch(0.205 0 0); --warning: oklch(0.828 0.189 84.4); --warning-foreground: oklch(0.205 0 0); } @keyframes message-in { from { opacity: 0; translate: 0 0.5rem; scale: 0.96; } } @keyframes video-player-flash { from { opacity: 0; scale: var(--video-player-flash-from, 0.9); } 20%, 55% { opacity: 1; scale: 1; } to { opacity: 0; scale: var(--video-player-flash-to, 1.08); } } @keyframes empty-in { from { opacity: 0; translate: 0 0.375rem; filter: blur(2px); } } @keyframes shimmer { from { translate: calc(-100% * var(--skeleton-dir, 1)) 0; } to { translate: calc(100% * var(--skeleton-dir, 1)) 0; } } @keyframes skeleton-pulse { 0%, 100% { opacity: 0; } 50% { opacity: 0.5; } } @keyframes button-state-in { from { opacity: 0; transform: perspective(220px) rotateX(90deg) translateY(0.75rem); } to { opacity: 1; transform: perspective(220px) rotateX(0) translateY(0); } } @keyframes button-state-out { from { opacity: 1; transform: perspective(220px) rotateX(0) translateY(0); } 50% { opacity: 0; } to { opacity: 0; transform: perspective(220px) rotateX(-90deg) translateY(-0.75rem); } } @keyframes spinner-rotate { to { rotate: 360deg; } } @keyframes spinner-dash { 0% { stroke-dasharray: 1 60; stroke-dashoffset: 0; } 50% { stroke-dasharray: 34 60; stroke-dashoffset: -12; } 100% { stroke-dasharray: 34 60; stroke-dashoffset: -56; } } @keyframes progress-slide { from { translate: calc(-100% * var(--progress-dir, 1)) 0; } to { translate: calc(250% * var(--progress-dir, 1)) 0; } } @keyframes spinner-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } @keyframes button-draw { to { stroke-dashoffset: 0; } } @keyframes avatar-badge-pulse { from { scale: 1; opacity: 0.5; } to { scale: 2.6; opacity: 0; } } @keyframes menu-blink { 0% { background-color: transparent; } 50%, 100% { background-color: var(--menu-blink, var(--color-accent)); } } @keyframes input-otp-wave { 0%, 60%, 100% { translate: 0 0; opacity: 1; } 30% { translate: 0 -0.18em; opacity: 0.45; } } @keyframes input-otp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } } @keyframes input-otp-pop { 0% { scale: 1; } 40% { scale: 1.16; } 100% { scale: 1; } } @keyframes button-shake { 0%, 100% { translate: 0; } 20% { translate: -3px; } 40% { translate: 3px; } 60% { translate: -2px; } 80% { translate: 2px; } } @utility text-hero { font-size: min(calc((100vw - 1.5rem) / 15.75), 2.6875rem); line-height: 1.3; } @utility border-hairline { border-width: var(--hairline); } :root { --hairline: 1px; } @media (min-resolution: 1.5dppx) { :root { --hairline: calc(1px / 1.5); } } @media (min-resolution: 2dppx) { :root { --hairline: 0.5px; } } @media (min-resolution: 3dppx) { :root { --hairline: calc(1px / 3); } } @media (min-resolution: 4dppx) { :root { --hairline: 0.25px; } } @layer base { body { position: relative; } :where(a, button, input, label, select, summary, textarea, [role="button"]) { touch-action: manipulation; } } ``` ## AI assistants Every page is available as Markdown, so assistants get the full source, examples and API without scraping. Use the Copy page menu at the top of any page, or point your assistant at these files. - `https://hextaui.com/llms.txt` lists every page with a short description. - `https://hextaui.com/llms-full.txt` has every page in one file. - Add `.md` to any docs URL for that page, like `https://hextaui.com/docs/button.md`. For the best results, connect the [HextaUI MCP server](https://hextaui.com/docs/mcp). Assistants search the docs, read each API and get install commands without leaving your editor. With the namespace above, the shadcn MCP server lets assistants like Claude Code, Cursor and Codex browse and install HextaUI components directly. ```bash npx shadcn@latest mcp init --client claude ``` --- # MCP server > Connect Claude Code, Cursor, VS Code and other AI assistants to HextaUI, so they search the docs, read each API and install components for you. Docs: https://hextaui.com/docs/mcp Markdown: https://hextaui.com/docs/mcp.md The HextaUI MCP server gives assistants the same docs you read here: what each component is for, its props and data attributes, keyboard support, working examples and the command that installs it. Assistants stop guessing at shadcn/ui props and use what HextaUI actually ships. It is a hosted, read-only server at `https://hextaui.com/mcp`. There is nothing to install and no API key. It reads the docs of the version on this site, so it is always current. ## Setup Add the server to your assistant, then ask it to build something with HextaUI. ### Claude Code ```bash claude mcp add --transport http hextaui https://hextaui.com/mcp ``` ### Cursor ```json title=".cursor/mcp.json" { "mcpServers": { "hextaui": { "url": "https://hextaui.com/mcp" } } } ``` ### VS Code ```json title=".vscode/mcp.json" { "servers": { "hextaui": { "type": "http", "url": "https://hextaui.com/mcp" } } } ``` ### Codex ```toml title="~/.codex/config.toml" [mcp_servers.hextaui] url = "https://hextaui.com/mcp" ``` ### Windsurf ```json title="~/.codeium/windsurf/mcp_config.json" { "mcpServers": { "hextaui": { "serverUrl": "https://hextaui.com/mcp" } } } ``` ### Other clients Any client that supports remote servers over Streamable HTTP can use the URL directly. For a client that only runs local servers, bridge it with `mcp-remote`. ```json { "mcpServers": { "hextaui": { "command": "npx", "args": ["-y", "mcp-remote", "https://hextaui.com/mcp"] } } } ``` ## Tools Every tool is read-only. Installing is left to your assistant, which runs the shadcn CLI command in your project like you would. - `search_docs` finds components, examples, props and data attributes from plain language, like “confirm before delete” or a prop name. - `list_components` lists every component, hook and utility by category. - `get_component` returns a page's full docs: install command, usage, examples, keyboard, accessibility and API reference. - `get_component_api` returns only the API reference and keyboard support. - `get_examples` returns the code of any example on a page. - `get_component_source` returns the source files the CLI would install. - `get_install_command` builds one shadcn command for several components, for npm, pnpm, yarn or bun. - `get_setup` returns the [installation](https://hextaui.com/docs/installation) guide and the conventions generated code should follow. The server also exposes each page as a `hextaui://docs/{slug}` resource, and a `build-ui` prompt that walks an assistant from picking components to installing them. ## Try it - Build a settings page with HextaUI: profile form, notification switches and a danger zone. - Which HextaUI component should I use for a list of files with checkboxes? - Replace this modal with the HextaUI dialog and keep the form working. - Add a chat view with HextaUI message bubbles that scrolls to new messages. Prefer the shadcn MCP server? Register the [@hextaui namespace](https://hextaui.com/docs/installation#namespace) and it can browse and install HextaUI components and examples too. --- # Accordion > Stacked headings that each reveal a panel, with height motion you can reverse mid-way and panels that stay searchable while closed. Docs: https://hextaui.com/docs/accordion Markdown: https://hextaui.com/docs/accordion.md ## Installation ### CLI ```bash npx shadcn@latest add https://hextaui.com/r/accordion.json ``` This adds the component, the HextaUI theme tokens and any HextaUI components it depends on. ### Manual Add the HextaUI theme tokens (https://hextaui.com/docs/installation#theme) to your global CSS if you haven't yet, then install the dependencies. ```bash pnpm add @base-ui/react @tabler/icons-react class-variance-authority cn ``` Copy `components/ui/accordion.tsx` into your project. Their source is in this page's Markdown. ## Usage ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" ``` ```tsx Is it accessible? Yes. It follows the WAI-ARIA accordion pattern. ``` ## Composition ```text Accordion └── AccordionItem ├── AccordionTrigger └── AccordionContent ``` ## Examples ### Outline Items share one bordered surface, with the outer corners rounded. ### Separated Each item is its own card. ### Ghost No borders. Triggers get a soft fill on hover and when open. ### Multiple Set `multiple` to let more than one item stay open. ### Disabled Disable a single item with `disabled` on ``, or the whole accordion on the root. ### Controlled Pass `value` and `onValueChange` to keep the open items in your own state. ### Custom icon Pass any element to `icon`. Style its open state with `group-data-panel-open/accordion-trigger`. ### Leading icons Icons before the label are sized and aligned for you. `icon={null}` removes the chevron. ### Long content Long titles wrap while the icon stays on the first line. Closed panels stay searchable, so Cmd/Ctrl+F for “retention” opens this one. ### Nested An inner accordion keeps its own variant, focus styles and arrow-key navigation. ### Right to left Content, icons and the open motion follow the reading direction. ## Keyboard | Key | Action | | --- | --- | | `Enter` `Space` | Opens or closes the focused item. | | `↓` | Moves focus to the next trigger. | | `↑` | Moves focus to the previous trigger. | | `Home` | Moves focus to the first trigger. | | `End` | Moves focus to the last trigger. | | `Tab` | Moves focus into the open panel, then on to the next trigger. | ## API reference Built on the Base UI accordion. Every part accepts the props of the primitive it wraps. ### Accordion | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"default" \| "outline" \| "separated" \| "ghost"` | `"default"` | | | `multiple` | `boolean` | `false` | Allow more than one item to be open. | | `value` | `Value[]` | – | | | `defaultValue` | `Value[]` | – | | | `onValueChange` | `(value: Value[], details) => void` | – | | | `disabled` | `boolean` | `false` | | | `hiddenUntilFound` | `boolean` | `true` | Keep closed panels findable with the browser’s page search. | | `keepMounted` | `boolean` | `false` | Ignored while hiddenUntilFound is on. | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="accordion"` | Target the root in CSS. | | `data-variant` | The current variant. | | `data-disabled` | Present when the accordion is disabled. | ### AccordionItem | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | `Value` | – | Generated automatically when omitted. | | `disabled` | `boolean` | `false` | | | `onOpenChange` | `(open: boolean, details) => void` | – | | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="accordion-item"` | Target items in CSS. | | `data-open` | Present when the item is open. | | `data-disabled` | Present when the item is disabled. | | `data-index` | The item’s position, starting at 0. | ### AccordionTrigger | Prop | Type | Default | Description | | --- | --- | --- | --- | | `icon` | `ReactNode \| null` | `` | null removes the icon. | | `render` | `ReactElement \| (props, state) => ReactElement` | ` ``` With `feedback`, return a promise from `onClick` and the button shows loading, then success or error, then resets on its own. ## Examples ### Variants Six variants. `destructive` is a soft tint so a dangerous action reads clearly without shouting. ### Sizes Text sizes `xs` to `lg`, and square `icon-*` sizes. Small icon buttons get a larger invisible touch area on touch screens. ### With icon Mark an icon with `data-icon="inline-start"` or `"inline-end"` and the padding on that side tightens to balance it. ### Disabled `focusableWhenDisabled` keeps a disabled button in the tab order, so a tooltip or explanation can still be reached by keyboard. ### Custom labels `loadingLabel`, `successLabel` and `errorLabel` replace the text for each state. Each label flips in while the old one flips out. ### Smooth width The button eases to the width of each label instead of reserving space for the longest one, so nothing around it jumps. ### Error details Pass a function to `errorLabel` to show the rejection reason. While the pointer or keyboard focus stays on the button, the error stays on screen. ### Forms For submit buttons, call `track()` from `useButtonFeedback` in `onSubmit` and spread `buttonProps` on the button. Remove the @ to see the error. ### Icon buttons Icon sizes swap only the icon for each state and keep their square shape. The aria-label stays the accessible name. ### Feedback on every variant Filled variants turn green or red when done. `ghost` and `link` only change their text color. ### Controlled loading Set `loading` yourself when the work is tracked elsewhere. The button stays focusable and announces that it is busy. ### Controlled status Drive `status` directly, for example from a form library’s submit state. ### As a link Pass an anchor to `render` and set `nativeButton={false}` so the button keeps link semantics. ### Right to left Icons and state labels follow the reading direction. ## Keyboard | Key | Action | | --- | --- | | `Enter` `Space` | Activates the button. Ignored while a feedback request is in flight. | | `Tab` | Moves focus. A loading button stays focusable, and focusing an error keeps it on screen until you move away. | ## Accessibility - Every state change is announced through a polite live region: loading, then the success or error label. - While loading, the button sets `aria-busy` and stays focusable, so focus is never lost mid-request. - The spinner appears only after 150ms and then stays for at least 400ms, so fast requests never flash and slow ones never flicker. - With reduced motion, state labels fade instead of flipping and the error shake is skipped. ## API reference Built on the Base UI button. It renders a ` ``` Button group joins the buttons you already use. Install ` 12 of 20 seats used. ``` ## Composition ```text Card ├── CardHeader │ ├── CardTitle │ │ └── CardLink │ ├── CardDescription │ └── CardAction ├── CardContent └── CardFooter ``` ## Examples ### Variants `default` sits on a hairline ring with a soft shadow, `outline` drops the fill and shadow, and `muted` uses a filled surface for quieter sections. ### Sizes `size="sm"` tightens the spacing, radius and title size together, so a compact card still looks like part of the same family. ### Media An image, video, picture or aspect ratio placed first or last runs edge to edge. The card drops the padding on that side and clips the media to its corners. ### Clickable card Put a `` in the title to make the whole card one link. Buttons and other controls inside stay clickable and keep their own focus, like the star here. ### Clickable variants Each variant gets its own hover feedback once it holds a link, and the focus ring wraps the whole card when the link is focused with the keyboard. ### Borders Add `border-b` to the header or `border-t` to the footer and the card adds matching padding on the inner side. ### Nested A card inside CardContent derives its radius from the parent’s radius minus the spacing, so the corners stay concentric. ### Long content Unbroken titles, long URLs, emoji and CJK text wrap inside a narrow card instead of pushing it wider. ### Semantics Every part takes a `render` prop, so a card can be an `
` with a real heading, header and footer. ### Right to left The action moves to the inline start and the footer follows the reading direction. ## Keyboard A card is not focusable on its own. These keys apply when it holds a ``. | Key | Action | | --- | --- | | `Tab` | Focuses the card link, which draws the focus ring around the whole card, then moves on to each control inside it. | | `Enter` | Follows the card link. | ## Accessibility - The link’s accessible name is its own text, so keep `` inside the title rather than wrapping the whole card in an anchor. - Controls inside a clickable card are lifted above the link’s hit area, so they stay reachable by pointer and keyboard. - Use `render` on `` to give the title the heading level that fits your page. ## API reference Every part renders a `
` by default, accepts its attributes and takes a `render` prop to swap the element. ### Card | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"default" \| "outline" \| "muted"` | `"default"` | | | `size` | `"default" \| "sm"` | `"default"` | Scales spacing, radius and title size together. | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card"` | Target the card in CSS. | | `data-variant` | The current variant. | | `data-size` | The current size. | | `data-link` | Present while the card contains a CardLink. | | `--card-spacing` | Inner spacing shared by every part. Set by the size. | | `--card-radius` | Corner radius. Nested cards derive theirs from the parent. | | `--card-title-size` | Font size used by CardTitle. | ### CardHeader | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card-header"` | A grid that places CardAction in the top end corner. | ### CardTitle | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card-title"` | Target the title. | ### CardDescription | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card-description"` | Target the description. | ### CardAction | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card-action"` | Spans the title and description rows, above a CardLink’s hit area. | ### CardContent | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card-content"` | Passes the card radius and spacing down to nested cards. | ### CardFooter | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="card-footer"` | Target the footer. | ### CardLink | Prop | Type | Default | Description | | --- | --- | --- | --- | | `href` | `string` | – | | | `render` | `ReactElement \| (props, state) => ReactElement` | `` | Pass your router’s link component here. | | Attribute | Description | | --- | --- | | `data-slot="card-link"` | Stretches its hit area over the whole card. | --- # Carousel > Native scroll-snap slides with momentum on touch, mouse dragging, arrow keys, dots, thumbnails and an autoplay that pauses when it should. Docs: https://hextaui.com/docs/carousel Markdown: https://hextaui.com/docs/carousel.md ## Installation ### CLI ```bash npx shadcn@latest add https://hextaui.com/r/carousel.json ``` This adds the component, the HextaUI theme tokens and any HextaUI components it depends on. ### Manual Add the HextaUI theme tokens (https://hextaui.com/docs/installation#theme) to your global CSS if you haven't yet, then install the dependencies. ```bash pnpm add @base-ui/react @tabler/icons-react class-variance-authority cn ``` Copy `components/ui/carousel.tsx`, `components/ui/button.tsx`, `components/ui/number-flow.tsx`, `lib/motion.ts` into your project. Their source is in this page's Markdown. ## Usage ```tsx import { Carousel, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel" ``` ```tsx ... ... ... ``` Slides scroll natively with CSS scroll snap, so touch momentum and trackpad scrolling feel like the platform. A mouse can drag, and the arrow keys move one slide at a time. ## Composition ```text Carousel ├── CarouselContent │ └── CarouselItem ├── CarouselPrevious ├── CarouselNext ├── CarouselDots ├── CarouselCounter ├── CarouselAutoplayToggle └── CarouselThumbnails └── CarouselThumbnail ``` ## Examples ### API Pass `setApi` to get the carousel API, then listen for `select` to show your own position. Next is disabled on the last slide but keeps focus. ### Several per view Items set their own `basis`. Gutters come from the `spacing` prop, so they stay exact at any basis. ### Dots and counter The active dot stretches as the slides scroll and its neighbours make room. The counter only spins the digit that changed. ### Autoplay `autoplay` is off by default and stays off with reduced motion. It pauses on hover, keyboard focus, touch, drag, a hidden tab, or when scrolled out of view, and the active dot fills as the timer runs. ### Thumbnails `` follows the main carousel and scrolls to keep the active thumbnail in view. ### Vertical `orientation="vertical"` needs a height on ``. The buttons move above and below. ### Controlled Pass `index` and `onIndexChange`. Swiping updates your state and your state scrolls the carousel. ### Rewind and start index `rewind` sends Next on the last slide back to the first. `defaultIndex` opens on a slide without a scroll animation. ### Links and focusable content Dragging a link with the mouse scrolls without opening it. Tabbing to a slide that is off screen scrolls it into view. ### Adding and removing slides Dots, the counter and the buttons update as slides come and go. ### Nested Arrow keys, dragging and dots only move the carousel you are in. ### Long content and a single slide Unbroken text wraps inside its slide. With one slide the dots hide and the buttons stay disabled. ### Right to left Slides start on the right, the arrows flip, the Left arrow key moves forward and the dots fill from the right. ## Keyboard Keys work while focus is anywhere inside the carousel, except in text fields and nested carousels. | Key | Action | | --- | --- | | `→` | Next slide. Previous in right-to-left layouts. ↓ in vertical carousels. | | `←` | Previous slide. Next in right-to-left layouts. ↑ in vertical carousels. | | `Tab` | Moves through the buttons, the active dot and the slides’ content, scrolling off-screen slides into view. | | `Enter` `Space` | Activates the focused button, dot or thumbnail. | ## Accessibility - The root is a `region` described as a carousel. Give it an `aria-label`. - Each item is a `group` described as a slide and labelled with its position, like “3 of 5”. - A polite live region announces the new slide after keyboard or button navigation, and stays quiet while autoplay runs. - Dots and thumbnails use a single tab stop, and focus follows the active one. - Previous and Next stay focusable when disabled, so focus is never lost at either end. ## API reference ### Carousel | Prop | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | | | `spacing` | `"none" \| "sm" \| "default" \| "lg"` | `"default"` | Gap between slides. | | `index` | `number` | – | | | `defaultIndex` | `number` | `0` | | | `onIndexChange` | `(index: number) => void` | – | | | `rewind` | `boolean` | `false` | Wrap from the last slide back to the first. | | `mouseDrag` | `boolean` | `true` | Let a mouse drag the slides. | | `autoplay` | `boolean \| { delay?: number }` | `false` | Advance on a timer. delay defaults to 5000ms, minimum 1000ms. | | `setApi` | `(api: CarouselApi) => void` | – | | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="carousel"` | Target the root in CSS. | | `data-orientation` | The orientation. | | `--carousel-spacing` | The gap between slides, set by spacing. | ### CarouselContent | Prop | Type | Default | Description | | --- | --- | --- | --- | | `className` | `string` | – | Applied to the track that holds the slides. | | `viewportClassName` | `string` | – | Applied to the scrolling viewport. | | Attribute | Description | | --- | --- | | `data-slot="carousel-content"` | The scrolling viewport. | | `data-slot="carousel-container"` | The track inside it. | | `data-scrollable` | Present when there is more than one position. | | `data-dragging` | Present while a mouse drag is in progress. | ### CarouselItem | Prop | Type | Default | Description | | --- | --- | --- | --- | | `render` | `ReactElement \| (props, state) => ReactElement` | `
` | | | Attribute | Description | | --- | --- | | `data-slot="carousel-item"` | Set basis-* to show several per view. | ### CarouselPrevious and CarouselNext Both render a `