Motivation
agentic-ui is a styling layer over base-ui, not a replacement. Understand when to use the high-level components and when to reach directly for base-ui primitives.
@brijbyte/agentic-ui is an augmentation layer on top of @base-ui/react. Every component it ships is built from the same primitives that @base-ui/react exposes — it adds scoped CSS module styles, design tokens, and convenience wrappers, but delegates all accessibility, keyboard interaction, focus management, and ARIA semantics entirely to base-ui.
The library exists to answer one question: what should these components look like? It does not try to answer questions about behaviour, state management, or composition — base-ui already answers those.
agentic-ui is not a replacement for @base-ui/react. Its high-level components — Dialog, Drawer, Select, and others — are intentionally thin conveniences for the common case. They intentionally expose only a subset of the underlying base-ui API surface.
Whenever a component does not expose a prop you need — a custom open/close callback, a ref, a specific ARIA attribute, an animation option — do not work around the high-level wrapper. Reach for base-ui directly and compose agentic-ui styled parts onto it.
render prop or a child.Every complex component in agentic-ui exports its styled sub-parts individually alongside the high-level convenience wrapper. The naming follows base-ui exactly —DialogPopup styles BaseDialog.Popup, AccordionHeader styles BaseAccordion.Header, and so on.
When you need behaviour that the high-level wrapper does not expose, drop down to the base-ui root and compose the styled parts in:
import * as BaseAccordion from "@base-ui/react/accordion";
import { AccordionItem, AccordionHeader, AccordionPanel } from "@brijbyte/agentic-ui/accordion";
import "@brijbyte/agentic-ui/accordion.css";
// Use the base-ui Root for full control over open state and callbacks,
// then drop in the agentic-ui styled parts for appearance.
<BaseAccordion.Root value={openItems} onValueChange={setOpenItems} loop>
<AccordionItem value="item-1">
<AccordionHeader render={<BaseAccordion.Header />}>
What is agentic-ui?
</AccordionHeader>
<AccordionPanel render={<BaseAccordion.Panel />}>
A set of styled parts built on top of base-ui primitives.
</AccordionPanel>
</AccordionItem>
</BaseAccordion.Root>;
The styled parts carry no state and own no behaviour. Swapping the root from the agentic-ui convenience wrapper to the raw base-ui root costs zero styling — everything looks identical.
It is tempting to import the high-level component for every use, reach for a prop that is not there, and then work around the wrapper with hacks. That path leads to brittle code. The intended pattern is the opposite: start with the base-ui root, add the agentic-ui styled parts you need, and own the composition yourself.
// ✗ Avoid — treating agentic-ui as a drop-in replacement breaks when you
// need behaviour the high-level component doesn't expose.
import { Dialog } from "@brijbyte/agentic-ui/dialog";
import "@brijbyte/agentic-ui/dialog.css";
<Dialog trigger={<button>Open</button>}>Content</Dialog>;
// ✓ Prefer — use base-ui for state/behaviour, agentic-ui for styling.
import * as BaseDialog from "@base-ui/react/dialog";
import { DialogBackdrop, DialogPopup, DialogTitle } from "@brijbyte/agentic-ui/dialog";
import "@brijbyte/agentic-ui/dialog.css";
<BaseDialog.Root open={open} onOpenChange={setOpen} animated>
<BaseDialog.Portal>
<DialogBackdrop />
<BaseDialog.Viewport>
<DialogPopup>
<DialogTitle>Confirm action</DialogTitle>
{/* your content here */}
</DialogPopup>
</BaseDialog.Viewport>
</BaseDialog.Portal>
</BaseDialog.Root>;
You are never forced to use all of a component's styled parts. Any part can be left unstyled or replaced with a custom implementation — agentic-ui parts and raw base-ui parts are fully interchangeable at any position in the tree.
import * as BaseSelect from "@base-ui/react/select";
import { SelectTrigger, SelectPopup, SelectItem } from "@brijbyte/agentic-ui/select";
import "@brijbyte/agentic-ui/select.css";
// Mix agentic-ui styled parts with a custom unstyled part — here the
// Positioner is owned by the app so it can set custom collision padding.
<BaseSelect.Root value={value} onValueChange={setValue}>
<SelectTrigger render={<BaseSelect.Trigger />} />
<BaseSelect.Portal>
<BaseSelect.Positioner sideOffset={8} collisionPadding={16}>
<SelectPopup render={<BaseSelect.Popup />}>
<SelectItem render={<BaseSelect.Item value="light" />}>Light</SelectItem>
<SelectItem render={<BaseSelect.Item value="dark" />}>Dark</SelectItem>
<SelectItem render={<BaseSelect.Item value="system" />}>System</SelectItem>
</SelectPopup>
</BaseSelect.Positioner>
</BaseSelect.Portal>
</BaseSelect.Root>;
In this example the Positioner is owned by the app so it can set custom collision padding, while the visual parts (SelectTrigger, SelectPopup, SelectItem) come from agentic-ui. The design-system appearance is preserved exactly.
- Use
@base-ui/reactfor state, behaviour, accessibility, and composition. - Use
@brijbyte/agentic-uiparts for design-system appearance. - The high-level wrappers are shortcuts — not contracts. Bypass them freely.
- When in doubt, compose. There is no penalty for mixing styled and unstyled parts.
