Radix UI is the unstyled component library behind shadcn/ui. Its primitives API solves the hardest UI problems — accessibility, keyboard navigation, focus management — so you never have to.
The Primitive Pattern
Every Radix component is a compound of composable parts:
import * as Dialog from "@radix-ui/react-dialog";
<Dialog.Root>
<Dialog.Trigger asChild>
<button>Open</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded">
<Dialog.Title>Edit Profile</Dialog.Title>
<Dialog.Description>Make changes to your profile.</Dialog.Description>
<Dialog.Close asChild>
<button>Close</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
You get: focus trap, Escape to close, click-outside, ARIA labels, screen reader support — all FREE.
Dropdown Menu with Submenus
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
<DropdownMenu.Root>
<DropdownMenu.Trigger>Options</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>New Tab</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>More Tools</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item>Developer Tools</DropdownMenu.Item>
<DropdownMenu.Item>Task Manager</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
<DropdownMenu.CheckboxItem checked={bookmarksChecked} onCheckedChange={setBookmarksChecked}>
<DropdownMenu.ItemIndicator>✓</DropdownMenu.ItemIndicator>
Show Bookmarks
</DropdownMenu.CheckboxItem>
</DropdownMenu.Content>
</DropdownMenu.Root>
Keyboard navigation (arrows, enter, escape, type-ahead) — built in.
Tabs with Controlled State
import * as Tabs from "@radix-ui/react-tabs";
<Tabs.Root defaultValue="overview" orientation="vertical">
<Tabs.List>
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="analytics">Analytics</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="overview">Overview content</Tabs.Content>
<Tabs.Content value="analytics">Analytics content</Tabs.Content>
<Tabs.Content value="settings">Settings content</Tabs.Content>
</Tabs.Root>
Toast Notifications
import * as Toast from "@radix-ui/react-toast";
<Toast.Provider swipeDirection="right">
<Toast.Root open={open} onOpenChange={setOpen} duration={5000}>
<Toast.Title>Data scraped!</Toast.Title>
<Toast.Description>1,234 records extracted</Toast.Description>
<Toast.Action altText="View results" asChild>
<button>View</button>
</Toast.Action>
</Toast.Root>
<Toast.Viewport className="fixed bottom-0 right-0 p-6" />
</Toast.Provider>
The asChild Pattern
Radix's asChild merges its behavior into YOUR component:
// Your custom button gets Dialog.Trigger behavior
<Dialog.Trigger asChild>
<MyFancyButton variant="primary">Open Dialog</MyFancyButton>
</Dialog.Trigger>
No wrapper divs. No style conflicts. Your component, their behavior.
Building data dashboards? My Apify scraping tools deliver structured data for your Radix-powered UIs.
Custom solution? Email spinov001@gmail.com
Top comments (0)