W↓
All docs
🔑
Sign Up/Sign In
base-ui.com/react/overview/ (+2)
Public Link
May 23, 2025, 4:06:30 PM - complete - 43.9 kB
Created by:
****ad@vlad.studio
Starting URLs:
https://base-ui.com/react/overview/quick-start
Crawl Prefixes:
https://base-ui.com/react/overview/
https://base-ui.com/react/handbook/
https://base-ui.com/react/utils/
## Page: https://base-ui.com/react/overview/quick-start ## Quick start A quick guide to getting started with Base UI. ## Install the library Install Base UI using a package manager. Terminal npm i @base-ui-components/react All components are included in a single package. Base UI is tree-shakeable, so your app bundle will contain only the components that you actually use. ## Set up portals Base UI uses portals for components that render popups, such as Dialog and Popover. To make portalled components always appear on top of the entire page, add the following style to your application layout root: layout.tsx <body> <div className="root"> {children} </div> </body> styles.css .root { isolation: isolate; } This style creates a separate stacking context for your application’s `.root` element. This way, popups will always appear above the page contents, and any `z-index` property in your styles won’t interfere with them. ## Assemble a component This demo shows you how to import a Popover component, assemble its parts, and apply styles. There are examples for both Tailwind and CSS Modules below, but since Base UI is unstyled, you can use CSS-in-JS, plain CSS, or any other styling solution you prefer. CSS Modules ## Next steps This walkthrough outlines the basics of putting together a Base UI component. Browse the rest of the documentation to see what components are available in the library and how to use them. --- ## Page: https://base-ui.com/react/overview/accessibility ## Accessibility Learn how to make the most of Base UI’s accessibility features and guidelines. Accessibility is a top priority for Base UI. Base UI components handle many complex accessibility details including ARIA attributes, role attributes, pointer interactions, keyboard navigation, and focus management. The goal is to provide an accessible user experience out of the box, with intuitive APIs for configuration. This page highlights some of the key accessibility features of Base UI, as well as some ways you will need to augment the library, in order to ensure that your application is accessible to everyone. ## Keyboard navigation Base UI components adhere to the WAI-ARIA Authoring Practices to provide basic keyboard accessibility out of the box. This is critical for users who have difficulty using a pointer device, but it’s also important for users who prefer navigating with a keyboard or other input mode. Many components provide support for arrow keys, alphanumeric keys, Home, End, Enter, and Esc. ## Focus management Base UI components manage focus automatically following a user interaction. Additionally, some components provide props like `initialFocus` and `finalFocus`, to configure focus management. While Base UI components manage focus, it’s the developer’s responsibility to visually indicate focus. This is typically handled by styling the `:focus` or `:focus-visible` CSS pseudo-classes. WCAG provides guidelines on focus appearance. ## Color contrast When styling elements, it’s important to meet the minimum requirements for color contrast between each foreground element and its corresponding background element. Unless your application has strict requirements around compliance with current standards, consider adhering to APCA, which is slated to become the new standard in WCAG 3. ## Accessible labels Base UI provides components like Form, Input, Field, Fieldset to automatically associate form controls. Additionally, you can use the native HTML `<label>` element to provide context to corresponding inputs. Most applications will present custom controls that require accessible names provided by markup features such as `alt`, `aria-label` or `aria-labelledby`. WAI-ARIA provides guidelines on providing accessible names to custom controls. ## Testing Base UI components are tested on a broad spectrum of browsers, devices, platforms, screen readers, and environments. --- ## Page: https://base-ui.com/react/overview/releases Components * Accordion * Alert Dialog * Avatar * Checkbox * Checkbox Group * Collapsible * Dialog * Field * Fieldset * Form * Input * Menu * Meter * Number Field * Popover * Preview Card * Progress * Radio * Scroll Area * Select * Separator * Slider * Switch * Tabs * Toast * Toggle * Toggle Group * Toolbar * Tooltip Releases * (Top) * v1.0.0-alpha.8 * Accordion * AlertDialog * CheckboxGroup * Collapsible * Dialog * Field * Menu * Meter * NumberField * Popover * Progress * Radio * ScrollArea * Select * Slider * Toast * Tooltip * v1.0.0-alpha.7 * Accordion * AlertDialog * Avatar * Collapsible * Dialog * Field * Menu * NumberField * Popover * PreviewCard * RadioGroup * Select * Slider * Tabs * Toolbar * Tooltip * useRender * v1.0.0-alpha.6 * AlertDialog * Avatar * Checkbox * Dialog * Field * Form * Menu * Popover * PreviewCard * Progress * Radio * Select * Slider * Tabs * Tooltip * v1.0.0-alpha.5 * AlertDialog * Dialog * Menu * NumberField * Popover * PreviewCard * Progress * ScrollArea * Select * Separator * Slider * Tabs * Tooltip * 1.0.0-alpha.4 ## Releases Changelogs for each Base UI release. ## v1.0.0-alpha.8 **Apr 17, 2025** ### Accordion * Recalculate panel dimensions on layout resize (#1704) * Rework animations and transitions (#1601) ### AlertDialog * **Breaking change:** Rename `data-has-nested-dialogs` to `data-nested-dialog-open` (#1686) * Fix `onOpenChange` types for `event`/`reason` passing (#1721) * Use consistent `inert` attr and map `[data-popup-open]` back to `open` (#1650) * Fix text selection & right-clicks (#1702) ### CheckboxGroup * Parent checkbox/nested demos (#1610) ### Collapsible * Fix ForwardedRef type of CollapsiblePanel (#1595) * Recalculate panel dimensions on layout resize (#1704) * Rework animations and transitions (#1601) ### Dialog * **Breaking change:** Rename `data-has-nested-dialogs` to `data-nested-dialog-open` (#1686) * **Breaking change:** Add new `trap-focus` value to `modal` prop. Dialogs with `modal=false` no longer trap focus. (#1571) * Fix `onOpenChange` types for `event`/`reason` passing (#1721) * Use consistent `inert` attr and map `[data-popup-open]` back to `open` (#1650) * Fix text selection & right-clicks (#1702) * Allow document to slide input into view on iOS when keyboard opens (#1735) ### Field * Fix forwarding of `name` and `disabled` props (#1616) * Add missing item data attributes docs (#1691) * Fix `inert` prop compatibility in React <19 (#1618) * Fix stuck highlight on submenu trigger when submenu opens with keyboard (#1698) * Fix `onOpenChange` types for `event`/`reason` passing (#1721) * Use consistent `inert` attr and map `[data-popup-open]` back to `open` (#1650) * Fix text selection & right-clicks (#1702) ### Meter * New Meter component (#1435) ### NumberField * Correct percentage parse handling (#1676) * New `snapOnStep` prop (#1560) ### Popover * **Breaking change:** Add new `trap-focus` value to `modal` prop (#1571) * Fix `inert` prop compatibility in React <19 (#1618) * Fix `onOpenChange` types for `event`/`reason` passing (#1721) * Use consistent `inert` attr and map `[data-popup-open]` back to `open` (#1650) * Fix text selection & right-clicks (#1702) ### Progress * **Breaking change:** Add `Progress.Label` and `locale` prop. The `getAriaLabel` prop was removed as `Progress.Label` should be used to provide an accessible name. (#1666) ### Radio * Fix value forwarding and null handling (#1697) ### ScrollArea * **Breaking change:** Add `Content` part. It is now required to include the `ScrollArea.Content` within `ScrollArea.Viewport` part when the content is horizontally scrollable. (#1607) * Handle visibility change and nesting (#1598) * Correct thumb sizing with scrollbar margins (#1606) ### Select * **Breaking change:** Improve item highlight performance. The highlighted state is now removed. It’s not possible to customize the `data-highlighted` attribute anymore. (#1570) * Avoid double commit on value change (#1597) * Reset `selectedIndex` when set to `null` (#1596) * Add missing item data attributes docs (#1691) * Fix `onOpenChange` types for `event`/`reason` passing (#1721) * Use consistent `inert` attr and map `[data-popup-open]` back to `open` (#1650) * Fix text selection & right-clicks (#1702) ### Slider * Correct thumb positioning when control has padding (#1661) * Prevent range slider thumbs from being dragged past each other (#1612) * Fix incorrect CSS position on vertical slider indicator (#1599) * Fix overlapping slider thumbs stuck at min or max (#1732) ### Toast * New Toast component (#1467) ### Tooltip * Avoid re-rendering unrelated consumers (#1677) * Add `disabled` prop (#1682) * Fix `onOpenChange` types for `event`/`reason` passing (#1721) * Use consistent `inert` attr and map `[data-popup-open]` back to `open` (#1650) * Fix text selection & right-clicks (#1702) ## v1.0.0-alpha.7 **Mar 20, 2025** ### Accordion * Fix `aria-labelledby` on accordion panel (#1544) ### AlertDialog * Fix selection on outside press on Firefox with modal prop (#1573) * Fix non-interactive button disabled state (#1473) * `actionsRef` prop (#1236) ### Avatar * Support cross origin in useImageLoadingStatus (#1433) * Add missing Avatar export (#1428) ### Collapsible * Update props destructuring to fix Trigger disabled state (#1469) ### Dialog * Fix selection on outside press on Firefox with modal prop (#1573) * Fix non-interactive button disabled state (#1473) * `actionsRef` prop (#1236) ### Field * Fix `FieldControl` \[data-filled\] not reacting to external value changes (#1565) * Ensure submenu triggers respond to clicks when `openOnHover=false` (#1583) * Ensure `stickIfOpen` is reset to `true` correctly (#1548) * Fix selection on outside press on Firefox with modal prop (#1573) * Reset `hoverEnabled` state on close (#1461) * Fix prop merging issues (#1445) * Set `pointer-events: none` style on backdrops when hoverable (#1351) * `actionsRef` prop (#1236) ### NumberField * Fix ScrubArea on Safari (\[#1584\])(https://github.com/mui/base-ui/pull/1584/)) * Fix `large/smallStep` getting stuck (#1578) * Fix parse of numbers with spaces as thousands separators (#1577) * Prevent virtual cursor overlapping native one (#1491) * Fix disabled state on increment/decrement buttons (#1462) * Correct virtual cursor rendering (#1484) * Add `locale` prop (#1488) * Improve virtual cursor perf (#1485) ### Popover * Ensure `stickIfOpen` is reset to `true` correctly (#1548) * Fix selection on outside press on Firefox with modal prop (#1573) * Set `pointer-events: none` style on backdrops when hoverable (#1351) * Fix non-interactive button disabled state (#1473) * `modal` prop (#1459) * `actionsRef` prop (#1236) ### PreviewCard * Set `pointer-events: none` style on backdrops when hoverable (#1351) * `actionsRef` prop (#1236) ### RadioGroup * Fix `Form`/`Field` validation integration (#1448) * Handle modifier keys (#1529) ### Select * Fix selection on outside press on Firefox with modal prop (#1573) * `actionsRef` prop (#1236) * Improve `ScrollArrow` behavior (#1564) * Ensure switching controlled value to `null` updates `Select.Value` label (#1561) * Pass `value` as second argument to function children `Select.Value` (#1562) * Fix focus jump while hovering while navigating with keyboard (#1563) * Fix disabled state changing (#1526) ### Slider * Fix thumb positioning when controlled value violates min/max/step (#1541) * Warn when `min` is not less than `max` (#1475) * Narrow the type of `value` in callbacks (#1241) ### Tabs * Fix keyboard navigation involving disabled Tabs (#1449) * Handle modifier keys (#1529) ### Toolbar * Add Toolbar components (#1349) ### Tooltip * `actionsRef` prop (#1236) * Fix `Provider` `delay=0` not being respected (#1416) ### useRender * Add public hook (#1418) * Refine docs and APIs (#1551) ## v1.0.0-alpha.6 **Feb 6, 2025** ### AlertDialog * `onOpenChangeComplete` prop (#1305) * Fix jump with `scroll-behavior` style (#1343) ### Avatar * Add Avatar component (#1210) ### Checkbox * Avoid applying `hidden` attr when `keepMounted=true` for indicators (#1329) ### Dialog * Remove `modal={open}` state (#1352) * Support multiple non-nested modal backdrops (#1327) * Fix missing `id`s on Title and Description (#1326) * `onOpenChangeComplete` prop (#1305) * Fix jump with `scroll-behavior` style (#1343) ### Field * Respect `validationMode` (#1053) * Add `filled` and `focused` style hooks (#1341) ### Form * Fix focusing of invalid field controls on errors prop change (#1364) * Avoid applying `hidden` attr when `keepMounted=true` for indicators (#1329) * Support submenus with `openOnHover` prop (#1338) * Fix iPad detection when applying scroll lock (#1342) * `onOpenChangeComplete` prop (#1305) * Fix jump with `scroll-behavior` style (#1343) * Add `OffsetFunction` for `sideOffset` and `alignOffset` (#1223) * Ensure `keepMounted` is a private param on `Positioner` (#1410) ### Popover * `onOpenChangeComplete` prop (#1305) * Add `OffsetFunction` for `sideOffset` and `alignOffset` (#1223) * Ensure `keepMounted` is a private param on `Positioner` (#1410) ### PreviewCard * `onOpenChangeComplete` prop (#1305) * Add `OffsetFunction` for `sideOffset` and `alignOffset` (#1223) * Ensure `keepMounted` is a private param on `Positioner` (#1410) ### Progress * Add `format` prop and `Value` component (#1355) ### Radio * Avoid applying `hidden` attr when `keepMounted=true` for indicators (#1329) ### Select * `onOpenChangeComplete` prop (#1305) * Fix jump with `scroll-behavior` style (#1343) * Add `OffsetFunction` for `sideOffset` and `alignOffset` (#1223) * Ensure `keepMounted` is a private param on `Positioner` (#1410) ### Slider * Fix thumb positioning (#1411) ### Tabs * Fix being able to activate a disabled tab (#1359) * Fix tabs activating incorrectly on non-primary button clicks (#1318) ### Tooltip * `onOpenChangeComplete` prop (#1305) * Add `OffsetFunction` for `sideOffset` and `alignOffset` (#1223) * Ensure `keepMounted` is a private param on `Positioner` (#1410) ## v1.0.0-alpha.5 **Jan 10, 2025** ### AlertDialog * **Breaking change:** Require `Portal` part. The AlertDialog must explicitly include the Portal part wrapping the Popup. The `keepMounted` prop was removed from the Popup. It’s only present on the Portal part. #1222 * Don’t call `onNestedDialogOpen` when unmounting a closed nested dialog #1280 * Fix the nesting of different dialogs #1167 * Remove `useFloating` call from the Popup #1300 * Set `pointer-events` on `InternalBackdrop` based on `open` state #1221 * Use internal backdrop for pointer modality #1161 ### Dialog * **Breaking change:** Require `Portal` part. The Dialog must explicitly include the Portal part wrapping the Popup. The `keepMounted` prop was removed from the Popup. It’s only present on the Portal part. #1222 * Don’t call `onNestedDialogOpen` when unmounting a closed nested dialog #1280 * Fix the nesting of different dialogs #1167 * Remove `useFloating` call from the Popup #1300 * Set `pointer-events` on `InternalBackdrop` based on `open` state #1221 * Use internal backdrop for pointer modality #1161 * **Breaking change:** Require `Portal` part. The Menu must explicitly include the Portal part wrapping the Positioner. The `keepMounted` prop was removed from the Positioner. It’s only present on the Portal part. #1222 * Apply `aria-hidden` to `Arrow` parts #1196 * Fix `focusableWhenDisabled` components #1313 * Fix `openOnHover` issues #1191 * Fix closing the menu when clicking on checkboxitem/radioitem #1301 * Fix Enter key preventDefault when rendering links #1251 * Handle pseudo-element bounds in mouseup detection #1250 * Set `pointer-events` on `InternalBackdrop` based on `open` state #1221 * Use internal backdrop for pointer modality #1161 ### NumberField * Correctly handle quick touches #1294 ### Popover * **Breaking change:** Require `Portal` part. The Popover must explicitly include the Portal part wrapping the Positioner. The `keepMounted` prop was removed from the Positioner. It’s only present on the Portal part. #1222 * Apply `aria-hidden` to `Arrow` parts #1196 * Fix PopoverTrigger and TooltipTrigger prop types #1209 ### PreviewCard * **Breaking change:** Require `Portal` part. The PreviewCard must explicitly include the Portal part wrapping the Positioner. The `keepMounted` prop was removed from the Positioner. It’s only present on the Portal part. #1222 * Apply `aria-hidden` to `Arrow` parts #1196 * Use `FloatingPortalLite` #1278 ### Progress * Set zero width when value is zero #1204 ### ScrollArea * Differentiate `x`/`y` orientation `data-scrolling` #1188 * Read `DirectionProvider` and use logical positioning CSS props #1194 ### Select * **Breaking change:** Require `Portal` part. The Select must explicitly include the Portal part wrapping the Positioner. The `keepMounted` prop was removed from the Positioner. It’s only present on the Portal part. #1222 * Allow `id` to be passed to trigger #1174 * Fallback to standard positioning when pinch-zoomed in Safari #1139 * Fix `focusableWhenDisabled` components #1313 * Fix highlight flash on Safari #1233 * Handle pseudo-element bounds in mouseup detection #1250 * Use internal backdrop for pointer modality #1161 ### Separator * Support vertical orientation #1304 ### Slider * Ensure `onValueCommitted` is called with the same value as latest `onValueChange` #1296 * Replace internal map with `Composite` metadata #1082 * Set `position: relative` on range slider indicator #1175 * Use un-rounded values to position thumbs #1219 ### Tabs * Expose width/height state in tabs indicator #1288 ### Tooltip * **Breaking change:** Require `Portal` part. The Tooltip must explicitly include the Portal part wrapping the Positioner. The `keepMounted` prop was removed from the Positioner. It’s only present on the Portal part. #1222 * Apply `aria-hidden` to `Arrow` parts #1196 * Fix PopoverTrigger and TooltipTrigger prop types #1209 * Use `FloatingPortalLite` #1278 ## 1.0.0-alpha.4 **December 17, 2024** Public alpha launch 🐣 Merry Xmas! 🎁 --- ## Page: https://base-ui.com/react/overview/about About Base UI * (Top) * Features * Headless * Accessible * Composable * Team * Community * GitHub * Discord * X * Bluesky ## About Base UI An open-source React component library for building accessible user interfaces. From the creators of Radix, Material UI, and Floating UI, Base UI is an unstyled React component library for building accessible user interfaces. Our focus is on accessibility, performance, and developer experience. Our goal is to provide a complete set of open-source UI components, with a delightful developer experience, in a sustainable way. ## Features ### Headless Base UI components are unstyled, don’t bundle CSS, and don’t prescribe a styling solution. You retain complete control over your application’s CSS layer. Base UI is compatible with Tailwind, CSS Modules, plain CSS, CSS-in-JS, or any other styling engine you prefer. ### Accessible Poor accessibility can make your application difficult to navigate for all users, not just for users with disabilities. Accessibility is our primary focus. Base UI components adhere to WAI-ARIA design patterns and are tested on a wide range of platforms, devices, browsers, screen readers, and other environments. ### Composable Component APIs are fully open, so you have direct access to each node, you can easily add or remove parts, and you can wrap them however you prefer. ## Team * **Colm Tuite** (Radix) @colmtuite * **Vlad Moroz** (Radix) @vladyslavmoroz * **James Nelson** (Floating UI) @atomiksdev * **Michał Dudak** (Material UI) @michaldudak * **Marija Najdova** (Material UI + Fluent UI) @marijanajdova * **Albert Yu** (Material UI) @mj12albert ### GitHub Base UI is an open-source project. If you want to file a bug report or contribute, visit our GitHub. ### Discord For community support, questions, and tips, join our Discord. ### X The best way to stay up-to-date on new releases and announcements is by following Base UI on X. ### Bluesky We’re also on Bluesky. --- ## Page: https://base-ui.com/react/handbook/styling Styling * (Top) * Style hooks * CSS classes * Data attributes * CSS variables * Tailwind CSS * CSS Modules * CSS-in-JS ## Styling A guide to styling Base UI components with your preferred styling engine. Base UI components are unstyled, don’t bundle CSS, and are compatible with Tailwind, CSS Modules, CSS-in-JS, or any other styling solution you prefer. You retain total control of your styling layer. ## Style hooks ### CSS classes Components that render an HTML element accept a `className` prop to style the element with CSS classes. switch.tsx <Switch.Thumb className="SwitchThumb" /> The prop can also be passed a function that takes the component’s state as an argument. switch.tsx <Switch.Thumb className={(state) => (state.checked ? 'checked' : 'unchecked')} /> ### Data attributes Components provide data attributes designed for styling their states. For example, Switch can be styled using its `[data-checked]` and `[data-unchecked]` attributes, among others. switch.css .SwitchThumb[data-checked] { background-color: green; } ### CSS variables Components expose CSS variables to aid in styling, often containing dynamic numeric values to be used in sizing or transform calculations. For example, Popover exposes CSS variables on its `Popup` component like `--available-height` and `--anchor-width`. popover.css .Popup { max-height: var(--available-height); } Check out each component’s API reference for a complete list of available data attributes and CSS variables. ## Tailwind CSS Apply Tailwind classes to each part via the `className` prop. menu.tsx import * as React from 'react'; import { Menu } from '@base-ui-components/react/menu'; export default function ExampleMenu() { return ( <Menu.Root> <Menu.Trigger className="flex h-10 items-center justify-center gap-1.5 rounded-md border border-gray-200 bg-gray-50 px-3.5 text-base font-medium text-gray-900 select-none hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-blue-800 active:bg-gray-100 data-[popup-open]:bg-gray-100"> Song </Menu.Trigger> <Menu.Portal> <Menu.Positioner className="outline-none" sideOffset={8}> <Menu.Popup className="origin-[var(--transform-origin)] rounded-md bg-[canvas] py-1 text-gray-900 shadow-lg shadow-gray-200 outline outline-1 outline-gray-200 transition-[transform,scale,opacity] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[starting-style]:scale-90 data-[starting-style]:opacity-0 dark:shadow-none dark:-outline-offset-1 dark:outline-gray-300"> <Menu.Item className="flex cursor-default py-2 pr-8 pl-4 text-sm leading-4 outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-gray-50 data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:rounded-sm data-[highlighted]:before:bg-gray-900"> Add to Library </Menu.Item> <Menu.Item className="flex cursor-default py-2 pr-8 pl-4 text-sm leading-4 outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-gray-50 data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:rounded-sm data-[highlighted]:before:bg-gray-900"> Add to Playlist </Menu.Item> </Menu.Popup> </Menu.Positioner> </Menu.Portal> </Menu.Root> ); } ## CSS Modules Apply custom CSS classes to each part via the `className` prop. Then style those classes in a CSS Modules file. menu.tsx import * as React from 'react'; import { Menu } from '@base-ui-components/react/menu'; import styles from './menu.module.css'; export default function ExampleMenu() { return ( <Menu.Root> <Menu.Trigger className={styles.Button}>Song</Menu.Trigger> <Menu.Portal> <Menu.Positioner className={styles.Positioner} sideOffset={8}> <Menu.Popup className={styles.Popup}> <Menu.Item className={styles.Item}>Add to Library</Menu.Item> <Menu.Item className={styles.Item}>Add to Playlist</Menu.Item> </Menu.Popup> </Menu.Positioner> </Menu.Portal> </Menu.Root> ); } ## CSS-in-JS Wrap each component part and apply styles, then assemble your styled components. menu.tsx import * as React from 'react'; import { Menu } from '@base-ui-components/react/menu'; import styled from '@emotion/styled'; const StyledMenuTrigger = styled(Menu.Trigger)` // Button styles `; const StyledMenuPositioner = styled(Menu.Positioner)` // Positioner styles `; const StyledMenuPopup = styled(Menu.Popup)` // Popup styles `; const StyledMenuItem = styled(Menu.Item)` // Menu item styles `; const MenuExample = () => ( <Menu.Root> <StyledMenuTrigger>Song</StyledMenuTrigger> <Menu.Portal> <StyledMenuPositioner> <StyledMenuPopup> <StyledMenuItem>Add to Library</StyledMenuItem> <StyledMenuItem>Add to Playlist</StyledMenuItem> </Menu.Portal> </StyledMenuPositioner> </StyledMenuPopup> </Menu.Root> ); export default MenuExample; --- ## Page: https://base-ui.com/react/handbook/animation Animation * (Top) * CSS transitions * CSS animations * JavaScript animations * Elements removed from the DOM when closed * Elements kept in the DOM when closed * Manual unmounting ## Animation A guide to animating Base UI components. Base UI components can be animated using CSS transitions, CSS animations, or JavaScript animation libraries. Each component provides a number of data attributes to target its states, as well as a few attributes specifically for animation. ## CSS transitions Use the following Base UI attributes for creating transitions when a component becomes visible or hidden: * `[data-starting-style]` corresponds to the initial style to transition from. * `[data-ending-style]` corresponds to the final style to transition to. Transitions are recommended over CSS animations, because a transition can be smoothly cancelled midway. For example, if the user closes a popup before it finishes opening, with CSS transitions it will smoothly animate to its closed state without any abrupt changes. popover.css .Popup { box-sizing: border-box; padding: 1rem 1.5rem; background-color: canvas; transform-origin: var(--transform-origin); transition: transform 150ms, opacity 150ms; &[data-starting-style], &[data-ending-style] { opacity: 0; transform: scale(0.9); } } ## CSS animations Use the following Base UI attributes for creating CSS animations when a component becomes visible or hidden: * `[data-open]` corresponds to the style applied when a component becomes visible. * `[data-closed]` corresponds to the style applied before a component becomes hidden. popover.css @keyframes scaleIn { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } } @keyframes scaleOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.9); } } .Popup[data-open] { animation: scaleIn 250ms ease-out; } .Popup[data-closed] { animation: scaleOut 250ms ease-in; } ## JavaScript animations JavaScript animation libraries such as Motion require control of the mounting and unmounting lifecycle of components in order for exit animations to play. Base UI relies on `element.getAnimations()` to detect if animations have finished on an element. When using Motion, the `opacity` property lets this detection work easily, so always animating `opacity` to a new value for exit animations will work. If it shouldn’t be animated, you can use a value close to `1`, such as `opacity: 0.9999`. ### Elements removed from the DOM when closed Most components like Popover are unmounted from the DOM when they are closed. To animate them: * Make the component controlled with the `open` prop so `AnimatePresence` can see the state as a child * Specify `keepMounted` on the `Portal` part * Use the `render` prop to compose the `Popup` with `motion.div` animated-popover.tsx function App() { const [open, setOpen] = React.useState(false); return ( <Popover.Root open={open} onOpenChange={setOpen}> <Popover.Trigger>Trigger</Popover.Trigger> <AnimatePresence> {open && ( <Popover.Portal keepMounted> <Popover.Positioner> <Popover.Popup render={ <motion.div initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.8 }} /> } > Popup </Popover.Popup> </Popover.Positioner> </Popover.Portal> )} </AnimatePresence> </Popover.Root> ); } ### Elements kept in the DOM when closed The `Select` component must be kept mounted in the DOM even when closed. In this case, a different approach is needed to animate it with Motion. * Make the component controlled with the `open` prop * Use the `render` prop to compose the `Popup` with `motion.div` * Animate the properties based on the `open` state, avoiding `AnimatePresence` animated-select.tsx function App() { const [open, setOpen] = React.useState(false); return ( <Select.Root open={open} onOpenChange={setOpen}> <Select.Trigger> <Select.Value /> </Select.Trigger> <Select.Portal> <Select.Positioner> <Select.Popup render={ <motion.div initial={false} animate={{ opacity: open ? 1 : 0, scale: open ? 1 : 0.8, }} /> } > Popup </Select.Popup> </Select.Positioner> </Select.Portal> </Select.Root> ); } ### Manual unmounting For full control, you can manually unmount the component when it’s closed once animations have finished using an `actionsRef` passed to the `Root`: manual-unmount.tsx function App() { const [open, setOpen] = React.useState(false); const actionsRef = React.useRef({ unmount: () => {} }); return ( <Popover.Root open={open} onOpenChange={setOpen} actionsRef={actionsRef}> <Popover.Trigger>Trigger</Popover.Trigger> <AnimatePresence> {open && ( <Popover.Portal keepMounted> <Popover.Positioner> <Popover.Popup render={ <motion.div initial={{ scale: 0 }} animate={{ scale: 1 }} exit={{ scale: 0 }} onAnimationComplete={() => { if (!open) { actionsRef.current.unmount(); } }} /> } > Popup </Popover.Popup> </Popover.Positioner> </Popover.Portal> )} </AnimatePresence> </Popover.Root> ); } --- ## Page: https://base-ui.com/react/handbook/composition Composition * (Top) * Composing custom React components * Composing multiple components * Changing the default rendered element * Render function ## Composition A guide to composing Base UI components with your own React components. ## Composing custom React components Use the `render` prop to compose a Base UI part with your own React components. For example, most triggers render a `<button>` by default. The code snippet below shows how to use a custom button instead. index.tsx <Menu.Trigger render={<MyButton size="md" />}> Open menu </Menu.Trigger> The custom component must forward the `ref`, and spread all the received props on its underlying DOM node. ## Composing multiple components In situations where you need to compose multiple Base UI components with custom React components, `render` props can be nested as deeply as necessary. Working with Tooltip is a common example. index.tsx <Dialog.Root> <Tooltip.Root> <Tooltip.Trigger render={ <Dialog.Trigger render={ <Menu.Trigger render={<MyButton size="md" />}> Open menu </Menu.Trigger> } /> } /> <Tooltip.Portal>...</Tooltip.Portal> </Tooltip.Root> <Dialog.Portal>...</Dialog.Portal> </Dialog.Root> ## Changing the default rendered element You can also use the `render` prop to override the rendered element of the component. For example, `Menu.Item` renders a `<div>` by default. The code snippet below shows how to render it as an `<a>` element so that it works like a link. index.tsx import * as React from 'react'; import { Menu } from '@base-ui-components/react/menu'; export default () => ( <Menu.Root> <Menu.Trigger>Song</Menu.Trigger> <Menu.Portal> <Menu.Positioner> <Menu.Popup> <Menu.Item render={<a href="base-ui.com" />}> Add to Library </Menu.Item> </Menu.Popup> </Menu.Positioner> </Menu.Portal> </Menu.Root> ); Each Base UI component renders the most appropriate element by default, and in most cases, rendering a different element is recommended only on a case-by-case basis. ## Render function If you are working in an extremely performance-sensitive application, you might want to pass a function to the `render` prop instead of a React element. switch.tsx <Switch.Thumb render={(props, state) => <span {...props}> {state.checked ? <CheckedIcon /> : <UncheckedIcon />} </span> } /> Using a function gives you complete control over spreading props and also allows you to render different content based on the component’s state. --- ## Page: https://base-ui.com/react/utils/direction-provider Direction Provider * (Top) * API reference ## Direction Provider Enables RTL behavior for Base UI components. CSS Modules ## API reference Import the component and wrap it around your app: Anatomy import { DirectionProvider } from '@base-ui-components/react/direction-provider'; <DirectionProvider> {/* Your app or a group of components */} </DirectionProvider> | Prop | Type | Default | | | --- | --- | --- | --- | | `direction` | `TextDirection` | `'ltr'` | | | `children` | `ReactNode` | `undefined` | | --- ## Page: https://base-ui.com/react/utils/use-render useRender * (Top) * API reference * Input parameters * Return value * Examples * Merging props * Merging refs * TypeScript * Migrating from Radix UI ## useRender Hook for enabling a render prop in custom components. The `useRender` hook lets you build custom components that provide a `render` prop to override the default rendered element. ## API reference ### Input parameters | Prop | Type | Default | | | --- | --- | --- | --- | | `render` | `RenderProp<State>` | `undefined` | | | `props` | `Record<string, unknown>` | `undefined` | | | `refs` | `React.Ref<RenderedElementType>[]` | `undefined` | | | `state` | `State` | `undefined` | | ### Return value | Prop | Type | | | --- | --- | --- | | `renderElement` | `() => React.ReactElement` | | renderElement function const { renderElement } = useRender({ // Input parameters }); const element = renderElement(); ## Examples A `render` prop for a custom Text component lets consumers use it to replace the default rendered `p` element with a different tag or component. Text component rendered as a paragraph tag **Text component rendered as a strong tag** The callback version of the `render` prop enables more control of how props are spread, and also passes the internal `state` of a component. ## Merging props The `mergeProps` function merges two or more sets of React props together. It safely merges three types of props: 1. Event handlers, so that all are invoked 2. `className` strings 3. `style` properties `mergeProps` merges objects from left to right, so that subsequent objects’ properties in the arguments overwrite previous ones. Merging props is useful when creating custom components, as well as inside the callback version of the `render` prop for any Base UI component. Using mergeProps in the render callback import { mergeProps } from '@base-ui-components/react/merge-props'; import styles from './index.module.css'; function Button() { return ( <Component render={(props, state) => ( <button {...mergeProps<'button'>(props, { className: styles.Button, })} /> )} /> ); } ## Merging refs When building custom components, you often need to control a ref internally while still letting external consumers pass their own—merging refs lets both parties have access to the underlying DOM element. The `refs` option in `useRender` enables this, which holds an array of refs to be merged together. In React 19, `React.forwardRef()` is not needed when building primitive components, as the external ref prop is already contained inside `props`. Your internal ref can be passed to `refs` to be merged with `props.ref`: React 19 function Text({ render = <p />, ...props }: TextProps) { const internalRef = React.useRef<HTMLElement | null>(null); const { renderElement } = useRender({ render, props, refs: [internalRef], }); return renderElement(); } In older versions of React, you need to use `React.forwardRef()` and add the forwarded ref to the `refs` array along with your own internal ref. The examples above assume React 19, and should be modified to use `React.forwardRef()` to support React 18 and 17. React 18 and 17 const Text = React.forwardRef(function Text( { render = <p />, ...props }: TextProps, forwardedRef: React.ForwardedRef<HTMLElement>, ) { const internalRef = React.useRef<HTMLElement | null>(null); const { renderElement } = useRender({ render, props, refs: [forwardedRef, internalRef], }); return renderElement(); }); ## TypeScript To type props, there are two interfaces: * `useRender.ComponentProps` for a component’s external (public) props. It types the `render` prop and HTML attributes. * `useRender.ElementProps` for the element’s internal (private) props. It types HTML attributes alone. Typing props interface ButtonProps extends useRender.ComponentProps<'button'> {} function Button({ render = <button />, ...props }: ButtonProps) { const defaultProps: useRender.ElementProps<'button'> = { className: styles.Button, type: 'button', children: 'Click me', }; const { renderElement } = useRender({ render, props: mergeProps<'button'>(defaultProps, props), }); return renderElement(); } ## Migrating from Radix UI Radix UI uses an `asChild` prop, while Base UI uses a `render` prop. Learn more about how composition works in Base UI in the composition guide. In Radix UI, the `Slot` component lets you implement an `asChild` prop. Radix UI Slot component import { Slot } from 'radix-ui'; function Button({ asChild, ...props }) { const Comp = asChild ? Slot.Root : 'button'; return <Comp {...props} />; } // Usage <Button asChild> <a href="/contact">Contact</a> </Button> In Base UI, `useRender` lets you implement a `render` prop. The example below is the equivalent implementation to the Radix example above. Base UI render prop import { useRender } from '@base-ui-components/react/use-render'; function Button({ render = <button />, ...props }) { const { renderElement } = useRender({ render, props }); return renderElement(); } // Usage <Button render={<a href="/contact">Contact</a>} />