Zum Inhalt springen

Radix UI Cheat Sheet

Overview

Radix UI is a collection of low-level, unstyled, and accessible React component primitives. Each primitive handles complex behaviors like keyboard navigation, focus management, screen reader support, and collision-aware positioning while leaving all visual styling to you. This makes Radix ideal for building custom design systems without fighting against pre-existing styles.

Radix primitives follow WAI-ARIA patterns and are fully composable. They are used as the foundation for popular styled libraries like shadcn/ui. Each component is published as a separate npm package, so you only install what you need. Radix also provides additional utilities including Colors (a color system), Icons, and Themes (a pre-styled component library built on the primitives).

Installation

# Install individual primitives as needed
npm install @radix-ui/react-dialog
npm install @radix-ui/react-dropdown-menu
npm install @radix-ui/react-popover
npm install @radix-ui/react-tabs
npm install @radix-ui/react-tooltip
npm install @radix-ui/react-accordion
npm install @radix-ui/react-select
npm install @radix-ui/react-slider
npm install @radix-ui/react-switch
npm install @radix-ui/react-checkbox
npm install @radix-ui/react-radio-group
npm install @radix-ui/react-toggle
npm install @radix-ui/react-toggle-group
npm install @radix-ui/react-avatar
npm install @radix-ui/react-hover-card
npm install @radix-ui/react-navigation-menu
npm install @radix-ui/react-context-menu
npm install @radix-ui/react-alert-dialog
npm install @radix-ui/react-toast

# Install Radix Themes (pre-styled components)
npm install @radix-ui/themes

# Install Radix Colors
npm install @radix-ui/colors

Core Components

Dialog (Modal)

import * as Dialog from "@radix-ui/react-dialog"

function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        <button>Open Dialog</button>
      </Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Overlay className="dialog-overlay" />
        <Dialog.Content className="dialog-content">
          <Dialog.Title>Edit Profile</Dialog.Title>
          <Dialog.Description>
            Make changes to your profile here.
          </Dialog.Description>
          <input placeholder="Name" />
          <Dialog.Close asChild>
            <button>Save</button>
          </Dialog.Close>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"

function MyDropdown() {
  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger asChild>
        <button>Options</button>
      </DropdownMenu.Trigger>
      <DropdownMenu.Portal>
        <DropdownMenu.Content className="dropdown-content" sideOffset={5}>
          <DropdownMenu.Item className="dropdown-item">
            New File
          </DropdownMenu.Item>
          <DropdownMenu.Item className="dropdown-item">
            Open File
          </DropdownMenu.Item>
          <DropdownMenu.Separator className="dropdown-separator" />
          <DropdownMenu.Sub>
            <DropdownMenu.SubTrigger className="dropdown-item">
              Export As
            </DropdownMenu.SubTrigger>
            <DropdownMenu.Portal>
              <DropdownMenu.SubContent className="dropdown-content">
                <DropdownMenu.Item className="dropdown-item">PNG</DropdownMenu.Item>
                <DropdownMenu.Item className="dropdown-item">SVG</DropdownMenu.Item>
                <DropdownMenu.Item className="dropdown-item">PDF</DropdownMenu.Item>
              </DropdownMenu.SubContent>
            </DropdownMenu.Portal>
          </DropdownMenu.Sub>
          <DropdownMenu.Separator className="dropdown-separator" />
          <DropdownMenu.CheckboxItem
            className="dropdown-item"
            checked={true}
          >
            <DropdownMenu.ItemIndicator>✓</DropdownMenu.ItemIndicator>
            Show Grid
          </DropdownMenu.CheckboxItem>
          <DropdownMenu.Arrow />
        </DropdownMenu.Content>
      </DropdownMenu.Portal>
    </DropdownMenu.Root>
  )
}

Tabs

import * as Tabs from "@radix-ui/react-tabs"

function MyTabs() {
  return (
    <Tabs.Root defaultValue="tab1" className="tabs-root">
      <Tabs.List className="tabs-list" aria-label="Manage settings">
        <Tabs.Trigger className="tabs-trigger" value="tab1">General</Tabs.Trigger>
        <Tabs.Trigger className="tabs-trigger" value="tab2">Security</Tabs.Trigger>
        <Tabs.Trigger className="tabs-trigger" value="tab3">Billing</Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content className="tabs-content" value="tab1">
        <p>General settings content</p>
      </Tabs.Content>
      <Tabs.Content className="tabs-content" value="tab2">
        <p>Security settings content</p>
      </Tabs.Content>
      <Tabs.Content className="tabs-content" value="tab3">
        <p>Billing settings content</p>
      </Tabs.Content>
    </Tabs.Root>
  )
}

Available Primitives

ComponentPackageDescription
Accordionreact-accordionCollapsible content sections
Alert Dialogreact-alert-dialogModal requiring acknowledgment
Aspect Ratioreact-aspect-ratioMaintain width/height ratio
Avatarreact-avatarUser avatar with fallback
Checkboxreact-checkboxCheckbox input
Collapsiblereact-collapsibleExpandable content
Context Menureact-context-menuRight-click menu
Dialogreact-dialogModal overlay
Dropdown Menureact-dropdown-menuClick-triggered menu
Hover Cardreact-hover-cardContent on hover
Navigation Menureact-navigation-menuSite navigation
Popoverreact-popoverFloating content panel
Progressreact-progressProgress indicator
Radio Groupreact-radio-groupRadio button group
Selectreact-selectCustom select dropdown
Separatorreact-separatorVisual divider
Sliderreact-sliderRange input
Switchreact-switchToggle switch
Tabsreact-tabsTabbed interface
Toastreact-toastNotification popups
Togglereact-toggleToggle button
Toolbarreact-toolbarToolbar with groups
Tooltipreact-tooltipHover tooltip

Configuration

Styling with CSS

/* Radix provides data attributes for styling states */

/* Open/closed state */
[data-state="open"] {
  background-color: #f0f0f0;
}
[data-state="closed"] {
  background-color: transparent;
}

/* Dialog overlay animation */
.dialog-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.5);
}
.dialog-overlay[data-state="open"] {
  animation: fadeIn 150ms ease-out;
}
.dialog-overlay[data-state="closed"] {
  animation: fadeOut 150ms ease-in;
}

/* Dropdown content */
.dropdown-content {
  min-width: 220px;
  background: white;
  border-radius: 6px;
  padding: 5px;
  box-shadow: 0 10px 38px -10px rgba(0, 0, 0, 0.35);
}
.dropdown-content[data-side="top"] { animation-name: slideDown; }
.dropdown-content[data-side="bottom"] { animation-name: slideUp; }

/* Highlighted item */
.dropdown-item[data-highlighted] {
  background-color: #4c9aff;
  color: white;
}

/* Disabled item */
.dropdown-item[data-disabled] {
  opacity: 0.5;
  pointer-events: none;
}

Styling with Tailwind CSS

<Dialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out" />

<Dialog.Content className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out" />

<DropdownMenu.Item className="flex cursor-pointer items-center rounded-md px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-blue-500 data-[highlighted]:text-white" />

Advanced Usage

Controlled Components

import { useState } from "react"
import * as Dialog from "@radix-ui/react-dialog"

function ControlledDialog() {
  const [open, setOpen] = useState(false)

  return (
    <Dialog.Root open={open} onOpenChange={setOpen}>
      <Dialog.Trigger asChild>
        <button>Open</button>
      </Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Content>
          <p>Controlled dialog content</p>
          <button onClick={() => setOpen(false)}>Close programmatically</button>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}

Composition with asChild

// asChild merges props onto the child element instead of rendering a default element
<Dialog.Trigger asChild>
  <MyCustomButton variant="primary">Open</MyCustomButton>
</Dialog.Trigger>

// Without asChild, Radix renders its own <button>
<Dialog.Trigger>Open</Dialog.Trigger>

Toast Notifications

import * as Toast from "@radix-ui/react-toast"

function Notifications() {
  const [open, setOpen] = useState(false)

  return (
    <Toast.Provider swipeDirection="right">
      <button onClick={() => setOpen(true)}>Show Toast</button>
      <Toast.Root className="toast" open={open} onOpenChange={setOpen}>
        <Toast.Title>Success</Toast.Title>
        <Toast.Description>Your changes have been saved.</Toast.Description>
        <Toast.Action altText="Undo" asChild>
          <button>Undo</button>
        </Toast.Action>
        <Toast.Close>Dismiss</Toast.Close>
      </Toast.Root>
      <Toast.Viewport className="toast-viewport" />
    </Toast.Provider>
  )
}

Radix Themes (Pre-Styled)

import "@radix-ui/themes/styles.css"
import { Theme, Button, Flex, Text, Card } from "@radix-ui/themes"

function App() {
  return (
    <Theme appearance="dark" accentColor="blue" radius="medium">
      <Card>
        <Flex direction="column" gap="3">
          <Text size="5" weight="bold">Welcome</Text>
          <Text color="gray">Get started with Radix Themes.</Text>
          <Button>Get Started</Button>
        </Flex>
      </Card>
    </Theme>
  )
}

Troubleshooting

IssueSolution
Component not renderingEnsure you wrap content in Portal for overlays (Dialog, Dropdown, etc.)
Styles not applying on state changeUse data-state attribute selectors, not class toggling
Focus trap not workingEnsure Dialog/AlertDialog Content is inside a Portal
Click outside not closingCheck that Overlay is rendered and covers the viewport
Keyboard navigation brokenDo not override onKeyDown unless calling the original handler
asChild not merging propsThe child must accept and forward ref and all props
Animation on close not playingUse data-state="closed" in CSS; ensure component unmounts after animation
TypeScript errorsInstall @types/react and use generic types from Radix typings
z-index conflictsAdjust z-index on Portal content; Radix portals render at document body