Presentational parts for a dropdown menu: a styled container (Dropdown.Items) and anchor-based menu items (Dropdown.Item). It provides styling and item semantics only — pair it with a headless behaviour/positioning layer such as @base-ui/react's Menu.
Use Dropdown when you need the visual menu panel and items but are driving open/close and positioning with Base UI (or similar). It does not position itself — there is no @floating-ui logic here.
Popover.Menu.There is no @uxf/form twin.
Grounded in the story — Base UI's Menu handles state and positioning; Dropdown supplies the styled surface. Menu.Root renders no host element, so the wrapper <div className="relative"> is written out explicitly, and a single Menu.Item with a render prop is both the menu item and the styled item. (Before the migration to Base UI, the equivalent state came from Headless UI's render prop as active; today it is state.highlighted.)
uxf-dropdown is position: absolute by default, which is what the standalone recipe (a relative wrapper and no headless positioner) needs. Because the panel below is placed by Menu.Positioner, it opts into position: relative with uxf-dropdown--positioned — without it the positioner has an out-of-flow child and collapses to zero size.
The panel classes go straight on Menu.Popup's className, not through Dropdown.Items: Dropdown.Items only forwards className, role, children and ref, so passing it to Menu.Popup via render would silently drop every other prop Base UI's popup needs to be keyboard-navigable (its own id, event handlers, tabIndex, …). Same pattern as button-list.tsx's own menu.
import { Menu } from "@base-ui/react/menu";
import { CLASSES } from "@uxf/core/constants/classes";
import { Button } from "@uxf/ui/button";
import { Dropdown } from "@uxf/ui/dropdown";
const items = [
{ id: 1, title: "Test 1" },
{ id: 2, title: "Test 2" },
];
<div className="relative">
<Menu.Root>
{/* `Button` renders an <a>, so Base UI must not assume native button semantics. */}
<Menu.Trigger nativeButton={false} render={<Button color="positive">Click me</Button>} />
<Menu.Portal>
<Menu.Positioner align="start" side="bottom" sideOffset={0}>
<Menu.Popup className="uxf-dropdown uxf-dropdown--positioned">
{items.map((item) => (
<Menu.Item
key={item.id}
render={(itemProps, state) => (
<Dropdown.Item {...itemProps} className={state.highlighted ? CLASSES.IS_ACTIVE : ""}>
{item.title}
</Dropdown.Item>
)}
/>
))}
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</div>;
Import: import { Dropdown } from "@uxf/ui/dropdown".
Dropdown.ItemsThe menu panel — a <div className="uxf-dropdown"> that forwards its ref. Accepts standard HTMLAttributes<HTMLDivElement> (className, role, children, …); the passed role is forwarded to the div. (The exported DropdownItemsProps type is the ref element type HTMLDivElement, not the props type.)
Dropdown.ItemA single item, rendered as an <a> (or a Next.js link via as) with role="menuitem" and class uxf-dropdown__item.
DropdownItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> (minus type) plus UseAnchorProps:
| Prop | Type | Default | Description |
|---|---|---|---|
as | "a" | NextLink | "a" | Element to render. Pass a Next.js Link for client-side navigation. |
isDisabled | boolean | false | Non-interactive state (adds the disabled class, drops it from tab order). |
isLoading | boolean | false | Busy state. |
analyticsCallback | () => void | — | Called when the item is activated. |
type | "submit" | — | Submit the closest <form> on activation; the only accepted type. |
Standard anchor attributes (href, onClick, className, children, …) are also accepted.
Item state is driven by classes on uxf-dropdown__item (typically supplied by the headless layer's render props):
| Class | Meaning |
|---|---|
is-active | Highlighted / keyboard-active item. Use CLASSES.IS_ACTIVE from @uxf/core/constants/classes. |
is-selected | Currently selected value. |
is-disabled | Disabled item. |
The panel itself takes one modifier:
| Class | Meaning |
|---|---|
uxf-dropdown--positioned | Something else owns the placement (a Base UI Positioner, or any wrapper that already sizes the box) — switches the panel from position: absolute to relative. |
The stylesheet also defines a uxf-dropdown__items class that is deprecated — use Dropdown.Items (which renders uxf-dropdown). It is position: absolute, matching the base uxf-dropdown rule.
Import the component stylesheet once in your global CSS:
@import url("@uxf/ui/css/dropdown.css");
Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.