Relevant source files
The following files were used as context for generating this wiki page:
- packages/base-ui/registry/index.ts
- packages/radix-ui/registry/index.ts
- packages/base-ui/package.json
- packages/base-ui/src/components/dialog/search.tsx
- packages/api-docs/src/components/dialog.tsx
- packages/base-ui/src/layouts/notebook/slots/sidebar.tsx
- packages/radix-ui/src/layouts/notebook/slots/sidebar.tsx
- packages/sanity/registry/base.component.tsx
- packages/base-ui/src/components/tabs.tsx
- packages/base-ui/src/components/card.tsx
- packages/base-ui/src/components/accordion.tsx
- packages/cli/src/commands/shared.ts
- packages/radix-ui/src/components/dialog/search.tsx
- packages/base-ui/src/components/sidebar/base.tsx
- packages/base-ui/src/layouts/home/navbar.tsx
- packages/base-ui/src/layouts/flux/slots/sidebar.tsx
- packages/base-ui/src/components/toc/index.tsx
- packages/radix-ui/package.json
- packages/python/src/components/index.tsx
- packages/openapi/src/ui/base.tsx
- packages/base-ui/src/provider/waku.tsx
- packages/radix-ui/src/components/card.tsx
- packages/radix-ui/src/components/sidebar/base.tsx
- packages/base-ui/src/layouts/shared/client.tsx
- packages/base-ui/src/provider/react-router.tsx
- packages/base-ui/src/provider/next.tsx
- packages/base-ui/src/provider/tanstack.tsx
- packages/base-ui/src/components/ui/navigation-menu.tsx
- packages/base-ui/src/components/ui/accordion.tsx
- packages/base-ui/src/layouts/flux/page/index.tsx
Overview
Base UI Components provides the core interactive building blocks, layout primitives, and styling integration for Fumadocs UI, enabling flexible documentation site construction across multiple JavaScript UI toolkits and routing frameworks. Built around modular registry architectures and headless design patterns, the package supports dual distribution via @fumadocs/base-ui and @fumadocs/radix-ui registries. It solves the challenge of maintaining accessible, framework-agnostic documentation layouts, keyboard-navigable search dialogs, collapsible navigation trees, and synchronized tab containers through encapsulated React components and utility hooks.
Sources: packages/base-ui/registry/index.ts:9-10, packages/radix-ui/registry/index.ts:9-10, packages/base-ui/package.json:2-4, packages/radix-ui/package.json:2-4, packages/cli/src/commands/shared.ts:1-4
Package Architecture and Registry Discovery
Registry Mapping and Package Configuration
The CLI command configuration maps UI registries to specific identifier strings through the UIRegistries object, binding 'base-ui' to 'fumadocs/base-ui' and 'radix-ui' to 'fumadocs/radix-ui'.
Sources: packages/cli/src/commands/shared.ts:1-4
export const UIRegistries = {
'base-ui': 'fumadocs/base-ui',
'radix-ui': 'fumadocs/radix-ui',
};Sources: packages/cli/src/commands/shared.ts:1-4
Both packages share version 16.10.2 and export modular entry points, yet their distribution layouts diverge based on underlying primitive dependencies. @fumadocs/base-ui defines package exports for base UI components, providers, and layout slots while targeting @fumadocs/base-ui.
Sources: packages/base-ui/package.json:2-3, packages/base-ui/package.json:18-114
{
"name": "@fumadocs/base-ui",
"version": "16.10.2",
"description": "The Base UI version of Fumadocs UI"
}Sources: packages/base-ui/package.json:1-4
Conversely, @fumadocs-ui (published from the radix-ui package directory) exposes legacy layout and sidebar entries alongside equivalent Radix UI primitive paths such as ./utils/get-sidebar-tabs and ./components/tabs.unstyled.
Sources: packages/radix-ui/package.json:1-4, packages/radix-ui/package.json:94-95, packages/radix-ui/package.json:116-117
{
"name": "fumadocs-ui",
"version": "16.10.2",
"description": "The Radix UI version of Fumadocs UI"
}Sources: packages/radix-ui/package.json:1-4
Component Registries and Layout Definitions
The registry definitions for both base-ui and radix-ui construct a component collection by spreading commonComponents and evaluating findSlotComponents(dir), subsequently registering unlisted layout blocks including layouts/sidebar, layouts/shared, layouts/docs, layouts/flux, layouts/notebook, layouts/home, and modular component entries like accordion, github-info, banner, callout, card, codeblock, files, heading, image-zoom, inline-toc, steps, and tabs.
Sources: packages/base-ui/registry/index.ts:9-14, packages/base-ui/registry/index.ts:15-35, packages/base-ui/registry/index.ts:159-272, packages/radix-ui/registry/index.ts:9-14, packages/radix-ui/registry/index.ts:15-35, packages/radix-ui/registry/index.ts:159-272
export const registry: Registry = {
name: 'fumadocs/base-ui',
dir,
components: [
...commonComponents,
...(await findSlotComponents(dir)),
{
name: 'layouts/sidebar',
unlisted: true,
files: [
{
type: 'components',
path: 'components/sidebar/base.tsx',
target: '<dir>/docs-sidebar/base.tsx',
},
{
type: 'components',
path: 'components/sidebar/page-tree.tsx',
target: '<dir>/docs-sidebar/page-tree.tsx',
},
{
type: 'components',
path: 'components/sidebar/link-item.tsx',
target: '<dir>/docs-sidebar/link-item.tsx',
},
],
},Sources: packages/base-ui/registry/index.ts:9-34
Note
File order matters in the shadcn CLI registry configuration when writing import paths on consumer codebases, making the sequence of layout and component registrations critical for proper module resolution. Sources: packages/base-ui/registry/index.ts:8-14, packages/radix-ui/registry/index.ts:8-14
Search Dialog Component Primitives
List Virtualization and Keyboard Navigation
The search dialog lifecycle and presentation are composed around primitive wrappers, including SearchDialogOverlay, SearchDialogContent, SearchDialogList, and SearchDialogListItem. These components manage portal mounting, backdrop blur effects, animated height adjustments via ResizeObserver, and keyboard navigation handlers.
Sources: packages/base-ui/src/components/dialog/search.tsx:268-283
SearchDialogContent wraps content inside Dialog.Portal and Dialog.Popup, fixing the container's position to the center of the screen with responsive top offset calculations (md:top-[calc(50%-250px)]), maximum width sizing (max-w-screen-sm), overflow clipping, and entrance/exit animation hooks (data-open:animate-fd-dialog-in, data-closed:animate-fd-dialog-out).
export function SearchDialogContent({
children,
className,
...props
}: ComponentProps<typeof Dialog.Popup>) {
const t = useTranslations({ note: 'search dialog' });
return (
<Dialog.Portal>
<Dialog.Popup
id="fd-search-dialog-content"
aria-describedby={undefined}
{...props}
className={(s) =>
cn(
'fixed left-1/2 top-4 md:top-[calc(50%-250px)] z-50 w-[calc(100%-1rem)] max-w-screen-sm -translate-x-1/2 rounded-xl border bg-fd-popover text-fd-popover-foreground shadow-2xl overflow-hidden data-closed:animate-fd-dialog-out data-open:animate-fd-dialog-in focus-visible:outline-none',
'*:border-b *:has-[+:last-child[data-empty=true]]:border-b-0 *:data-[empty=true]:border-b-0 *:last:border-b-0',
typeof className === 'function' ? className(s) : className,
)
}
>
<Dialog.Title className="hidden">{t('Search')}</Dialog.Title>
{children}
</Dialog.Popup>
</Dialog.Portal>
);
}Sources: packages/base-ui/src/components/dialog/search.tsx:285-311
Note
SearchDialogContent explicitly forces aria-describedby={undefined} on the underlying Dialog.Popup component to suppress screen reader warnings regarding missing descriptive node elements when rendering dynamic search records.
Sources: packages/base-ui/src/components/dialog/search.tsx:295-296
SearchDialogList manages active item selection state, observes container dimensions via ResizeObserver to dynamically apply a --fd-animated-height CSS property for height transitions, and binds window- or content-level keydown listeners for keyboard traversal.
export function SearchDialogList({
items = null,
Empty = () => (
<div className="py-12 text-center text-sm text-fd-muted-foreground">
<T text="No results found" note="search dialog" />
</div>
),
Item = (props) => <SearchDialogListItem {...props} />,
...props
}: Omit<ComponentProps<'div'>, 'children'> & {
items: SearchItemType[] | null | undefined;
/**
* Renderer for empty list UI
*/
Empty?: () => ReactNode;
/**
* Renderer for items
*/
Item?: (props: { item: SearchItemType; onClick: () => void }) => ReactNode;
}) {
const ref = useRef<HTMLDivElement>(null);
const { onSelect } = useSearch();
const [active, setActive] = useState<string | null>(() =>
items && items.length > 0 ? items[0].id : null,
);
const onKey = useEffectEvent((e: KeyboardEvent) => {
if (!items || e.isComposing) return;
if (e.key === 'ArrowDown' || e.key == 'ArrowUp') {
let idx = items.findIndex((item) => item.id === active);
if (idx === -1) idx = 0;
else if (e.key === 'ArrowDown') idx++;
else idx--;
setActive(items.at(idx % items.length)?.id ?? null);
e.preventDefault();
}
if (e.key === 'Enter') {
const selected = items.find((item) => item.id === active);
if (selected) onSelect(selected);
e.preventDefault();
}
});
useEffect(() => {Sources: packages/base-ui/src/components/dialog/search.tsx:313-360
The keyboard event handler processes specific navigation keys to modify the active selection index modulo the total item length, and triggers selection upon pressing Enter.
| Key | Action | Behavior |
|---|---|---|
ArrowDown | Increment Index | Moves active focus down; wraps to index 0 at the end of the list. |
ArrowUp | Decrement Index | Moves active focus up; wraps to the last item when reaching the top. |
Enter | Select Item | Invokes onSelect with the currently active search item object. |
Sources: packages/base-ui/src/components/dialog/search.tsx:342-358
Warning
Keyboard listeners check e.isComposing before evaluating arrow keys or enter inputs to prevent capturing keystrokes while users are actively composing characters via an Input Method Editor (IME).
Sources: packages/base-ui/src/components/dialog/search.tsx:340
Collapsible Accordions and Trigger Controls
Accordion Structure and State Synchronization
The accordion components are built on top of @base-ui/react/accordion primitives, providing accessible disclosure containers with animated height transitions and hash-based anchor linking. The exported Accordions wrapper component maintains the expanded item values in local state while reading the initial window.location.hash on mount to automatically expand targeted items.
export function Accordions({
ref,
className,
defaultValue,
...props
}: ComponentProps<typeof Root>) {
const rootRef = useRef<HTMLDivElement>(null);
const composedRef = mergeRefs(ref, rootRef);
const [value, setValue] = useState<unknown[]>(defaultValue ?? []);
useEffect(() => {
const id = window.location.hash.substring(1);
const element = rootRef.current;
if (!element || id.length === 0) return;
const selected = document.getElementById(id);
if (!selected || !element.contains(selected)) return;
const value = selected.getAttribute('data-accordion-value');
if (value) setValue((prev) => [value, ...prev]);
}, []);Sources: packages/base-ui/src/components/accordion.tsx:18-38
Note
When an element ID matches the URL hash upon initial page load, the accordion component verifies that the target DOM element exists inside the accordion container before prepending its data-accordion-value into the expanded state array.
Sources: packages/base-ui/src/components/accordion.tsx:28-38
The styling and layout structure for the accordion components are mapped through UI wrapper functions defined in the UI layer.
| Component | Base UI Primitive | CSS Classes and Styling Behavior |
|---|---|---|
Accordion | Primitive.Root | divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card |
AccordionItem | Primitive.Item | Passes props directly without extra wrapper classes. |
AccordionHeader | Primitive.Header | scroll-m-24 not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent |
AccordionTrigger | Primitive.Trigger | group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none |
AccordionContent | Primitive.Panel | h-(--accordion-panel-height) overflow-hidden transition-[height] ease-out data-[ending-style]:h-0 data-[starting-style]:h-0 |
Sources: packages/base-ui/src/components/ui/accordion.tsx:8-84
Triggers, Content Panels, and Copy Actions
Individual Accordion sections combine an AccordionItem, AccordionHeader, AccordionTrigger, and AccordionContent with an optional link-copy button. The trigger incorporates a rotating chevron indicator that reacts to panel opening states via group data attributes.
export function AccordionTrigger({
className,
children,
...props
}: ComponentProps<typeof Primitive.Trigger>) {
return (
<Primitive.Trigger
className={(s) =>
cn(
'group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
>
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[panel-open]:rotate-90" />
{children}
</Primitive.Trigger>
);Sources: packages/base-ui/src/components/ui/accordion.tsx:46-64
export function AccordionContent({
className,
children,
...props
}: ComponentProps<typeof Primitive.Panel>) {
return (
<Primitive.Panel
className={(s) =>
cn(
'h-(--accordion-panel-height) overflow-hidden transition-[height] ease-out data-[ending-style]:h-0 data-[starting-style]:h-0',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
>
{children}
</Primitive.Panel>
);Sources: packages/base-ui/src/components/ui/accordion.tsx:67-84
When an id prop is supplied to the Accordion component, a CopyButton is rendered inside the header alongside the trigger. This button uses useCopyButton to write a direct URL hash link to the clipboard and temporarily toggles a check icon upon successful copying.
export function Accordion({
title,
id,
value = String(title),
children,
...props
}: Omit<ComponentProps<typeof AccordionItem>, 'value' | 'title'> & {
title: string | ReactNode;
value?: string;
}) {
return (
<AccordionItem value={value} {...props}>
<AccordionHeader id={id} data-accordion-value={value}>
<AccordionTrigger>{title}</AccordionTrigger>
{id ? <CopyButton id={id} /> : null}
</AccordionHeader>
<AccordionContent hiddenUntilFound>
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin [&[hidden]:not([hidden='until-found'])]:hidden">
{children}
</div>
</AccordionContent>
</AccordionItem>
);
}Sources: packages/base-ui/src/components/accordion.tsx:56-79
function CopyButton({ id }: { id: string }) {
const t = useTranslations({ note: 'accordion' });
const [checked, onClick] = useCopyButton(() => {
const url = new URL(window.location.href);
url.hash = id;
return navigator.clipboard.writeText(url.toString());
});
return (
<button
type="button"
aria-label={t('Copy Link', { note: 'aria-label' })}
className={cn(
buttonVariants({
color: 'ghost',
className: 'text-fd-muted-foreground me-2',
}),
)}
onClick={onClick}
>
{checked ? <Check className="size-3.5" /> : <LinkIcon className="size-3.5" />}
</button>
);
}Sources: packages/base-ui/src/components/accordion.tsx:81-105
Caution
AccordionContent specifies hiddenUntilFound to allow browser find-in-page features to automatically reveal text content hidden inside collapsed accordion panels.
Sources: packages/base-ui/src/components/accordion.tsx:72
Tabs Containers and Active State Sync
State Synchronization and Simple Mode
The Tabs component supports both advanced Radix-style usage and simple mode via the items property. In simple mode, string identifiers passed to items are transformed into normalized values using the escapeValue helper function, which converts strings to lowercase and replaces whitespace characters with hyphens.
function escapeValue(v: string): string {
return v.toLowerCase().replace(/\s/, '-');
}Sources: packages/base-ui/src/components/tabs.tsx:202-204
The Tabs root component initializes its state using defaultIndex or defaultValue, validating any value updates against the item collection when simple mode items are defined.
export function Tabs({
ref,
className,
items,
label,
defaultIndex = 0,
defaultValue = items ? escapeValue(items[defaultIndex]) : undefined,
...props
}: TabsProps) {
const [value, setValue] = useState(defaultValue);
const collection = useMemo<CollectionKey[]>(() => [], []);
return (
<Unstyled.Tabs
ref={ref}
className={(s) =>
cn(
'flex flex-col overflow-hidden rounded-xl border bg-fd-secondary my-4',
typeof className === 'function' ? className(s) : className,
)
}
value={value}
onValueChange={(v: string) => {
if (items && !items.some((item) => escapeValue(item) === v)) return;
setValue(v);
}}
{...props}
>Sources: packages/base-ui/src/components/tabs.tsx:86-113
Warning
When items are provided, onValueChange explicitly blocks value updates if the incoming value does not match an escaped item string in the collection list.
Sources: packages/base-ui/src/components/tabs.tsx:108-111
Collection Index Tracking and Context
Child panels coordinate their active states through a React context that holds the items array and a mutable collection key array. The useCollectionIndex hook registers component render order to automatically assign indices for tab items lacking explicit value props.
const TabsContext = createContext<{
items?: string[];
collection: CollectionKey[];
} | null>(null);
function useTabContext() {
const ctx = useContext(TabsContext);
if (!ctx) throw new Error('You must wrap your component in <Tabs>');
return ctx;
}Sources: packages/base-ui/src/components/tabs.tsx:41-50
function useCollectionIndex() {
const key = useId();
const { collection } = useTabContext();
useEffect(() => {
return () => {
const idx = collection.indexOf(key);
if (idx !== -1) collection.splice(idx, 1);
};
}, [key, collection]);
if (!collection.includes(key)) collection.push(key);
return collection.indexOf(key);
}Sources: packages/base-ui/src/components/tabs.tsx:184-197
The Tab component resolves its target value by falling back to the collection index position within the items list if no explicit value prop is supplied.
export function Tab({ value, ...props }: TabProps) {
const { items } = useTabContext();
const resolved =
value ??
// eslint-disable-next-line react-hooks/rules-of-hooks -- `value` is not supposed to change
items?.at(useCollectionIndex());
if (!resolved)
throw new Error(
'Failed to resolve tab `value`, please pass a `value` prop to the Tab component.',
);
return (
<TabsContent value={escapeValue(resolved)} {...props}>
{props.children}
</TabsContent>
);
}Sources: packages/base-ui/src/components/tabs.tsx:138-154
Note
TabsContent forces keepMounted to maintain DOM nodes while inactive, and hides unselected panels using the data-[inactive]:hidden attribute selector.
Sources: packages/base-ui/src/components/tabs.tsx:156-176
Interactive Cards and Grid Layouts
Cards Grid Collection and Container Queries
The Cards wrapper component organizes card collections into a responsive two-column grid layout utilizing container query units. It renders a standard div element spreading all inbound HTMLAttributes<HTMLDivElement> props while applying utility classes for grid placement, column count, and container query context.
export function Cards(props: HTMLAttributes<HTMLDivElement>) {
return (
<div {...props} className={cn('grid grid-cols-2 gap-3 @container', props.className)}>
{props.children}
</div>
);
}Sources: packages/base-ui/src/components/card.tsx:5-11
Note
The @container class on Cards enables container query breakpoints so child cards can respond dynamically to the grid container width rather than the global viewport width.
Sources: packages/base-ui/src/components/card.tsx:7
Card Presentation and Anchor Link Integration
Individual Card components switch their underlying HTML tag dynamically based on the presence of an href property. When href is supplied, the card uses fumadocs-core/link (Link); otherwise, it defaults to a standard 'div' element.
export function Card({ icon, title, description, ...props }: CardProps) {
const E = props.href ? Link : 'div';
Sources: packages/base-ui/src/components/card.tsx:22-24
return (
<E
{...props}
data-card
className={cn(
'block rounded-xl border bg-fd-card p-4 text-fd-card-foreground transition-colors @max-lg:col-span-full',
props.href && 'hover:bg-fd-accent/80',
props.className,
)}
>Sources: packages/base-ui/src/components/card.tsx:25-34
| Property | Type | Description |
|---|---|---|
icon | ReactNode | Optional visual indicator rendered inside a styled container block above the title |
title | ReactNode | Primary card heading text rendered within an h3 element |
description | ReactNode | Optional secondary description text rendered in muted styling |
href | string | Target URL for anchor navigation, transforming the container into a link component |
external | boolean | Flag indicating whether the link targets an external destination |
Sources: packages/base-ui/src/components/card.tsx:13-20
Warning
Cards configured with an href automatically gain hover background styles (hover:bg-fd-accent/80) and span the full grid width when container queries drop below large viewports (@max-lg:col-span-full).
Sources: packages/base-ui/src/components/card.tsx:30-31
Table of Contents Integration Primitives
Anchor Providers and Scroll Observer Bindings
The Table of Contents implementation centers on TOCProvider, which wraps fumadocs-core/toc's AnchorProvider while concurrently exposing the parsed table of contents items array through a React context called TOCContext. Consumer components access these structured items via the useTOCItems hook, or consume active tracking primitives such as useActiveAnchor, useActiveAnchors, and useItems re-exported directly from the core primitive.
const TOCContext = createContext<Primitive.TOCItemType[]>([]);
export function useTOCItems(): Primitive.TOCItemType[] {
return use(TOCContext);
}
export type TOCProviderProps = Primitive.AnchorProviderProps;
export const { useActiveAnchor, useActiveAnchors, useItems } = Primitive;
export function TOCProvider({ toc, children, ...props }: TOCProviderProps) {
return (
<TOCContext value={toc}>
<Primitive.AnchorProvider toc={toc} {...props}>
{children}
</Primitive.AnchorProvider>
</TOCContext>
);
}Sources: packages/base-ui/src/components/toc/index.tsx:7-25
Scroll Areas and Element Ref Merging
The TOCScrollArea component manages scrollable viewport containers for sidebars by establishing a local viewRef bound to a styled div. It uses mergeRefs to combine this internal view reference with any inbound ref prop from parent consumers, ensuring that scroll observers and DOM bindings attach correctly without overwriting user-provided ref objects.
export function TOCScrollArea({ ref, className, ...props }: ComponentProps<'div'>) {
const viewRef = useRef<HTMLDivElement>(null);
return (
<div
ref={mergeRefs(viewRef, ref)}
className={cn(
'relative min-h-0 text-sm ms-px overflow-auto [scrollbar-width:none] mask-[linear-gradient(to_bottom,transparent,white_16px,white_calc(100%-16px),transparent)] py-3',
className,
)}
{...props}
>
<Primitive.ScrollProvider containerRef={viewRef}>{props.children}</Primitive.ScrollProvider>
</div>
);Sources: packages/base-ui/src/components/toc/index.tsx:27-41
| Function / Component | Return Type / Signature | Description |
|---|---|---|
useTOCItems | Primitive.TOCItemType[] | Consumes TOCContext to return the array of table of contents items |
TOCProvider | (props: TOCProviderProps) => ReactNode | Context provider wrapping AnchorProvider and TOCContext |
TOCScrollArea | (props: ComponentProps<'div'>) => ReactNode | Scrollable container linking viewRef to Primitive.ScrollProvider via mergeRefs |
Sources: packages/base-ui/src/components/toc/index.tsx:9-41
Note
TOCScrollArea hides native scrollbars using [scrollbar-width:none] and applies a vertical linear gradient mask to fade out overflowing heading items smoothly at the top and bottom edges.
Sources: packages/base-ui/src/components/toc/index.tsx:34
Related
On This Page
OverviewPackage Architecture and Registry DiscoveryRegistry Mapping and Package ConfigurationComponent Registries and Layout DefinitionsSearch Dialog Component PrimitivesList Virtualization and Keyboard NavigationCollapsible Accordions and Trigger ControlsAccordion Structure and State SynchronizationTriggers, Content Panels, and Copy ActionsTabs Containers and Active State SyncState Synchronization and Simple ModeCollection Index Tracking and ContextInteractive Cards and Grid LayoutsCards Grid Collection and Container QueriesCard Presentation and Anchor Link IntegrationTable of Contents Integration PrimitivesAnchor Providers and Scroll Observer BindingsScroll Areas and Element Ref MergingRelated