DEV Community

Alex Spinov
Alex Spinov

Posted on

Radix UI Has a Free API That Solves Accessibility Once and For All

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)