Building accessible UI from scratch is painful. Radix UI gives you unstyled, accessible primitives that just work.
What is Radix UI?
Radix UI is a collection of low-level, unstyled, accessible React components. Unlike Material UI or Chakra, Radix gives you behavior and accessibility — you bring the styles.
Quick Start
bun add @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-popover
Dialog (Modal)
import * as Dialog from "@radix-ui/react-dialog";
export function ConfirmDialog() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button className="btn">Delete Account</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-lg">
<Dialog.Title className="text-lg font-bold">
Are you sure?
</Dialog.Title>
<Dialog.Description className="mt-2 text-gray-600">
This action cannot be undone.
</Dialog.Description>
<div className="mt-4 flex gap-3">
<Dialog.Close asChild>
<button className="btn-secondary">Cancel</button>
</Dialog.Close>
<button className="btn-danger">Yes, delete</button>
</div>
<Dialog.Close asChild>
<button className="absolute top-2 right-2" aria-label="Close">
X
</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
What you get for free: focus trapping, Esc to close, click outside to close, screen reader announcements, proper ARIA attributes.
Dropdown Menu
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
export function UserMenu() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button>Options</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content className="bg-white shadow-lg rounded-md p-1 min-w-[160px]">
<DropdownMenu.Item className="px-3 py-2 hover:bg-gray-100 cursor-pointer rounded">
Profile
</DropdownMenu.Item>
<DropdownMenu.Item className="px-3 py-2 hover:bg-gray-100 cursor-pointer rounded">
Settings
</DropdownMenu.Item>
<DropdownMenu.Separator className="h-px bg-gray-200 my-1" />
<DropdownMenu.Item className="px-3 py-2 hover:bg-red-100 text-red-600 cursor-pointer rounded">
Logout
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
}
Tabs
import * as Tabs from "@radix-ui/react-tabs";
export function SettingsTabs() {
return (
<Tabs.Root defaultValue="general">
<Tabs.List className="flex border-b">
<Tabs.Trigger value="general" className="px-4 py-2 data-[state=active]:border-b-2 data-[state=active]:border-blue-500">
General
</Tabs.Trigger>
<Tabs.Trigger value="security" className="px-4 py-2 data-[state=active]:border-b-2 data-[state=active]:border-blue-500">
Security
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="general" className="p-4">
General settings here
</Tabs.Content>
<Tabs.Content value="security" className="p-4">
Security settings here
</Tabs.Content>
</Tabs.Root>
);
}
Why Radix Over Other Libraries
| Feature | Radix UI | Headless UI | Material UI | Chakra UI |
|---|---|---|---|---|
| Styled | No (you style) | No | Yes | Yes |
| Accessible | Full WAI-ARIA | Good | Good | Good |
| Bundle Size | Tiny (per component) | Small | Large | Medium |
| Framework Lock | React | React | React | React |
| Customizable | 100% | High | Medium | High |
The Radix + Tailwind Stack
Radix + Tailwind CSS is the most popular combination. In fact, shadcn/ui is built entirely on Radix primitives styled with Tailwind.
// This is essentially what shadcn/ui does
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out" />
Available Components
Accordion, Alert Dialog, Aspect Ratio, Avatar, Checkbox, Collapsible, Context Menu, Dialog, Dropdown Menu, Form, Hover Card, Label, Menubar, Navigation Menu, Popover, Progress, Radio Group, Scroll Area, Select, Separator, Slider, Switch, Tabs, Toast, Toggle, Toggle Group, Toolbar, Tooltip.
All free. All accessible. All unstyled.
Building data-driven UIs? Check out my web scraping actors on Apify Store — get clean, structured data for your dashboards. For custom solutions, email spinov001@gmail.com.
Top comments (0)