Agent Skill
2/7/2026

base-ui-best-practices

Base UI implementation guidelines for building accessible, unstyled React components. Use this skill when writing, reviewing, or implementing Base UI components to ensure proper patterns and accessibility. Triggers on tasks involving Base UI components like Dialog, Menu, Popover, Select, Tabs, etc.

J
jellypod
0GitHub Stars
1Views
npx skills add Jellypod-Inc/agent-plugins

SKILL.md

Namebase-ui-best-practices
DescriptionBase UI implementation guidelines for building accessible, unstyled React components. Use this skill when writing, reviewing, or implementing Base UI components to ensure proper patterns and accessibility. Triggers on tasks involving Base UI components like Dialog, Menu, Popover, Select, Tabs, etc.

name: base-ui-best-practices description: Base UI implementation guidelines for building accessible, unstyled React components. Use this skill when writing, reviewing, or implementing Base UI components to ensure proper patterns and accessibility. Triggers on tasks involving Base UI components like Dialog, Menu, Popover, Select, Tabs, etc.

Base UI Best Practices

Overview

Base UI is an unstyled UI component library for building accessible user interfaces, from the creators of Radix, Floating UI, and Material UI. It provides headless components that you can style with any CSS solution.

When to Apply

Reference these guidelines when:

  • Building UI components with Base UI
  • Implementing accessible modals, menus, popovers, or forms
  • Creating custom styled components on top of Base UI primitives
  • Reviewing code for accessibility and composition patterns
  • Migrating from Radix UI or other headless libraries

Key Principles

PrincipleDescription
Headless/UnstyledNo default styles - full control over CSS
ComposableBuild complex UIs from primitive parts
AccessibleWAI-ARIA compliant out of the box
FlexibleWorks with any styling solution

Quick Start Setup

Installation

npm install @base-ui/react

All components are in a single package. Base UI is tree-shakable.

Portal Setup (Required)

Base UI uses portals for popups (Dialog, Popover, Menu, etc.). Add this to ensure popups appear above page content:

// layout.tsx
<body>
  <div className="root">
    {children}
  </div>
</body>
/* styles.css */
.root {
  isolation: isolate;
}

iOS 26+ Safari Fix

For backdrops to cover the full viewport on iOS 26+:

body {
  position: relative;
}

Accessibility

Base UI handles complex accessibility automatically:

Built-in Features

  • ARIA attributes - Roles, labels, and relationships
  • Keyboard navigation - Arrow keys, Tab, Escape, Enter, Home, End
  • Focus management - Automatic focus trapping in modals
  • Screen reader support - Live regions for dynamic content

Developer Responsibilities

Focus indicators - Style :focus-visible for keyboard users:

.button:focus-visible {
  outline: 2px solid var(--color-blue);
  outline-offset: 2px;
}

Color contrast - Meet WCAG requirements (consider APCA)

Accessible labels - All form controls need labels:

// Option 1: Wrap in label
<label>
  Email
  <Input />
</label>

// Option 2: Use Field component (recommended)
<Field.Root>
  <Field.Label>Email</Field.Label>
  <Input />
</Field.Root>

// Option 3: aria-label for icon buttons
<Button aria-label="Close">
  <CloseIcon />
</Button>

Available Components

Overlays & Modals

  • Dialog - Modal windows for focused interactions
  • Alert Dialog - Modal requiring user acknowledgment
  • Popover - Floating content anchored to triggers
  • Tooltip - Contextual information on hover/focus
  • Toast - Temporary notifications

Navigation & Menus

  • Menu - Dropdown menus with keyboard navigation
  • Menubar - Horizontal menu bar
  • Context Menu - Right-click menus
  • Navigation Menu - Site navigation with submenus
  • Tabs - Tabbed content organization

Form Controls

  • Button - Clickable button element
  • Checkbox - Single checkbox input
  • Checkbox Group - Multiple related checkboxes
  • Radio - Radio button group
  • Switch - Toggle switch
  • Toggle - Pressable toggle button
  • Toggle Group - Group of toggle buttons
  • Input - Text input field
  • Number Field - Numeric input with increment/decrement
  • Select - Dropdown selection
  • Combobox - Searchable dropdown
  • Autocomplete - Input with suggestions
  • Slider - Range slider input
  • Field - Form field wrapper with label/error
  • Fieldset - Group of form fields
  • Form - Form container with validation

Layout & Display

  • Accordion - Expandable/collapsible sections
  • Collapsible - Single collapsible section
  • Scroll Area - Custom scrollable container
  • Separator - Visual divider
  • Toolbar - Grouped action buttons
  • Avatar - User avatar display
  • Meter - Visual meter/gauge
  • Progress - Progress indicator
  • Preview Card - Hoverable preview content

Utilities

  • CSP Provider - Content Security Policy support
  • Direction Provider - RTL/LTR support
  • mergeProps - Utility for merging props
  • useRender - Custom render hook

Quick Reference

Component Composition Pattern

Base UI components use a parts-based composition pattern:

import { Dialog } from '@base-ui/react/dialog';

function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Open</Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Backdrop className="backdrop" />
        <Dialog.Popup className="popup">
          <Dialog.Title>Title</Dialog.Title>
          <Dialog.Description>Description</Dialog.Description>
          <Dialog.Close>Close</Dialog.Close>
        </Dialog.Popup>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

Controlled vs Uncontrolled

// Uncontrolled (internal state)
<Dialog.Root defaultOpen={false}>

// Controlled (external state)
const [open, setOpen] = useState(false);
<Dialog.Root open={open} onOpenChange={setOpen}>

Styling Approaches

// CSS classes
<Dialog.Popup className="my-dialog">

// CSS-in-JS / Tailwind
<Dialog.Popup className="bg-white rounded-lg shadow-xl p-6">

// Render prop for state-based styling
<Dialog.Popup className={(state) => state.open ? 'visible' : 'hidden'}>

The render Prop (Important)

Base UI uses a render prop instead of Radix's asChild. This is the primary way to change the rendered element or access component state.

Changing the Rendered Element

// Default: renders as <button>
<Dialog.Trigger>Open</Dialog.Trigger>

// Override: render as <a> instead
<Dialog.Trigger render={<a href="#dialog" />}>Open</Dialog.Trigger>

// Override: render as custom component
<Dialog.Trigger render={<MyButton />}>Open</Dialog.Trigger>

Accessing State with Render Callback

The callback form gives you access to props and internal state:

<Dialog.Popup
  render={(props, state) => (
    <div {...props} data-nested={state.nested}>
      {props.children}
    </div>
  )}
/>

<Menu.Item
  render={(props, state) => (
    <a {...props} className={state.highlighted ? 'highlighted' : ''}>
      {props.children}
    </a>
  )}
/>

Building Custom Components with useRender

Use the useRender hook to add render prop support to your own components:

import { useRender } from '@base-ui/react/use-render';
import { mergeProps } from '@base-ui/react/merge-props';

interface ButtonProps extends useRender.ComponentProps<'button'> {}

function Button({ render, ...props }: ButtonProps) {
  return useRender({
    defaultTagName: 'button',
    render,
    props: mergeProps<'button'>({ className: 'my-button' }, props),
  });
}

// Usage
<Button>Click me</Button>                        // renders <button>
<Button render={<a href="/home" />}>Home</Button> // renders <a>

Migrating from Radix asChild

// Radix UI (asChild)
<Button asChild>
  <a href="/contact">Contact</a>
</Button>

// Base UI (render prop)
<Button render={<a href="/contact" />}>Contact</Button>

Full documentation: references/utils/use-render.md

Handbook Guides

IMPORTANT: These in-depth guides cover essential patterns. Reference them for specific implementation needs.

GuideWhen to UseFile
StylingApplying styles with Tailwind, CSS Modules, CSS-in-JS. Using className functions, data attributes, CSS variables for state-based styling.references/handbook/styling.md
AnimationAdding enter/exit animations, CSS transitions, using Motion library, animating popups and dialogs on mount/unmount.references/handbook/animation.md
CompositionWrapping Base UI parts in custom components, using render prop to change rendered elements, building reusable component abstractions.references/handbook/composition.md
CustomizationHandling events, preventing default behaviors, controlling internal state, intercepting Base UI event handlers.references/handbook/customization.md
FormsBuilding forms with Field/Fieldset/Form, validation (constraint & custom), displaying errors, integrating React Hook Form or TanStack Form.references/handbook/forms.md
TypeScriptAccessing component types via namespaces (Dialog.Props, Dialog.State), typing custom wrappers, event types.references/handbook/typescript.md

References

Full documentation with code examples:

  • references/base-ui-guidelines.md - Complete implementation guide
  • references/components/ - Individual component documentation (35 components)
  • references/handbook/ - In-depth guides (see table above)
  • references/utils/ - Utility functions:
    • use-render.md - Hook for enabling render prop in custom components
    • merge-props.md - Safely merge event handlers, classNames, and styles

To look up a specific component:

grep -l "Dialog" references/components/
grep -l "Menu" references/components/
grep -l "Popover" references/components/
Skills Info
Original Name:base-ui-best-practicesAuthor:jellypod