Redpanda UIRedpanda UI

Upgrading to Base UI

How to upgrade consumer apps to the Base UI–backed registry.

Who this is for

Any project that installs components from the Redpanda UI Registry via shadcn add "https://redpanda-ui-registry.netlify.app/r/<component>" or the @redpanda/... namespaced form. If you only use the docs site or the playground, no action is needed.

One-time install

Add @base-ui/react to your dependencies and remove radix-ui if it was only pulled in transitively via the registry:

bun add @base-ui/react@^1.4.0
bun remove radix-ui   # only if you don't use Radix directly elsewhere

npm / pnpm / yarn work the same:

npm install @base-ui/react@^1.4.0
# or
pnpm add @base-ui/react@^1.4.0
# or
yarn add @base-ui/react@^1.4.0

Re-run shadcn add

Re-install every registry component you consume so the latest source (Base UI–backed) is copied into your project:

npx shadcn@latest add "https://redpanda-ui-registry.netlify.app/r/button" \
                      "https://redpanda-ui-registry.netlify.app/r/dialog" \
                      "https://redpanda-ui-registry.netlify.app/r/popover" \
                      # ...and any other components you use

Or, if you use the namespaced form:

npx shadcn@latest add @redpanda/button @redpanda/dialog @redpanda/popover

Public API compatibility

Every public symbol is preserved byte-for-byte:

SurfaceStatus
Exported component namesUnchanged
asChild propUnchanged (translated to Base UI's render prop internally)
onOpenChange(open: boolean) signatureUnchanged (compat wrapper narrows Base UI's (open, details)(open))
onValueChange(value) / onCheckedChange(checked) / onPressedChange(pressed)Unchanged
data-state="open" | "closed" | "checked" | "unchecked" | "indeterminate"Unchanged (compat render prop sets it alongside Base UI's native data-open / data-closed)
data-slot="..." attributesUnchanged
data-orientationUnchanged
Tailwind className stringsUnchanged

If you wrote CSS against data-[state=open]:..., data-[state=checked]:..., or [data-slot="..."] — it still works.

Behavioural deviations (eight, flagged in-docs)

The compat layer could not preserve these 1:1. Each flagged component page carries a Callout with the details; the summary:

  1. NavigationMenuIndicator — rendered as a no-op <div>. Base UI has no indicator primitive.
  2. Menubar controlled-open props removed — Radix's value / defaultValue / onValueChange on the root are gone. Use defaultOpen / open / onOpenChange on each child MenubarMenu.
  3. DropdownMenuContent.forceMount — no longer forwarded. Use keepMounted on the Portal instead.
  4. CollapsibleContent.forceMountkeepMounted — renamed on the primitive.
  5. Progress CSS variable--radix-progress-indicator-transform is gone. The indicator now writes width inline via Motion; rewrite any custom CSS that targeted the old var.
  6. Separator.decorative — accepted for API parity but ignored at runtime.
  7. Accordion type="single" collapsible={false} — Base UI always allows re-close; the prop is accepted but ignored.
  8. MultiSelectContent.onOpenAutoFocus — accepted for API parity but no longer invoked. Use initialFocus on the Popup if you need equivalent behaviour.

Full detail: MIGRATION.md.

DOM shape changes

Floating popups (Popover, Menu, Select, Tooltip, HoverCard, ContextMenu) now render an extra Positioner wrapper between the Portal and the Popup. CSS selectors that hard-code deeply-nested element positions may need adjusting — selectors on [role=dialog], [role=menu], [data-slot="..."], or [data-state=...] keep working.

Visual regression baselines should be re-approved after the upgrade, but no pixel-level styling of the components themselves changed.

Rollback

If the migration is blocking you, pin to the previous registry release (0.3.1) and keep radix-ui@^1.4.3. File an issue with your Tailwind selectors, custom CSS, or consumer integration details and we will patch the compat layer.

Built by malinskibeniamin. The source code is available on GitHub.

On this page