)
}
```
### Value Format
```tsx
import { Meter, MeterIndicator, MeterLabel, MeterTrack, MeterValue } from "@/components/ui/meter"
export default function MeterValueFormat() {
return (
Currency
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the meter:
| Slot name | Element |
| ----------------- | ------------------------------------ |
| `meter` | Root wrapper |
| `meter-track` | Background track bar |
| `meter-indicator` | Filled indicator bar |
| `meter-label` | Label text |
| `meter-value` | Value text |
### Customization Examples
```css
/* Make the meter track taller */
[data-slot="meter-track"] {
@apply h-4 rounded-lg;
}
/* Custom indicator color */
[data-slot="meter-indicator"] {
@apply bg-gradient-to-r from-blue-500 to-purple-500;
}
```
```tsx
{/* Override track color via className */}
```
## API Reference
### Meter
Root component that provides meter value context.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `number` | - | Current value of the meter |
| `min` | `number` | `0` | Minimum value |
| `max` | `number` | `100` | Maximum value |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Meter content |
All [Base UI Meter.Root props](https://base-ui.com/react/components/meter) are forwarded via `...props`.
### MeterTrack
Background track that contains the indicator.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `"default" \| "success" \| "warning" \| "destructive"` | `"default"` | Color variant for the track |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Track content (typically MeterIndicator) |
All [Base UI Meter.Track props](https://base-ui.com/react/components/meter) are forwarded via `...props`.
### MeterIndicator
The filled portion of the track representing the current value.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `"default" \| "success" \| "warning" \| "destructive"` | `"default"` | Color variant for the indicator |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Meter.Indicator props](https://base-ui.com/react/components/meter) are forwarded via `...props`.
### MeterLabel
Accessible label for the meter.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Label text |
All [Base UI Meter.Label props](https://base-ui.com/react/components/meter) are forwarded via `...props`.
### MeterValue
Displays the current meter value as formatted text.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Custom value rendering |
All [Base UI Meter.Value props](https://base-ui.com/react/components/meter) are forwarded via `...props`.
### meterTrackVariants
A `cva` helper exported for applying meter track styles outside of the `` component.
```tsx
import { meterTrackVariants } from "@/components/ui/meter"
Custom track
```
### meterIndicatorVariants
A `cva` helper exported for applying meter indicator styles outside of the `` component.
```tsx
import { meterIndicatorVariants } from "@/components/ui/meter"
Custom indicator
```
## Accessibility
### Keyboard Interactions
The meter is a non-interactive display element and does not have keyboard interactions. Focus management is handled by surrounding interactive elements.
### ARIA Attributes
- The meter renders with `role="meter"` via the Base UI primitive.
- `aria-valuenow` reflects the current value.
- `aria-valuemin` and `aria-valuemax` define the range.
- `aria-labelledby` is automatically linked to `MeterLabel` when present.
- Screen readers announce the meter value and its label.
## Full Component Source
```tsx
"use client"
import { Meter as MeterPrimitive } from "@base-ui/react/meter"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const meterTrackVariants = cva(
"relative h-2 w-full overflow-hidden rounded-full",
{
variants: {
color: {
default: "bg-primary/20",
success: "bg-success/20",
warning: "bg-warning/20",
destructive: "bg-destructive/20",
},
},
defaultVariants: {
color: "default",
},
}
)
const meterIndicatorVariants = cva(
"h-full transition-[width,background-color] duration-300 ease-smooth motion-reduce:transition-none",
{
variants: {
color: {
default: "bg-primary",
success: "bg-success",
warning: "bg-warning",
destructive: "bg-destructive",
},
},
defaultVariants: {
color: "default",
},
}
)
function Meter({ className, ...props }: MeterPrimitive.Root.Props) {
return (
)
}
function MeterTrack({
className,
color,
...props
}: MeterPrimitive.Track.Props & VariantProps) {
return (
)
}
function MeterIndicator({
className,
color,
...props
}: MeterPrimitive.Indicator.Props &
VariantProps) {
return (
)
}
function MeterLabel({ className, ...props }: MeterPrimitive.Label.Props) {
return (
)
}
function MeterValue({ className, ...props }: MeterPrimitive.Value.Props) {
return (
)
}
export {
Meter,
MeterTrack,
MeterIndicator,
MeterLabel,
MeterValue,
meterIndicatorVariants,
meterTrackVariants,
}
```
# NumberField
> A number input with increment/decrement controls built on Base UI
URL: https://prototyper-ui.com/docs/components/numberfield
Base UI reference: https://base-ui.com/react/components/number-field
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldDemo() {
return (
Width
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/numberfield.json
```
This will add the following files to your project:
- `components/ui/numberfield.tsx`
> **Note:** This component depends on [Field](/docs/components/field). It will be installed automatically.
## Usage
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
```
## Anatomy
```tsx
```
Or with individual step buttons:
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| -------------------------- | ------------------------------ | ------------------------------------------- | -------- |
| `NumberField` | `number-field` | Root provider, manages value state | Yes |
| `NumberFieldGroup` | `number-field-group` | Visual container for input and steppers | Yes |
| `NumberFieldInput` | `number-field-input` | The numeric input element | Yes |
| `NumberFieldIncrement` | `number-field-increment` | Button to increase the value | No |
| `NumberFieldDecrement` | `number-field-decrement` | Button to decrease the value | No |
| `NumberFieldSteppers` | `number-field-steppers` | Convenience wrapper with increment/decrement | No |
| `NumberFieldScrubArea` | `number-field-scrub-area` | Drag-to-scrub area for value adjustment | No |
| `NumberFieldScrubAreaCursor` | `number-field-scrub-area-cursor` | Custom cursor for the scrub area | No |
## Examples
### Currency
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldCurrency() {
return (
Transaction amount
)
}
```
### Description
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldDescription() {
return (
Width
Enter a width in centimeters.
)
}
```
### Disabled
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldDisabled() {
return (
Disabled
)
}
```
### Formatting
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldFormatting() {
return (
Adjust exposure
)
}
```
### Percentages
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldPercentages() {
return (
Sales tax
)
}
```
### Read Only
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldReadonly() {
return (
Read only
)
}
```
### Reusable
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldDescription, FieldLabel } from "@/components/ui/field"
export default function NumberfieldReusable() {
return (
CookiesPlease enter a number
)
}
```
### Step Values
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldStepValues() {
return (
StepStep + minValueStep + minValue + maxValue
)
}
```
### Units
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldUnits() {
return (
Package width
)
}
```
### Validation
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { FieldLabel } from "@/components/ui/field"
export default function NumberFieldValidation() {
return (
Enter your age
)
}
```
### Validation Error
```tsx
import {
NumberField,
NumberFieldGroup,
NumberFieldInput,
NumberFieldSteppers,
} from "@/components/ui/numberfield"
import { Button } from "@/components/ui/button"
import { FieldError, FieldLabel } from "@/components/ui/field"
export default function NumberFieldValidationError() {
return (
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the number field:
| Slot name | Element |
| ------------------------------ | ----------------------------------------- |
| `number-field` | Root wrapper |
| `number-field-group` | Visual container for input and controls |
| `number-field-input` | The `` element |
| `number-field-increment` | Increment button |
| `number-field-decrement` | Decrement button |
| `number-field-steppers` | Steppers container (increment + decrement)|
| `number-field-scrub-area` | Drag-to-scrub interaction area |
| `number-field-scrub-area-cursor` | Custom cursor for scrub area |
### Customization Examples
```css
/* Make the number field group larger */
[data-slot="number-field-group"] {
@apply h-12;
}
/* Style the stepper buttons */
[data-slot="number-field-increment"],
[data-slot="number-field-decrement"] {
@apply px-2 text-primary;
}
```
```tsx
{/* Override size via className on the group */}
```
## API Reference
### NumberField
Root component that manages the number field state. Built on Base UI `NumberField.Root`.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI NumberField props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### NumberFieldGroup
Visual container that wraps the input and stepper controls.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Height of the group container |
| `className` | `string` | - | Additional CSS classes |
All [Base UI NumberField.Group props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### NumberFieldInput
The numeric input element.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI NumberField.Input props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### NumberFieldIncrement
Button that increments the value. Renders a chevron-up icon by default.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Custom icon (replaces default chevron-up) |
All [Base UI NumberField.Increment props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### NumberFieldDecrement
Button that decrements the value. Renders a chevron-down icon by default.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Custom icon (replaces default chevron-down) |
All [Base UI NumberField.Decrement props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### NumberFieldSteppers
Convenience wrapper that renders increment and decrement buttons in a vertical stack.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All standard `div` props are forwarded via `...props`.
### NumberFieldScrubArea
A drag-to-scrub interaction area for adjusting the value by dragging.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI NumberField.ScrubArea props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### NumberFieldScrubAreaCursor
Custom cursor element rendered within the scrub area.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI NumberField.ScrubAreaCursor props](https://base-ui.com/react/components/number-field) are forwarded via `...props`.
### numberFieldGroupVariants
A `cva` helper exported for applying number field group styles outside the component.
```tsx
import { numberFieldGroupVariants } from "@/components/ui/numberfield"
...
```
## Accessibility
### Keyboard Interactions
| Key | Action |
| ------------ | ---------------------------------------------------- |
| `ArrowUp` | Increments the value by one step |
| `ArrowDown` | Decrements the value by one step |
| `Home` | Sets the value to the minimum (if `min` is set) |
| `End` | Sets the value to the maximum (if `max` is set) |
| `Tab` | Moves focus into or out of the number field |
### ARIA Attributes
- The input renders with `role="spinbutton"` via Base UI.
- `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` are set automatically based on the current value and constraints.
- `aria-invalid` is set when validation fails.
- `data-disabled` is set on the group and controls when the field is disabled.
- Increment and decrement buttons are labeled for screen readers.
## Full Component Source
```tsx
"use client"
import * as React from "react"
import { NumberField as NumberFieldPrimitive } from "@base-ui/react/number-field"
import { cva, type VariantProps } from "class-variance-authority"
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function NumberField({ className, ...props }: NumberFieldPrimitive.Root.Props) {
return (
)
}
const numberFieldGroupVariants = cva(
[
"border-field-border bg-field-background rounded-md border shadow-field focus-within:focus-field-ring relative flex w-full items-center overflow-hidden",
"hover-only:border-field-border-hover",
"data-invalid:border-field-border-invalid data-invalid:focus-within:invalid-field-ring",
"transition-[color,background-color,border-color,box-shadow,opacity] duration-150 ease-smooth",
"motion-reduce:transition-none",
"data-disabled:status-disabled",
],
{
variants: {
size: {
sm: "h-8",
default: "h-9",
lg: "h-10",
},
},
defaultVariants: {
size: "default",
},
}
)
function NumberFieldGroup({
className,
size,
...props
}: NumberFieldPrimitive.Group.Props &
VariantProps) {
return (
)
}
function NumberFieldInput({
className,
...props
}: NumberFieldPrimitive.Input.Props) {
return (
)
}
function NumberFieldIncrement({
className,
children,
...props
}: NumberFieldPrimitive.Increment.Props) {
return (
{children ?? }
)
}
function NumberFieldDecrement({
className,
children,
...props
}: NumberFieldPrimitive.Decrement.Props) {
return (
{children ?? }
)
}
function NumberFieldSteppers({
className,
...props
}: React.ComponentProps<"div">) {
return (
)
}
function NumberFieldScrubArea({
className,
...props
}: NumberFieldPrimitive.ScrubArea.Props) {
return (
)
}
function NumberFieldScrubAreaCursor({
className,
...props
}: NumberFieldPrimitive.ScrubAreaCursor.Props) {
return (
)
}
export {
NumberField,
NumberFieldGroup,
numberFieldGroupVariants,
NumberFieldInput,
NumberFieldIncrement,
NumberFieldDecrement,
NumberFieldSteppers,
NumberFieldScrubArea,
NumberFieldScrubAreaCursor,
}
```
# Popover
> A popup anchored to a trigger element built on Base UI
URL: https://prototyper-ui.com/docs/components/popover
Base UI reference: https://base-ui.com/react/components/popover
```tsx
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Switch } from "@/components/ui/switch"
export default function PopoverDemo() {
return (
}>
Settings
Wi-FiBluetoothMute
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/popover.json
```
This will add the following files to your project:
- `components/ui/popover.tsx`
## Usage
```tsx
import {
Popover,
PopoverTrigger,
PopoverContent,
} from "@/components/ui/popover"
Open
Popover content here.
```
## Anatomy
```tsx
{/* your content */}
```
| Sub-component | `data-slot` | Purpose | Required |
| -------------------- | ---------------------- | --------------------------------------------- | -------- |
| `Popover` | `popover` | Root provider, manages open/close state | Yes |
| `PopoverTrigger` | `popover-trigger` | Element that toggles the popover | Yes |
| `PopoverContent` | `popover-content` | The popup panel anchored to the trigger | Yes |
| `PopoverHeader` | `popover-header` | Flex container for title and description | No |
| `PopoverTitle` | `popover-title` | Accessible title for the popover | No |
| `PopoverDescription` | `popover-description` | Accessible description for the popover | No |
## Examples
### Container Padding
```tsx
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
export default function PopoverContainerPadding() {
return (
}>
Container Padding
)
}
```
### Flipping
```tsx
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
export default function PopoverFlipping() {
return (
}>
Default
This is a popover that will flip if it can't fully render below
the button.
}>
shouldFlip=false
This is a popover that won't flip if it can't fully render below
the button.
)
}
```
### Offset
```tsx
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
export default function PopoverOffset() {
return (
}>
Offset
Offset by an additional 50px.
)
}
```
### Position
```tsx
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
export default function PopoverPosition() {
return (
}>
⬅️
In left-to-right, this is on the left. In right-to-left, this is
on the right.
}>
⬆️
This popover is above the button.
}>
⬇️
This popover is below the button.
}>
➡️
In left-to-right, this is on the right. In right-to-left, this is
on the left.
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the popover:
| Slot name | Element |
| ---------------------- | -------------------------------- |
| `popover` | Root provider (no DOM rendered) |
| `popover-trigger` | The trigger element |
| `popover-content` | The popup panel |
| `popover-header` | Header container |
| `popover-title` | Title heading |
| `popover-description` | Description text |
### Customization Examples
```css
/* Make popover wider */
[data-slot="popover-content"] {
@apply min-w-[16rem];
}
/* Style the popover title */
[data-slot="popover-title"] {
@apply text-base font-bold;
}
```
```tsx
{/* Override styles via className */}
Settings
{/* ... */}
```
## API Reference
### Popover
Root component that manages open/close state.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | - | Controlled open state |
| `onOpenChange` | `(open: boolean) => void` | - | Callback when open state changes |
| `defaultOpen` | `boolean` | `false` | Initial open state for uncontrolled usage |
All [Base UI Popover.Root props](https://base-ui.com/react/components/popover) are forwarded via `...props`.
### PopoverTrigger
Element that toggles the popover when clicked.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Popover.Trigger props](https://base-ui.com/react/components/popover) are forwarded via `...props`.
### PopoverContent
The popup panel rendered inside a portal with a positioner.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `"start" \| "center" \| "end"` | `"center"` | Alignment relative to the trigger |
| `alignOffset` | `number` | `0` | Offset from the alignment edge |
| `side` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Preferred side relative to trigger |
| `sideOffset` | `number` | `4` | Gap between trigger and popup |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Popover content |
All [Base UI Popover.Popup props](https://base-ui.com/react/components/popover) are forwarded via `...props`.
### PopoverHeader
Flex column container for title and description.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Header content |
Standard div props are forwarded via `...props`.
### PopoverTitle
Accessible title for the popover.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Popover.Title props](https://base-ui.com/react/components/popover) are forwarded via `...props`.
### PopoverDescription
Accessible description for the popover.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Popover.Description props](https://base-ui.com/react/components/popover) are forwarded via `...props`.
## Accessibility
### Keyboard Interactions
| Key | Action |
| ----------- | ----------------------------------------------------------------- |
| `Escape` | Closes the popover |
| `Tab` | Moves focus to the next focusable element within the popover |
| `Shift+Tab` | Moves focus to the previous focusable element within the popover |
### ARIA Attributes
- `PopoverContent` receives `role="dialog"` by default via Base UI.
- `aria-labelledby` is automatically linked to `PopoverTitle` when present.
- `aria-describedby` is automatically linked to `PopoverDescription` when present.
- Focus moves into the popover when it opens.
- Focus returns to the trigger element when the popover closes.
## Full Component Source
```tsx
"use client"
import * as React from "react"
import { Popover as PopoverPrimitive } from "@base-ui/react/popover"
import { cn } from "@/lib/utils"
function Popover({ ...props }: PopoverPrimitive.Root.Props) {
return
}
function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {
return
}
function PopoverContent({
className,
align = "center",
alignOffset = 0,
side = "bottom",
sideOffset = 4,
...props
}: PopoverPrimitive.Popup.Props &
Pick<
PopoverPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return (
)
}
function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
)
}
function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
return (
)
}
function PopoverDescription({
className,
...props
}: PopoverPrimitive.Description.Props) {
return (
)
}
export {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
}
```
# Progress
> A progress bar showing completion status
URL: https://prototyper-ui.com/docs/components/progress
Base UI reference: https://base-ui.com/react/components/progress
```tsx
"use client"
import React from "react"
import {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress"
export default function ProgressDemo() {
const [progress, setProgress] = React.useState(13)
React.useEffect(() => {
const timer = setTimeout(() => setProgress(80), 500)
return () => clearTimeout(timer)
}, [])
return (
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/progress.json
```
This will add the following files to your project:
- `components/ui/progress.tsx`
## Usage
```tsx
import { Progress, ProgressLabel } from "@/components/ui/progress"
```
## Anatomy
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| ------------------- | -------------------- | ------------------------------------------ | -------- |
| `Progress` | `progress` | Root provider with built-in track/indicator| Yes |
| `ProgressTrack` | `progress-track` | Background track for the indicator | Yes |
| `ProgressIndicator` | `progress-indicator` | Filled portion representing progress | Yes |
| `ProgressLabel` | `progress-label` | Accessible label for the progress bar | No |
| `ProgressValue` | `progress-value` | Displays the current value as text | No |
> **Note:** `ProgressTrack` and `ProgressIndicator` are rendered automatically inside the `Progress` component. You do not need to include them manually.
## Examples
### Custom Format
```tsx
import {
Progress,
ProgressLabel,
} from "@/components/ui/progress"
export default function ProgressCustomFormat() {
return (
)
}
```
### Reusable
```tsx
import {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress"
export default function ProgressReusable() {
return (
)
}
```
### Value Format
```tsx
import {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress"
export default function ProgressValueFormat() {
return (
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the progress bar:
| Slot name | Element |
| -------------------- | ------------------------------------ |
| `progress` | Root wrapper |
| `progress-track` | Background track bar |
| `progress-indicator` | Filled indicator bar |
| `progress-label` | Label text |
| `progress-value` | Value text |
### Customization Examples
```css
/* Make the progress track taller */
[data-slot="progress-track"] {
@apply h-4 rounded-lg;
}
/* Custom indicator with gradient */
[data-slot="progress-indicator"] {
@apply bg-gradient-to-r from-green-500 to-emerald-500;
}
```
```tsx
{/* Override color via the color prop */}
```
## API Reference
### Progress
Root component that provides progress value context and renders the track and indicator internally.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `number \| null` | - | Current progress value, null for indeterminate |
| `min` | `number` | `0` | Minimum value |
| `max` | `number` | `100` | Maximum value |
| `color` | `"default" \| "success" \| "warning" \| "destructive"` | `"default"` | Color variant for the track and indicator |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Progress content (label, value) |
All [Base UI Progress.Root props](https://base-ui.com/react/components/progress) are forwarded via `...props`.
### ProgressTrack
Background track that contains the indicator. Rendered automatically by `Progress`.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `"default" \| "success" \| "warning" \| "destructive"` | `"default"` | Color variant for the track |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Track content (typically ProgressIndicator) |
All [Base UI Progress.Track props](https://base-ui.com/react/components/progress) are forwarded via `...props`.
### ProgressIndicator
The filled portion of the track representing current progress. Rendered automatically by `Progress`.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `"default" \| "success" \| "warning" \| "destructive"` | `"default"` | Color variant for the indicator |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Progress.Indicator props](https://base-ui.com/react/components/progress) are forwarded via `...props`.
### ProgressLabel
Accessible label for the progress bar.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Label text |
All [Base UI Progress.Label props](https://base-ui.com/react/components/progress) are forwarded via `...props`.
### ProgressValue
Displays the current progress value as formatted text.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Custom value rendering |
All [Base UI Progress.Value props](https://base-ui.com/react/components/progress) are forwarded via `...props`.
### progressTrackVariants
A `cva` helper exported for applying progress track styles outside of the `` component.
```tsx
import { progressTrackVariants } from "@/components/ui/progress"
Custom track
```
### progressIndicatorVariants
A `cva` helper exported for applying progress indicator styles outside of the `` component.
```tsx
import { progressIndicatorVariants } from "@/components/ui/progress"
Custom indicator
```
## Accessibility
### Keyboard Interactions
The progress bar is a non-interactive display element and does not have keyboard interactions. Focus management is handled by surrounding interactive elements.
### ARIA Attributes
- The progress bar renders with `role="progressbar"` via the Base UI primitive.
- `aria-valuenow` reflects the current value.
- `aria-valuemin` and `aria-valuemax` define the range.
- When `value` is `null`, the progress bar enters indeterminate mode and `aria-valuenow` is removed.
- `aria-labelledby` is automatically linked to `ProgressLabel` when present.
- The `data-indeterminate` attribute is set when value is `null`, and `data-complete` is set when value equals max.
- Screen readers announce the progress value and its label.
## Full Component Source
```tsx
"use client"
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const progressTrackVariants = cva(
"relative h-2 w-full overflow-hidden rounded-full",
{
variants: {
color: {
default: "bg-primary/20",
success: "bg-success/20",
warning: "bg-warning/20",
destructive: "bg-destructive/20",
},
},
defaultVariants: {
color: "default",
},
}
)
const progressIndicatorVariants = cva(
"h-full transition-[width,background-color] duration-300 ease-smooth motion-reduce:transition-none data-[indeterminate]:animate-pulse motion-reduce:animate-none",
{
variants: {
color: {
default: "bg-primary data-[complete]:bg-primary",
success: "bg-success data-[complete]:bg-success",
warning: "bg-warning data-[complete]:bg-warning",
destructive: "bg-destructive data-[complete]:bg-destructive",
},
},
defaultVariants: {
color: "default",
},
}
)
function Progress({
className,
children,
value,
color,
...props
}: ProgressPrimitive.Root.Props &
VariantProps) {
return (
{children}
)
}
function ProgressTrack({
className,
color,
...props
}: ProgressPrimitive.Track.Props &
VariantProps) {
return (
)
}
function ProgressIndicator({
className,
color,
...props
}: ProgressPrimitive.Indicator.Props &
VariantProps) {
return (
)
}
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
return (
)
}
function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
return (
)
}
export {
Progress,
ProgressTrack,
ProgressIndicator,
ProgressLabel,
ProgressValue,
progressIndicatorVariants,
progressTrackVariants,
}
```
# Radio Group
> A group of radio buttons built on Base UI
URL: https://prototyper-ui.com/docs/components/radio-group
Base UI reference: https://base-ui.com/react/components/radio
```tsx
import { FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupDemo() {
return (
Favorite pet
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/radio-group.json
```
This will add the following files to your project:
- `components/ui/radio-group.tsx`
## Usage
```tsx
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
Option 1Option 2Option 3
```
## Anatomy
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| ---------------- | ---------------------- | ------------------------------------------- | -------- |
| `RadioGroup` | `radio-group` | Root container, manages selection state | Yes |
| `RadioGroupItem` | `radio-group-item` | Individual radio button with built-in indicator | Yes |
| (indicator) | `radio-group-indicator`| Selection dot (rendered internally by RadioGroupItem) | Yes |
## Examples
### Description
```tsx
import { FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupDescription() {
return (
Favorite avatar
Please select an avatar.
)
}
```
### Disabled
```tsx
import { FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupDisabled() {
return (
Favorite sport
)
}
```
### Disabled Individual
```tsx
import { FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupDisabledIndividual() {
return (
Favorite sport
)
}
```
### Orientation
```tsx
import { FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupOrientation() {
return (
Favorite avatar
)
}
```
### Read Only
```tsx
import { FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupReadonly() {
return (
Favorite avatar
)
}
```
### Reusable
```tsx
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { FieldLabel } from "@/components/ui/field"
export default function RadioGroupReusable() {
return (
Favorite sport *
Select a favorite sport
)
}
```
### Validation
```tsx
import { Button } from "@/components/ui/button"
import { FieldError, FieldLabel } from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export default function RadioGroupValidation() {
return (
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the radio group:
| Slot name | Element |
| ---------------------- | -------------------------------------- |
| `radio-group` | Root container for all radio items |
| `radio-group-item` | Individual radio button circle |
| `radio-group-indicator`| The selection dot inside the circle |
### Customization Examples
```css
/* Make radio items larger */
[data-slot="radio-group-item"] {
@apply size-5;
}
/* Custom checked color */
[data-slot="radio-group-item"] {
@apply data-checked:bg-green-600 data-checked:border-green-600;
}
```
```tsx
{/* Override layout via className */}
AB
```
## API Reference
### RadioGroup
Root container that manages which radio item is selected.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | - | Controlled selected value |
| `defaultValue` | `string` | - | Initial selected value for uncontrolled usage |
| `onValueChange` | `(value: string, event: Event) => void` | - | Callback when selection changes |
| `disabled` | `boolean` | `false` | Whether all radio items are disabled |
| `readOnly` | `boolean` | `false` | Whether the group is read-only |
| `required` | `boolean` | `false` | Whether a selection is required |
| `name` | `string` | - | Name attribute for form submission |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Orientation of the radio group |
| `className` | `string` | - | Additional CSS classes |
All [Base UI RadioGroup props](https://base-ui.com/react/components/radio) are forwarded via `...props`.
### RadioGroupItem
An individual radio button with a built-in selection indicator.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | - | Unique value for this radio item (required) |
| `disabled` | `boolean` | `false` | Whether this individual item is disabled |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Radio props](https://base-ui.com/react/components/radio) are forwarded via `...props`.
## Accessibility
### Keyboard Interactions
| Key | Action |
| ------------ | --------------------------------------------------- |
| `ArrowDown` | Moves focus and selection to the next radio item |
| `ArrowRight` | Moves focus and selection to the next radio item |
| `ArrowUp` | Moves focus and selection to the previous radio item|
| `ArrowLeft` | Moves focus and selection to the previous radio item|
| `Tab` | Moves focus into / out of the radio group |
### ARIA Attributes
- `RadioGroup` renders with `role="radiogroup"` via Base UI.
- Each `RadioGroupItem` renders with `role="radio"`.
- `aria-checked` is set to `true` on the selected item and `false` on others.
- `aria-disabled` is set when the group or an individual item is disabled.
- `aria-readonly` is set when the group is read-only.
- `aria-required` is set when the group is required.
- Screen readers announce each radio item label and its selected/unselected state.
## Full Component Source
```tsx
"use client"
import { Radio as RadioPrimitive } from "@base-ui/react/radio"
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
import { cn } from "@/lib/utils"
function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
return (
)
}
function RadioGroupItem({
className,
...props
}: RadioPrimitive.Root.Props) {
return (
)
}
export { RadioGroup, RadioGroupItem }
```
# Select
> A dropdown select input built on Base UI
URL: https://prototyper-ui.com/docs/components/select
Base UI reference: https://base-ui.com/react/components/select
```tsx
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export default function SelectDemo() {
return (
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/select.json
```
This will add the following files to your project:
- `components/ui/select.tsx`
## Usage
```tsx
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from "@/components/ui/select"
```
## Anatomy
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| ---------------------- | ---------------------------- | ---------------------------------------------- | -------- |
| `Select` | `select` | Root provider, manages selection state | Yes |
| `SelectTrigger` | `select-trigger` | Button that opens the dropdown | Yes |
| `SelectValue` | `select-value` | Displays the currently selected value | Yes |
| `SelectContent` | `select-content` | Popup container for items | Yes |
| `SelectItem` | `select-item` | An individual selectable option | Yes |
| `SelectGroup` | `select-group` | Groups related items together | No |
| `SelectLabel` | `select-label` | Label for a group of items | No |
| `SelectSeparator` | `select-separator` | Visual divider between items or groups | No |
| `SelectScrollUpButton` | `select-scroll-up-button` | Scroll indicator at the top of the list | No |
| `SelectScrollDownButton` | `select-scroll-down-button` | Scroll indicator at the bottom of the list | No |
## Examples
### Content
```tsx
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export default function SelectContentDemo() {
const options = [
{ id: 1, name: "Aerospace" },
{ id: 2, name: "Mechanical" },
{ id: 3, name: "Civil" },
{ id: 4, name: "Biomedical" },
{ id: 5, name: "Nuclear" },
{ id: 6, name: "Industrial" },
{ id: 7, name: "Chemical" },
{ id: 8, name: "Agricultural" },
{ id: 9, name: "Electrical" },
]
return (
)
}
```
### Description
```tsx
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export default function SelectDescription() {
return (
)
}
```
### With Icon
```tsx
import { Check } from "lucide-react"
import { Switch, SwitchTrack, SwitchThumb, SwitchIcon } from "@/components/ui/switch"
export default function SwitchWithIcon() {
return (
Notifications
)
}
```
### Label Left
```tsx
import { Switch, SwitchTrack, SwitchThumb } from "@/components/ui/switch"
export default function SwitchLabelLeft() {
return (
Notifications
)
}
```
### Disabled
```tsx
import { Switch, SwitchTrack, SwitchThumb } from "@/components/ui/switch"
export default function SwitchDisabled() {
return (
Airplane Mode
)
}
```
### Read Only
```tsx
import { Switch, SwitchTrack, SwitchThumb } from "@/components/ui/switch"
export default function SwitchReadonly() {
return (
Bluetooth
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the switch:
| Slot name | Element |
| -------------- | ------------------------------ |
| `switch` | Root wrapper (label + track) |
| `switch-track` | The sliding track background |
| `switch-thumb` | The circular thumb indicator |
### Customization Examples
```css
/* Make the switch track wider */
[data-slot="switch-track"] {
@apply w-12 h-6;
}
/* Custom checked track color */
[data-slot="switch"] [data-slot="switch-track"] {
@apply group-data-checked:bg-green-600;
}
```
```tsx
{/* Override styles via className */}
Wide gap label
```
## API Reference
### Switch
Root component that manages on/off state and renders a track with thumb.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` | `boolean` | - | Controlled checked state |
| `defaultChecked` | `boolean` | `false` | Initial checked state for uncontrolled usage |
| `onCheckedChange` | `(checked: boolean, event: Event) => void` | - | Callback when checked state changes |
| `disabled` | `boolean` | `false` | Whether the switch is disabled |
| `readOnly` | `boolean` | `false` | Whether the switch is read-only |
| `required` | `boolean` | `false` | Whether the switch is required |
| `name` | `string` | - | Name attribute for form submission |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Label content rendered next to the switch |
All [Base UI Switch.Root props](https://base-ui.com/react/components/switch) are forwarded via `...props`.
## Accessibility
### Keyboard Interactions
| Key | Action |
| ------- | ------------------------------------ |
| `Space` | Toggles the switch on/off |
| `Tab` | Moves focus to / away from the switch |
### ARIA Attributes
- Renders with `role="switch"` via Base UI.
- `aria-checked` is set to `true` or `false` based on the switch state.
- `aria-disabled` is set when the switch is disabled.
- `aria-readonly` is set when the switch is read-only.
- Screen readers announce the label and the on/off state.
## Full Component Source
```tsx
"use client"
import React from "react"
import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
/* ---------------------------------------------------------------------------
* Internal context — passes size from Switch root to sub-components
* -------------------------------------------------------------------------*/
type SwitchSize = "sm" | "md" | "lg"
type SwitchContextValue = { size: SwitchSize }
const SwitchCtx = React.createContext({ size: "md" })
/* ---------------------------------------------------------------------------
* CVA — track sizing variants
* -------------------------------------------------------------------------*/
const switchVariants = cva(
[
"peer inline-flex shrink-0 cursor-pointer items-center rounded-full",
"bg-border",
"group-data-checked:bg-primary",
"group-data-unchecked:hover-only:bg-accent-hover",
"group-data-checked:hover-only:bg-primary-hover",
"group-focus-visible:focus-ring",
"group-data-disabled:status-disabled",
"group-data-readonly:cursor-default",
"motion-safe:active:scale-[0.97]",
"transition-[color,background-color,box-shadow,opacity] duration-250 ease-smooth",
"motion-reduce:transition-none",
"shadow-field",
],
{
variants: {
size: {
sm: "h-[18px] w-8",
md: "h-[22px] w-10",
lg: "h-[26px] w-12",
},
},
defaultVariants: { size: "md" },
}
)
/* ---------------------------------------------------------------------------
* Switch (Root)
* -------------------------------------------------------------------------*/
function Switch({
className,
children,
size = "md",
...props
}: SwitchPrimitive.Root.Props & { size?: SwitchSize }) {
return (
{children}
)
}
/* ---------------------------------------------------------------------------
* SwitchTrack
* -------------------------------------------------------------------------*/
function SwitchTrack({
className,
size: sizeProp,
children,
...props
}: React.ComponentProps<"div"> & VariantProps) {
const { size: ctxSize } = React.useContext(SwitchCtx)
const size = sizeProp ?? ctxSize
return (
{children}
)
}
/* ---------------------------------------------------------------------------
* SwitchThumb
*
* Rounded rectangle (pill) thumb, wider than tall for a premium feel.
* Positioned via margin-inline-start for RTL support.
*
* Size dimensions:
* sm: 14×20px (h-3.5 w-5) — checked ms-[10px]
* md: 18×24px (h-[18px] w-6) — checked ms-[14px]
* lg: 22×28px (h-[22px] w-7) — checked ms-[18px]
* -------------------------------------------------------------------------*/
const thumbSizeClasses: Record = {
sm: "h-3.5 w-5 ms-0.5 group-data-checked:ms-[10px]",
md: "h-[18px] w-6 ms-0.5 group-data-checked:ms-[14px]",
lg: "h-[22px] w-7 ms-0.5 group-data-checked:ms-[18px]",
}
function SwitchThumb({
className,
children,
...props
}: SwitchPrimitive.Thumb.Props) {
const { size } = React.useContext(SwitchCtx)
return (
{children}
)
}
/* ---------------------------------------------------------------------------
* SwitchIcon — presentational wrapper for icons inside the thumb
* -------------------------------------------------------------------------*/
const iconSizeClasses: Record = {
sm: "[&_svg]:size-2.5",
md: "[&_svg]:size-3",
lg: "[&_svg]:size-3.5",
}
function SwitchIcon({
className,
...props
}: React.ComponentProps<"span">) {
const { size } = React.useContext(SwitchCtx)
return (
)
}
export { Switch, SwitchTrack, SwitchThumb, SwitchIcon, switchVariants }
```
# Tabs
> Tabbed content panels built on Base UI
URL: https://prototyper-ui.com/docs/components/tabs
Base UI reference: https://base-ui.com/react/components/tabs
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsDemo() {
return (
AccountNotificationsBilling
Update your name, email, and profile photo.
Choose which notifications you receive and how.
Manage your subscription plan and payment method.
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/tabs.json
```
This will add the following files to your project:
- `components/ui/tabs.tsx`
## Usage
```tsx
import {
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@/components/ui/tabs"
Tab 1Tab 2Content 1Content 2
```
## Anatomy
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| -------------- | -------------- | ---------------------------------------- | -------- |
| `Tabs` | `tabs` | Root provider, manages active tab state | Yes |
| `TabsList` | `tabs-list` | Container for tab triggers | Yes |
| `TabsTrigger` | `tabs-trigger` | Button that activates a tab panel | Yes |
| `TabsContent` | `tabs-content` | Content panel associated with a trigger | Yes |
## Examples
### Disabled
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsDisabled() {
return (
Mouse SettingsKeyboard SettingsGamepad SettingsMouse SettingsKeyboard SettingsGamepad Settings
)
}
```
### Disabled Dynamic
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsDisabledDynamic() {
let tabs = [
{ id: 1, title: "Mouse settings" },
{ id: 2, title: "Keyboard settings" },
{ id: 3, title: "Gamepad settings" },
]
return (
{tabs.map((item) => (
{item.title}
))}
{tabs.map((item) => (
{item.title}
))}
)
}
```
### Disabled Items
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsDisabledItems() {
return (
Mouse SettingsKeyboard Settings
Gamepad Settings
Mouse SettingsKeyboard SettingsGamepad Settings
)
}
```
### Dynamic
```tsx
"use client"
import React from "react"
import { Button } from "@/components/ui/button"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsDynamic() {
let [tabs, setTabs] = React.useState([
{ id: 1, title: "Tab 1", content: "Tab body 1" },
{ id: 2, title: "Tab 2", content: "Tab body 2" },
{ id: 3, title: "Tab 3", content: "Tab body 3" },
])
let addTab = () => {
setTabs((tabs) => [
...tabs,
{
id: tabs.length + 1,
title: `Tab ${tabs.length + 1}`,
content: `Tab body ${tabs.length + 1}`,
},
])
}
let removeTab = () => {
if (tabs.length > 1) {
setTabs((tabs) => tabs.slice(0, -1))
}
}
return (
{tabs.map((item) => (
{item.title}
))}
Add tab
Remove tab
{tabs.map((item) => (
{item.content}
))}
)
}
```
### Focus
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
import { Input } from "@/components/ui/textfield"
export default function TabsFocus() {
return (
Jane DoeJohn DoeJoe BloggsSenatus Populusque Romanus.Alea jacta est.
)
}
```
### Vertical
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsVertical() {
return (
John DoeJane DoeJoe BloggsThere is no prior chat history with John Doe.There is no prior chat history with Jane Doe.
There is no prior chat history with Joe Bloggs.
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the tabs:
| Slot name | Element |
| -------------- | ------------------------------------ |
| `tabs` | Root wrapper |
| `tabs-list` | Container for tab triggers |
| `tabs-trigger` | Individual tab button |
| `tabs-content` | Tab panel content area |
### Customization Examples
```css
/* Make the tab list full-width */
[data-slot="tabs-list"] {
@apply w-full;
}
/* Custom active tab style */
[data-slot="tabs-trigger"][data-active] {
@apply bg-primary text-primary-foreground;
}
```
```tsx
{/* Override list variant via className */}
Tab 1Tab 2
```
## API Reference
### Tabs
Root component that manages active tab state and context.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultValue` | `any` | - | Initial active tab for uncontrolled usage |
| `value` | `any` | - | Controlled active tab value |
| `onValueChange` | `(value: any) => void` | - | Callback when the active tab changes |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction of the tabs |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Tabs content |
All [Base UI Tabs.Root props](https://base-ui.com/react/components/tabs) are forwarded via `...props`.
### TabsList
Container for tab triggers with variant styling.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "line"` | `"default"` | Visual style variant for the tab list |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Tab triggers |
All [Base UI Tabs.List props](https://base-ui.com/react/components/tabs) are forwarded via `...props`.
### TabsTrigger
Button that activates a tab panel when clicked.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `any` | - | Value linking this trigger to a panel |
| `disabled` | `boolean` | `false` | Whether the trigger is disabled |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Tab label content |
All [Base UI Tabs.Tab props](https://base-ui.com/react/components/tabs) are forwarded via `...props`.
### TabsContent
Content panel that is shown when its associated trigger is active.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `any` | - | Value linking this panel to a trigger |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Panel content |
All [Base UI Tabs.Panel props](https://base-ui.com/react/components/tabs) are forwarded via `...props`.
### tabsListVariants
A `cva` helper exported for use outside of the `` component (e.g., applying tab list styles to custom elements).
```tsx
import { tabsListVariants } from "@/components/ui/tabs"
Custom tab list
```
## Accessibility
### Keyboard Interactions
| Key | Action |
| ---------------- | ------------------------------------------------------------------- |
| `ArrowRight` | Moves focus to the next tab trigger (horizontal orientation) |
| `ArrowLeft` | Moves focus to the previous tab trigger (horizontal orientation) |
| `ArrowDown` | Moves focus to the next tab trigger (vertical orientation) |
| `ArrowUp` | Moves focus to the previous tab trigger (vertical orientation) |
| `Home` | Moves focus to the first tab trigger |
| `End` | Moves focus to the last tab trigger |
| `Space` | Activates the focused tab trigger |
| `Enter` | Activates the focused tab trigger |
| `Tab` | Moves focus into the active tab panel, then to the next element |
### ARIA Attributes
- The tab list renders with `role="tablist"`.
- Each trigger renders with `role="tab"` and `aria-selected` indicating whether it is active.
- Each content panel renders with `role="tabpanel"`.
- `aria-controls` on each trigger links to its corresponding panel.
- `aria-labelledby` on each panel links back to its corresponding trigger.
- `aria-orientation` is set on the tab list based on the `orientation` prop.
- `aria-disabled` is set on disabled tab triggers.
## Full Component Source
```tsx
"use client"
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
function Tabs({
className,
orientation = "horizontal",
...props
}: TabsPrimitive.Root.Props) {
return (
)
}
const tabsListVariants = cva(
"rounded-lg p-[3px] group-data-horizontal/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col",
{
variants: {
variant: {
default: "bg-surface-secondary",
line: "gap-1 bg-transparent border-b border-border p-0",
},
},
defaultVariants: {
variant: "default",
},
}
)
function TabsList({
className,
variant = "default",
...props
}: TabsPrimitive.List.Props & VariantProps) {
return (
)
}
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
return (
)
}
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
return (
)
}
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
```
## All Examples
### tabs-line
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsLine() {
return (
OverviewActivitySettings
A summary of your project status and key metrics.
Recent commits, pull requests, and team updates.
Configure repository access, webhooks, and integrations.
)
}
```
### tabs-settings
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
import { FieldLabel } from "@/components/ui/field"
import { Input, TextField } from "@/components/ui/textfield"
import { Button } from "@/components/ui/button"
export default function TabsSettings() {
return (
AccountPassword
)
}
```
### tabs-with-icons
```tsx
import { Music, ImageIcon, Video } from "lucide-react"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function TabsWithIcons() {
return (
Music
Photos
Videos
Browse and manage your music collection.
View, organize, and share your photo albums.
Watch and manage your saved video clips.
)
}
```
# TextField
> A text input with label and validation built on Base UI
URL: https://prototyper-ui.com/docs/components/textfield
Base UI reference: https://base-ui.com/react/components/field
```tsx
import { FieldLabel } from "@/components/ui/field"
import { Input, TextField } from "@/components/ui/textfield"
export default function TextFieldDemo() {
return (
First name
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/textfield.json
```
This will add the following files to your project:
- `components/ui/textfield.tsx`
> **Note:** This component depends on [Field](/docs/components/field). It will be installed automatically.
## Usage
```tsx
import { TextField, Input } from "@/components/ui/textfield"
import { FieldLabel } from "@/components/ui/field"
Name
```
## Anatomy
```tsx
```
Or with `TextArea`:
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| ----------------- | -------------------- | -------------------------------------------- | -------- |
| `TextField` | `text-field` | Root field wrapper, provides form context | Yes |
| `Input` | `input` | Single-line text input | Yes* |
| `TextArea` | `textarea` | Multi-line text input | Yes* |
| `FieldLabel` | `field-label` | Label for the text field | No |
| `FieldDescription`| `field-description` | Descriptive helper text | No |
| `FieldError` | `field-error` | Validation error message | No |
\* Use either `Input` or `TextArea`, not both.
## Examples
### Description
```tsx
import { FieldLabel } from "@/components/ui/field"
import { Input, TextField } from "@/components/ui/textfield"
export default function TextFieldDescription() {
return (
Email
Enter an email for us to contact you about your order.
)
}
```
### Disabled
```tsx
import { FieldLabel } from "@/components/ui/field"
import { Input, TextField } from "@/components/ui/textfield"
export default function TextFieldDisabled() {
return (
Email
)
}
```
### Multiline
```tsx
import { FieldLabel } from "@/components/ui/field"
import { TextArea, TextField } from "@/components/ui/textfield"
export default function TextFieldMultiline() {
return (
Comment
)
}
```
### Read Only
```tsx
import { FieldLabel } from "@/components/ui/field"
import { Input, TextField } from "@/components/ui/textfield"
export default function TextFieldReadonly() {
return (
Email
)
}
```
### Reusable
```tsx
import { ProtoTextField } from "@/components/ui/textfield"
export default function TextfieldReusable() {
return (
)
}
```
### Validation
```tsx
import { Button } from "@/components/ui/button"
import { FieldError, FieldLabel } from "@/components/ui/field"
import { Input, TextField } from "@/components/ui/textfield"
export default function TextFieldValidation() {
return (
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the text field:
| Slot name | Element |
| ------------------ | ------------------------------ |
| `text-field` | Root wrapper (`Field.Root`) |
| `input` | The `` element |
| `textarea` | The `
# Toggle
> A two-state toggle button built on Base UI
URL: https://prototyper-ui.com/docs/components/toggle
Base UI reference: https://base-ui.com/react/components/toggle
```tsx
import { FontBoldIcon } from "@radix-ui/react-icons"
import { Toggle } from "@/components/ui/toggle"
export default function ToggleDemo() {
return (
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/toggle.json
```
This will add the following files to your project:
- `components/ui/toggle.tsx`
## Usage
```tsx
import { Toggle } from "@/components/ui/toggle"
Bold
```
## Examples
### Outline
```tsx
import { FontItalicIcon } from "@radix-ui/react-icons"
import { Toggle } from "@/components/ui/toggle"
export default function ToggleOutline() {
return (
)
}
```
### Small
```tsx
import { FontItalicIcon } from "@radix-ui/react-icons"
import { Toggle } from "@/components/ui/toggle"
export default function ToggleSm() {
return (
)
}
```
### Large
```tsx
import { FontItalicIcon } from "@radix-ui/react-icons"
import { Toggle } from "@/components/ui/toggle"
export default function ToggleLg() {
return (
)
}
```
### Disabled
```tsx
import { UnderlineIcon } from "@radix-ui/react-icons"
import { Toggle } from "@/components/ui/toggle"
export default function ToggleDisabled() {
return (
)
}
```
### With Text
```tsx
import { FontItalicIcon } from "@radix-ui/react-icons"
import { Toggle } from "@/components/ui/toggle"
export default function ToggleWithText() {
return (
Italic
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target the toggle in CSS:
| Slot name | Element |
| --------- | ------------------- |
| `toggle` | The `` root |
### Customization Examples
```css
/* Style all pressed toggles */
[data-slot="toggle"][aria-pressed="true"] {
@apply bg-primary text-primary-foreground;
}
```
```tsx
{/* Use className for one-off overrides */}
Pill Toggle
```
## API Reference
### Toggle
A two-state toggle button that can be pressed or unpressed.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "outline"` | `"default"` | Visual style variant |
| `size` | `"default" \| "sm" \| "lg"` | `"default"` | Size of the toggle |
| `pressed` | `boolean` | - | Controlled pressed state |
| `onPressedChange` | `(pressed: boolean) => void` | - | Callback when pressed state changes |
| `defaultPressed` | `boolean` | `false` | Initial pressed state for uncontrolled usage |
| `disabled` | `boolean` | `false` | Whether the toggle is disabled |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Toggle content |
All [Base UI Toggle props](https://base-ui.com/react/components/toggle) are forwarded via `...props`.
### toggleVariants
A `cva` helper exported for use outside of the `` component.
```tsx
import { toggleVariants } from "@/components/ui/toggle"
Custom Toggle
```
## Accessibility
### Keyboard Interactions
| Key | Action |
| ------- | -------------------------------------- |
| `Space` | Toggles the pressed state |
| `Enter` | Toggles the pressed state |
| `Tab` | Moves focus to / away from the toggle |
### ARIA Attributes
- Renders as a `` element with `aria-pressed` attribute.
- `aria-pressed="true"` when the toggle is in the pressed state, `aria-pressed="false"` when unpressed.
- Screen readers announce the toggle as a toggle button with its current pressed/unpressed state.
- `disabled` attribute is set when the toggle is disabled.
## Full Component Source
```tsx
"use client"
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const toggleVariants = cva(
[
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-[color,background-color,border-color,box-shadow,opacity] duration-150 ease-smooth",
"motion-safe:active:scale-[0.97]",
"motion-reduce:transition-none",
"no-highlight",
"focus-visible:focus-ring",
"disabled:status-disabled",
"hover-only:bg-muted hover-only:text-muted-foreground",
"aria-pressed:bg-accent aria-pressed:text-accent-foreground",
"[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"group/toggle",
],
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-field-border bg-transparent shadow-field hover-only:bg-accent hover-only:text-accent-foreground",
},
size: {
default: "h-9 px-3",
sm: "h-8 px-2",
lg: "h-10 px-3",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Toggle({
className,
variant = "default",
size = "default",
...props
}: TogglePrimitive.Props & VariantProps) {
return (
)
}
export { Toggle, toggleVariants }
```
# Toolbar
> A toolbar container with keyboard navigation built on Base UI
URL: https://prototyper-ui.com/docs/components/toolbar
Base UI reference: https://base-ui.com/react/components/toolbar
```tsx
"use client"
import {
FontBoldIcon,
FontItalicIcon,
UnderlineIcon,
} from "@radix-ui/react-icons"
import { Button } from "@/components/ui/button"
import {
Checkbox,
CheckboxControl,
CheckboxIndicator,
} from "@/components/ui/checkbox"
import { Toggle } from "@/components/ui/toggle"
import { Toolbar, ToolbarSeparator } from "@/components/ui/toolbar"
export default function ToolbarDemo() {
return (
CopyPasteCut
Night Mode
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/toolbar.json
```
This will add the following files to your project:
- `components/ui/toolbar.tsx`
## Usage
```tsx
import {
Toolbar,
ToolbarButton,
ToolbarSeparator,
} from "@/components/ui/toolbar"
BoldItalicLink
```
## Anatomy
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| ------------------ | -------------------- | -------------------------------------------- | -------- |
| `Toolbar` | `toolbar` | Root container with keyboard navigation | Yes |
| `ToolbarButton` | `toolbar-button` | A button within the toolbar | No |
| `ToolbarLink` | `toolbar-link` | A link within the toolbar | No |
| `ToolbarGroup` | `toolbar-group` | Groups related toolbar items together | No |
| `ToolbarSeparator` | `toolbar-separator` | Visual separator between toolbar items | No |
| `ToolbarInput` | `toolbar-input` | A text input within the toolbar | No |
## Examples
### Vertical
```tsx
"use client"
import {
CursorArrowIcon,
MagicWandIcon,
MoveIcon,
Pencil1Icon,
Pencil2Icon,
} from "@radix-ui/react-icons"
import { Button } from "@/components/ui/button"
import { Toolbar, ToolbarSeparator } from "@/components/ui/toolbar"
export default function ToolbarVerticalDemo() {
return (
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the toolbar:
| Slot name | Element |
| ------------------- | ------------------------------------- |
| `toolbar` | Root toolbar container |
| `toolbar-button` | Button element within the toolbar |
| `toolbar-link` | Link element within the toolbar |
| `toolbar-group` | Group wrapper for related items |
| `toolbar-separator` | Separator line between items |
| `toolbar-input` | Text input within the toolbar |
### Customization Examples
```css
/* Add a border around the toolbar */
[data-slot="toolbar"] {
@apply rounded-lg border bg-background p-1;
}
/* Style toolbar buttons with more padding */
[data-slot="toolbar-button"] {
@apply px-4;
}
```
```tsx
{/* Override styles via className */}
Bold
```
## API Reference
### Toolbar
Root container that provides keyboard navigation between toolbar items.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction of the toolbar |
| `disabled` | `boolean` | `false` | Disables all items in the toolbar |
| `loop` | `boolean` | `true` | Whether keyboard navigation loops around |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Toolbar content |
All [Base UI Toolbar.Root props](https://base-ui.com/react/components/toolbar) are forwarded via `...props`.
### ToolbarButton
A button within the toolbar that participates in keyboard navigation.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | Whether the button is disabled |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Button content |
All [Base UI Toolbar.Button props](https://base-ui.com/react/components/toolbar) are forwarded via `...props`.
### ToolbarLink
A link within the toolbar that participates in keyboard navigation.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `href` | `string` | - | The URL the link points to |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Link content |
All [Base UI Toolbar.Link props](https://base-ui.com/react/components/toolbar) are forwarded via `...props`.
### ToolbarGroup
Groups related toolbar items together visually and semantically.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | Disables all items in the group |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Group content |
All [Base UI Toolbar.Group props](https://base-ui.com/react/components/toolbar) are forwarded via `...props`.
### ToolbarSeparator
A visual separator between toolbar items or groups.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Direction of the separator line |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Toolbar.Separator props](https://base-ui.com/react/components/toolbar) are forwarded via `...props`.
### ToolbarInput
A text input within the toolbar that participates in keyboard navigation.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Toolbar.Input props](https://base-ui.com/react/components/toolbar) are forwarded via `...props`.
## Accessibility
### Keyboard Interactions
| Key | Action |
| ---------------- | ----------------------------------------------------------- |
| `Tab` | Moves focus into the toolbar (focuses the first/last active item) |
| `ArrowRight` | Moves focus to the next toolbar item (horizontal orientation) |
| `ArrowLeft` | Moves focus to the previous toolbar item (horizontal orientation) |
| `ArrowDown` | Moves focus to the next toolbar item (vertical orientation) |
| `ArrowUp` | Moves focus to the previous toolbar item (vertical orientation) |
| `Home` | Moves focus to the first toolbar item |
| `End` | Moves focus to the last toolbar item |
| `Space` / `Enter`| Activates the focused toolbar button or link |
### ARIA Attributes
- `Toolbar` renders with `role="toolbar"` by default via Base UI.
- `aria-orientation` is set to match the `orientation` prop.
- `aria-disabled` is set on the toolbar when `disabled` is `true`.
- Toolbar items are part of a single tab stop; arrow keys navigate between items within the toolbar.
- Focus management follows the roving tabindex pattern so only one item is tabbable at a time.
## Full Component Source
```tsx
"use client"
import { Toolbar as ToolbarPrimitive } from "@base-ui/react/toolbar"
import { cn } from "@/lib/utils"
function Toolbar({ className, ...props }: ToolbarPrimitive.Root.Props) {
return (
)
}
function ToolbarButton({
className,
...props
}: ToolbarPrimitive.Button.Props) {
return (
)
}
function ToolbarLink({ className, ...props }: ToolbarPrimitive.Link.Props) {
return (
)
}
function ToolbarGroup({ className, ...props }: ToolbarPrimitive.Group.Props) {
return (
)
}
function ToolbarSeparator({
className,
orientation = "vertical",
...props
}: ToolbarPrimitive.Separator.Props) {
return (
)
}
function ToolbarInput({ className, ...props }: ToolbarPrimitive.Input.Props) {
return (
)
}
export {
Toolbar,
ToolbarButton,
ToolbarLink,
ToolbarGroup,
ToolbarSeparator,
ToolbarInput,
}
```
# Tooltip
> A tooltip that appears on hover built on Base UI
URL: https://prototyper-ui.com/docs/components/tooltip
Base UI reference: https://base-ui.com/react/components/tooltip
```tsx
import { Pencil1Icon } from "@radix-ui/react-icons"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
export default function TooltipDemo() {
return (
}>
Edit
)
}
```
## Installation
```bash
pnpm dlx shadcn@latest add https://prototyper-ui.com/r/tooltip.json
```
This will add the following files to your project:
- `components/ui/tooltip.tsx`
## Usage
```tsx
import {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider,
} from "@/components/ui/tooltip"
Hover meTooltip text
```
## Anatomy
```tsx
```
| Sub-component | `data-slot` | Purpose | Required |
| ------------------ | -------------------- | ---------------------------------------------- | -------- |
| `TooltipProvider` | `tooltip-provider` | Shared delay and configuration for tooltips | Yes |
| `Tooltip` | `tooltip` | Root provider, manages open/close state | Yes |
| `TooltipTrigger` | `tooltip-trigger` | Element that triggers the tooltip on hover | Yes |
| `TooltipContent` | `tooltip-content` | The popup displaying tooltip text | Yes |
## Examples
### Cross Offset
```tsx
import { ArrowRightIcon } from "@radix-ui/react-icons"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
export default function TooltipOffset() {
return (
}>
This will shift over to the right.
)
}
```
### Disabled
```tsx
import { Pencil1Icon } from "@radix-ui/react-icons"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
export default function TooltipDisabled() {
return (
}>
Edit
)
}
```
### Offset
```tsx
import { ArrowUpIcon } from "@radix-ui/react-icons"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
export default function TooltipOffset() {
return (
}>
This will shift up.
)
}
```
### Position
```tsx
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
export default function TooltipPosition() {
return (
}>
Left
Add to library
}>
Up
Add to library
}>
Down
Add to library
}>
Right
Add to library
)
}
```
## Styling
### Data Slots
Use `data-slot` attributes to target specific parts of the tooltip:
| Slot name | Element |
| -------------------- | -------------------------------- |
| `tooltip-provider` | Shared provider (no DOM rendered)|
| `tooltip` | Root provider (no DOM rendered) |
| `tooltip-trigger` | The trigger element |
| `tooltip-content` | The popup panel |
| `tooltip-arrow` | Arrow element pointing to trigger|
### Customization Examples
```css
/* Change tooltip background */
[data-slot="tooltip-content"] {
@apply bg-primary text-primary-foreground;
}
/* Style the tooltip arrow */
[data-slot="tooltip-arrow"] {
@apply bg-primary fill-primary;
}
```
```tsx
{/* Override styles via className */}
This action is destructive
```
## API Reference
### TooltipProvider
Shared provider that configures delay and grouping behavior for all child tooltips.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `delay` | `number` | `0` | Delay in ms before tooltips appear |
All [Base UI Tooltip.Provider props](https://base-ui.com/react/components/tooltip) are forwarded via `...props`.
### Tooltip
Root component that manages open/close state for a single tooltip.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | - | Controlled open state |
| `onOpenChange` | `(open: boolean) => void` | - | Callback when open state changes |
| `defaultOpen` | `boolean` | `false` | Initial open state for uncontrolled usage |
All [Base UI Tooltip.Root props](https://base-ui.com/react/components/tooltip) are forwarded via `...props`.
### TooltipTrigger
Element that triggers the tooltip on hover and focus.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | - | Additional CSS classes |
All [Base UI Tooltip.Trigger props](https://base-ui.com/react/components/tooltip) are forwarded via `...props`.
### TooltipContent
The popup panel displaying the tooltip text, rendered inside a portal with a positioner.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `side` | `"top" \| "bottom" \| "left" \| "right"` | `"top"` | Preferred side relative to trigger |
| `sideOffset` | `number` | `8` | Gap between trigger and popup |
| `align` | `"start" \| "center" \| "end"` | `"center"` | Alignment relative to the trigger |
| `alignOffset` | `number` | `0` | Offset from the alignment edge |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Tooltip content |
All [Base UI Tooltip.Popup props](https://base-ui.com/react/components/tooltip) are forwarded via `...props`.
## Accessibility
### Keyboard Interactions
| Key | Action |
| --------- | ----------------------------------------------- |
| `Tab` | Moves focus to the trigger, showing the tooltip |
| `Escape` | Closes the tooltip |
### ARIA Attributes
- `TooltipContent` receives `role="tooltip"` via Base UI.
- The trigger element receives `aria-describedby` pointing to the tooltip content.
- Tooltips appear on both hover and focus, ensuring keyboard accessibility.
- The tooltip includes an arrow element for visual connection to the trigger.
- Screen readers announce tooltip content when the trigger receives focus.
## Full Component Source
```tsx
"use client"
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
import { cn } from "@/lib/utils"
function TooltipProvider({
delay = 0,
...props
}: TooltipPrimitive.Provider.Props) {
return (
)
}
function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
return
}
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
return
}
function TooltipContent({
className,
side = "top",
sideOffset = 8,
align = "center",
alignOffset = 0,
children,
...props
}: TooltipPrimitive.Popup.Props &
Pick<
TooltipPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return (
{children}
)
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
```