Radix Primitives Reference
Radix primitives provide behavior for complex interactions while leaving the visual design to your app. Style every Radix part with Tailwind classes so the result matches the app's local design.
Overview
Use native HTML and local Tailwind components for ordinary UI such as buttons, cards, panels, inputs, badges, lists, and page structure.
Reach for direct Radix imports when the interaction itself is complex: dialogs, alert dialogs, dropdown menus, popovers, tabs, selects, accordions, switches, sliders, radio groups, checkboxes, and tooltips.
The generated app runtime depends on the individual Radix React packages, so use direct @radix-ui/react-* imports even when upstream examples show the aggregate radix-ui package.
import * as Dialog from '@radix-ui/react-dialog'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import * as Tabs from '@radix-ui/react-tabs'
Style interactive states with Radix data attributes such as data-state, data-highlighted, data-disabled, data-side, data-align, and data-placeholder.
Dialog and Sheet
A sheet is a Radix dialog styled at the edge of the viewport.
import * as Dialog from '@radix-ui/react-dialog'
import { X } from 'lucide-react'
function DetailsDialog() {
return (
<Dialog.Root>
<Dialog.Trigger className="rounded-full bg-zinc-950 px-4 py-2 text-sm font-medium text-white">
Open details
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-40 bg-black/50" />
<Dialog.Content className="fixed inset-x-0 bottom-0 z-50 rounded-t-3xl bg-white p-6 text-zinc-950 shadow-2xl md:inset-y-4 md:left-auto md:right-4 md:w-[420px] md:rounded-3xl">
<div className="flex items-start justify-between gap-4">
<div>
<Dialog.Title className="text-lg font-semibold">
Project details
</Dialog.Title>
<Dialog.Description className="mt-1 text-sm text-zinc-500">
Review ownership, status, and recent activity.
</Dialog.Description>
</div>
<Dialog.Close className="rounded-full p-2 text-zinc-500 hover:bg-zinc-100">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</Dialog.Close>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
)
}
Menus and Popovers
Use dropdown menus for compact action lists and popovers for lightweight contextual panels.
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { MoreHorizontal } from 'lucide-react'
<DropdownMenu.Root>
<DropdownMenu.Trigger className="rounded-full p-2 text-zinc-500 hover:bg-zinc-100">
<MoreHorizontal className="h-5 w-5" />
<span className="sr-only">Open menu</span>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
align="end"
sideOffset={8}
className="z-50 min-w-44 rounded-2xl border border-zinc-200 bg-white p-1 text-sm text-zinc-900 shadow-xl"
>
<DropdownMenu.Item className="cursor-pointer rounded-xl px-3 py-2 outline-none data-[highlighted]:bg-zinc-100">
Rename
</DropdownMenu.Item>
<DropdownMenu.Item className="cursor-pointer rounded-xl px-3 py-2 text-red-600 outline-none data-[highlighted]:bg-red-50">
Delete
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
Tabs and Selects
Use tabs for meaningful view switching. Use Radix Select when native select is not enough for the app's visual design. Select items require Select.ItemText; use position="popper" when you want popover-style side, offset, and trigger-width positioning.
import * as Tabs from '@radix-ui/react-tabs'
<Tabs.Root defaultValue="overview">
<Tabs.List className="inline-flex rounded-full bg-zinc-100 p-1">
<Tabs.Trigger
value="overview"
className="rounded-full px-4 py-2 text-sm font-medium text-zinc-500 data-[state=active]:bg-white data-[state=active]:text-zinc-950 data-[state=active]:shadow-sm"
>
Overview
</Tabs.Trigger>
<Tabs.Trigger
value="history"
className="rounded-full px-4 py-2 text-sm font-medium text-zinc-500 data-[state=active]:bg-white data-[state=active]:text-zinc-950 data-[state=active]:shadow-sm"
>
History
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="overview" className="mt-4">
Overview content
</Tabs.Content>
<Tabs.Content value="history" className="mt-4">
History content
</Tabs.Content>
</Tabs.Root>
import * as Select from '@radix-ui/react-select'
import { Check, ChevronDown } from 'lucide-react'
<Select.Root defaultValue="weekly">
<Select.Trigger
aria-label="Report cadence"
className="inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-900 data-[placeholder]:text-zinc-400"
>
<Select.Value placeholder="Choose cadence" />
<Select.Icon>
<ChevronDown className="h-4 w-4" />
</Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Content
position="popper"
sideOffset={8}
className="z-50 w-[var(--radix-select-trigger-width)] overflow-hidden rounded-2xl border border-zinc-200 bg-white p-1 text-sm text-zinc-900 shadow-xl"
>
<Select.Viewport>
<Select.Item
value="daily"
className="flex cursor-pointer items-center justify-between rounded-xl px-3 py-2 outline-none data-[highlighted]:bg-zinc-100"
>
<Select.ItemText>Daily</Select.ItemText>
<Select.ItemIndicator>
<Check className="h-4 w-4" />
</Select.ItemIndicator>
</Select.Item>
<Select.Item
value="weekly"
className="flex cursor-pointer items-center justify-between rounded-xl px-3 py-2 outline-none data-[highlighted]:bg-zinc-100"
>
<Select.ItemText>Weekly</Select.ItemText>
<Select.ItemIndicator>
<Check className="h-4 w-4" />
</Select.ItemIndicator>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
Control Primitives
Use these primitives when native controls cannot support the visual treatment.
@radix-ui/react-accordion:Root,Item,Header,Trigger, andContent; style open items withdata-[state=open].@radix-ui/react-checkbox:RootandIndicator;checkedcan betrue,false, or'indeterminate'.@radix-ui/react-radio-group:Root,Item, andIndicator.@radix-ui/react-switch:RootandThumb; styledata-[state=checked]anddata-[state=unchecked].@radix-ui/react-slider:Root,Track,Range, and one or moreThumbparts; values arenumber[].@radix-ui/react-tooltip: wrap related tooltips inTooltip.Provider.