Begin with the stack you have
A primitive is infrastructure. Consistency with the existing application matters more than novelty.
Inventory the primitives already used for dialogs, menus, focus management, and form state. Reusing that family reduces bundle overlap and keeps behavior predictable for contributors.
- Choose Radix when the product already composes Radix parts and Slot patterns.
- Choose Base UI when the application uses its unstyled components and event model.
- Choose React Aria when React Aria Components already owns interaction and accessibility behavior.
Compare the component contract
The visible button may match across adapters while the composition API does not.
<RadixButton variant="primary">Save changes</RadixButton>
<BaseUIButton variant="primary">Save changes</BaseUIButton>
<AriaButton variant="primary">Save changes</AriaButton>Inspect ref behavior, event signatures, disabled semantics, and child composition before standardizing an adapter. These differences surface later in forms and compound components.
Record the decision
A short architecture note prevents the next component from reopening the same choice.
Keep the proof with the change.
Record the commands you ran, the states you checked, and the evidence that remains outside this guide.
Put it into practice