Skip to main content
Primitives are the foundational controls in Twenty UI. They include inputs, display elements, navigation, and compound controls such as Select, Popover, and AlertDialog. Each primitive receives data and callbacks from your application. Import primitives from a family entry point, such as twenty-ui/primitives/input. See Getting started for installation and theme setup. Components combine these primitives into shared design presets and reusable app building blocks.

State ownership

Use uncontrolled state when a component can manage its own interactions. A default prop supplies the initial state. Use controlled state when the application needs to coordinate that state with other controls, navigation, or application data. In controlled mode, pass the current state and update it in the change callback. In uncontrolled mode, you can still listen to changes without taking ownership of the state. Adding a callback alone does not make a component controlled. Choose a mode for each state and keep it for the lifetime of the component. Default props initialize state; changing them later does not replace the current state. Avoid passing both the default and controlled prop for the same state. Some components have more than one independent state. For example, Select can have a controlled value and uncontrolled popup visibility. State belongs to the part that manages the interaction. Configure radio selection on RadioGroup, not on each Radio. Display components such as Text do not have a controlled or uncontrolled mode. Toast delegates its visibility and removal to the application.

Composition

Compound primitives expose parts such as Select.Root, Select.Trigger, and Select.Popup. Keep related parts under the same root in the component tree so they share state and accessibility relationships. Each component’s anatomy section identifies the required parts and optional content. Slots such as InputGroup.startElement and ListItem.actions accept React nodes as props. They add content within a component’s layout. Parts and slots are different APIs; use the names listed in the component guide. Some primitives package supporting overlay elements inside their Popup part: Place content directly inside these popup parts. Their portal may move the rendered DOM to another container while preserving the component hierarchy and context. See theming for scoped portal containers. Autocomplete exposes separate Portal, Positioner, and Popup parts. Configure the destination and mounting on Portal, placement and collision handling on Positioner, and content width and native props on Popup. Their refs and render composition target their own elements. Autocomplete also preserves the installed upstream collection, grouping, grid, filtering, and status parts. Dialog and AlertDialog expose separate Portal, Backdrop, Viewport and Popup parts. Configure mounting and the destination on Portal, overlay appearance on Backdrop and Viewport, and content size and focus on Popup. Each part preserves its own native attributes, ref target and render composition. These dialog APIs are for standalone UI and the Twenty host. Front components use the SDK host dialog API.

Custom components

For parts that support render, pass a compatible element or component to replace the default element. For example, Popover.Trigger can use a Twenty UI Button while retaining its trigger behavior. A custom wrapper must pass the supplied props and ref to the interactive element. This preserves event handlers, accessibility attributes, positioning, and focus restoration. This example forwards them through Button, which attaches them to its native button.
Pass the button through render and keep each action as a single interactive control. Preserve the handlers and ref supplied to your wrapper when adding your own behavior. The ref must reach the element that receives focus. Use a native button for button-like parts. On parts that expose nativeButton, set nativeButton={false} only when the rendered element is not a native button. A custom component that ultimately renders a button, such as HelpButton, keeps the default button behavior.

Browse primitives

Each page includes usage examples, interaction guidance, and a generated API reference.