Radix Primitives API provides flexibility for abstraction
The component APIs in Radix Primitives provide a lot of flexibility, allowing developers to create their own abstractions on top of the low-level primitives. For example, CodeSandbox abstracts away some of the options of the Context Menu to create a standardised Context Menu built on top of Radix.
Radix developer experience: animations with data attributes
Radix Primitives allow developers to add, enter, and exit animations relying purely on data attributes, making animation implementation straightforward.
Radix API consistency and flexibility
The Radix Primitives API is consistent and extremely flexible, allowing developers to create their own abstractions on top of Radix Primitives.
Radix focuses on core primitives that can be extended and combined
Radix implements only a key-set of core UI primitives, which allows users to extend and combine them as needed. This approach prioritizes simplicity and flexibility.
Radix design follows Single Responsibility Pattern with individual packages
Radix is designed so that each Primitive is its own JavaScript package, following the Single Responsibility Pattern. This reduces wastage of resources and reduces the need to rely on bundlers to compute which pieces of the library are used.
Radix Primitives structured like DOM tree with wrappers and root elements
Radix Primitives are structured similarly to a DOM tree, including wrappers, root elements, triggers, and other shared React components. This structure allows developers to structure, decorate, and easily design UI components while maintaining a high degree of control over individual pieces.
Radix API is consistent and opinionated across components
Radix Primitives contain a consistent and highly opinionated API shared across their components, which facilitates standardizing UI components and makes Radix predictable and reliable.
Radix headless model enables custom styling
The headless UI model of Radix Primitives allows developers to apply custom styles without fighting the library. Radix is compatible with any CSS layer, enabling integration with zero runtime CSS libraries like Vanilla Extract.
Radix API design is consistent and learnable
The API of Radix components is well-designed and consistent across components, while also being easy to learn and maintain.
Consistent API patterns across Radix components aid learning
Radix components share consistent API patterns and conventions such as forceMount, as, and data-state attributes. This consistency means learning one component's API helps developers understand and use other components intuitively, which is key to maintaining a solid user base.
Radix primitives support easy animation implementation
Radix Primitives allow developers to easily add entry and exit CSS animations on top of components with minimal domain knowledge or external library dependencies.
asChild can be nested for composing multiple primitives
asChild can be used as deeply as needed to compose multiple primitive behaviors together. For example, you can nest Tooltip.Trigger and Dialog.Trigger both with asChild on the same custom button component to combine both primitives' functionality.
asChild prop enables composition onto alternative elements
All Radix primitive parts that render a DOM element accept an asChild prop. When asChild is set to true, Radix will not render a default DOM element, instead cloning the part's child and passing it the props and behavior required to make it functional.
Changing element type with asChild on Tooltip.Trigger
Tooltip.Trigger is rendered as a button by default, but you can use asChild to compose it onto a link (a tag) or other element. When you do change the underlying element type, you are responsible for ensuring it remains accessible and functional. For Tooltip.Trigger specifically, the element must be focusable and can respond to pointer and keyboard events.
Custom components with asChild must spread props
When using asChild with custom React components, your component must spread all props onto the underlying DOM node using the spread operator. This is necessary because Radix will pass its own props and event handlers to make the component functional and accessible. Spreading props ensures you don't break the functionality by missing specific props or events.
Custom components with asChild must forward ref
When using asChild with custom React components, your component must forward the ref using React.forwardRef. This is necessary because Radix will sometimes need to attach a ref to your component, for example to measure its size. Without forwarding the ref, the functionality will break.
Responsibility for accessibility when changing element types
When you use asChild to change the underlying element type or compose Radix functionality onto a custom component, it is your responsibility to ensure the element type rendered remains accessible and functional. Radix provides the prop passing and event handling, but the correctness of the DOM element choice is your responsibility.
Extending Radix primitives with React.forwardRef
Radix primitives can be extended the same way as any React component. Use React.forwardRef with proper typing via React.ElementRef and React.ComponentPropsWithoutRef to create a wrapper component.
Radix Primitives are unstyled
Radix Primitives ship without styles. They are compatible with any styling solution, giving you complete control over styling.
Functional styles are your responsibility
You are in control of all aspects of styling, including functional styles. For example, by default a Dialog Overlay won't cover the entire viewport. You are responsible for adding those styles, plus any presentation styles.
className prop on components and parts
All components and their parts accept a className prop. This class will be passed through to the DOM element and can be used in CSS as expected.
data-state attribute for component state
When components are stateful, their state is exposed in a data-state attribute. For example, when an Accordion Item is opened, it includes a data-state="open" attribute.
Styling CSS with className targeting
You can style a component part by targeting the className that you provide. Apply styles directly to the class name in your CSS.
Styling CSS with data-state attribute
You can style a component state by targeting its data-state attribute using attribute selectors in CSS, such as .AccordionItem[data-state="open"].
CSS-in-JS styling with Radix primitives
CSS-in-JS libraries like styled-components can directly wrap Radix primitive components to apply styles. You can provide the Radix primitive component directly to the CSS-in-JS function and target data-state attributes with ampersand notation like &[data-state="open"].
Example: Extending Accordion.Item with React.forwardRef
import * as React from "react";
import { Accordion as AccordionPrimitive } from "radix-ui";
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>((props, forwardedRef) => (
<AccordionPrimitive.Item {...props} ref={forwardedRef} />
));
AccordionItem.displayName = "AccordionItem";
Components are uncontrolled by default but can be controlled
Where applicable, Radix Primitives components are uncontrolled by default but can also be controlled. All behavior wiring is handled internally, allowing users to get up and running without needing to create local states.
Open component architecture for customization
Radix Primitives are designed with an open component architecture that provides granular access to each component part, so users can wrap them and add their own event listeners, props, or refs.
asChild prop for full element control
Radix Primitives provides an asChild prop that gives users full control over the rendered element.
Tooltip version 0.0.17 breaking change: anchorRef prop removed
In Tooltip version 0.0.17, the anchorRef prop was removed.
Escape key capture in dismissible components
Make sure that components that close on Escape key press capture the corresponding keyboard event. This way you can call stopPropagation in onEscapeKeyDown if you need more control rendering Radix components within another component that closes on Escape key press.
Roving focus no interference with browser hotkeys
Make sure that components with roving focus do not interfere with browser or system hotkeys, such as back navigation.
hideWhenDetached preventing interactions
Make sure that components that support hideWhenDetached prop do not allow interactions with hidden content.
DirectionProvider required for RTL support as of July 21 2022
Starting July 21, 2022, DirectionProvider must be used if relying on RTL support. The dir attribute inheritance from document (or any element) is no longer supported for RTL performance reasons.
Portal container prop replaces containerRef
Portal's containerRef prop changed to container, which accepts an element instead of a ref. The data-radix-portal attribute was removed; use asChild to control the element instead.
Portal z-index no longer managed
Portal no longer manages z-index, providing full control of layering to the developer.
asChild prop replaces polymorphic as prop
The polymorphic 'as' prop was replaced with an 'asChild' boolean prop across all primitives. This changes how you compose and render custom elements.
IdProvider deprecated and removed
IdProvider was deprecated in December 2021 and is no longer needed. It should be removed from your app to avoid deprecation warnings.
Modal prop for Dialog, Popover, DropdownMenu, ContextMenu, and AlertDialog
Dialog, Popover, DropdownMenu, ContextMenu, and AlertDialog have a modal prop for modality support.
Toggle Group version 0.0.7 adds optional orientation, dir, and loop props
Toggle Group version 0.0.7 added optional orientation, dir, and loop props.
Menu items no hover scroll
Don't scroll menu items in response to hover.
Toggle version 0.0.6 breaking changes: renamed from ToggleButton with prop renames
In Toggle version 0.0.6, the ToggleButton primitive was renamed to Toggle. The toggled prop was renamed to pressed, defaultToggled was renamed to defaultPressed, and onToggledChange was renamed to onPressedChange.
Toggle Group version 0.0.6 introduced as new primitive
Toggle Group was introduced as a new primitive in version 0.0.6.
Toolbar version 0.0.9 introduced as new primitive
Toolbar was introduced as a new primitive in version 0.0.9.
Tooltip version 0.0.16 adds custom timing and unmount animation support
Tooltip version 0.0.16 added custom timing support and unmount animation support.
All primitives version 0.0.3 adds SSR support and removes selector prop and data-radix-* attributes
In all primitives version 0.0.3, support for server-side rendering (SSR) was added. The selector prop and data-radix-* attributes were removed as breaking changes.
Tabs version 0.0.6 adds RTL support with dir prop
Tabs version 0.0.6 added right-to-left (RTL) support through a dir prop.
Slot version 0.0.1 introduced as new utility
Slot was introduced as a new utility in version 0.0.1.
Slot component inheritance
The Slot component in Radix themes inherits all props and functionality from the Slot primitive utility.
Slot component purpose
Slot merges its props onto its immediate child.
Default cursor for interactive elements
By default, interactive elements that don't link to another page use the regular arrow cursor. Disabled elements use an explicit disabled cursor.
Available cursor CSS variables
Cursor settings can be accessed using the following CSS variables: var(--cursor-button), var(--cursor-checkbox), var(--cursor-disabled), var(--cursor-link), var(--cursor-menu-item), var(--cursor-radio), var(--cursor-slider-thumb), var(--cursor-slider-thumb-active), var(--cursor-switch).
Customizing cursor tokens example
Cursor tokens can be customized by overriding CSS variables within the .radix-themes class. Example customization: --cursor-button: pointer; --cursor-checkbox: pointer; --cursor-disabled: default; --cursor-link: pointer; --cursor-menu-item: pointer; --cursor-radio: pointer; --cursor-slider-thumb: grab; --cursor-slider-thumb-active: grabbing; --cursor-switch: pointer;
CSS variable precedence for cursor customization
Custom CSS for cursor tokens must be applied after the Radix Themes styles so that it takes precedence.
Component variants for visual hierarchy
Variants are visual variations of a component used to create visual hierarchies and communicate relative importance. Each component offers a different set of variants, all designed to be consistent and complimentary with each other. Examples include 'classic', 'solid', and 'soft' variants.
Theme radius setting manages radius factor applied to components
The Theme component has a radius prop that sets a radius factor applied to all components. The resulting border-radius is contextual and differs depending on the component. For example, when set to 'full', a Button becomes pill-shaped, while a Checkbox will never become fully rounded to prevent confusion with Radio.
Radius prop values and component support
Certain components allow you to override the radius factor using their own radius prop with values: none, small, medium, large, and full. Components that render panels like Card, Dialog, and Popover do not have the radius prop but inherit the radius setting from the theme. The radius prop is also unavailable on most text-based components.
Radius scale CSS variables
Radius tokens are accessed using CSS variables. The available variables are: var(--radius-1), var(--radius-2), var(--radius-3), var(--radius-4), var(--radius-5), var(--radius-6). Additionally, var(--radius-factor) is a multiplier that controls the theme radius, var(--radius-full) is used to calculate a fully rounded radius usually within a CSS max() function, and var(--radius-thumb) is used to calculate radius of a thumb element also usually within a CSS max() function.
Radius scale is 6-step
Radius values used in components are derived from a 6-step scale. While you cannot use a specific step on a particular component directly through the radius prop, you can use the radius scale CSS variables to style custom components consistently with the rest of your theme.
Theme radius example with TextField and Button
Example showing Theme with radius="medium" containing a TextField.Root with size="3" and placeholder="Reply…", with a TextField.Slot on the right side containing a Button with size="2" and text "Send".