agentic-ui logoagentic-ui

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.

What agentic-ui is

@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.

What agentic-ui is not

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.

Think of agentic-ui exports as a palette of styled bricks, not as sealed black-box components. Any part can be lifted out and placed anywhere a base-ui primitive accepts a render prop or a child.

The augmentation model

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.

Avoid using as a drop-in replacement

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>;
The high-level wrappers are conveniences for demos and simple cases — they are not the primary API. The primary API is the set of styled parts.

Mixing styled and unstyled parts

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.

In short

  • Use @base-ui/react for state, behaviour, accessibility, and composition.
  • Use @brijbyte/agentic-ui parts 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.
The Quick Start guide shows how to wire up CSS and render your first component. The component pages each include a composed example showing the base-ui root with agentic-ui parts.