Capítulo 7 de 51
Base UI's universal composition mechanism is the render prop (not asChild — the naming used by Radix and older libraries): pass a React element to replace the rendered output, nest it across multiple components as deep as needed, or pass a function (props, state) => element for full control and state-driven rendering.
render with an element: <Menu.Trigger render={<MyButton size="md" />}>Open menu</Menu.Trigger> — the custom component must forward ref and spread all received props onto its underlying DOM node (same contract as Radix's asChild, different prop name).render props nest arbitrarily deep — the documented pattern for composing Tooltip.Trigger + Dialog.Trigger + Menu.Trigger + a custom button all onto one element is chaining render={<Dialog.Trigger render={<Menu.Trigger render={<MyButton />} />} />}.render prop changes what HTML tag a part outputs — e.g. <Menu.Item render={<a href="..." />}> turns a <div>-rendering item into an anchor. Each part renders the most semantically appropriate element by default; override case-by-case, not globally.render={(props, state) => <span {...props}>{state.checked ? <A/> : <B/>}</span>} — for performance-sensitive cases, gives full control over prop spreading and lets output vary by the component's live state (e.g. Switch.Thumb swapping an icon based on state.checked)./* Element form — swap the rendered component */
<Menu.Trigger render={<MyButton size="md" />}>Open menu</Menu.Trigger>
/* Nested composition across three components + a custom button */
<Dialog.Root>
<Tooltip.Root>
<Tooltip.Trigger render={
<Dialog.Trigger render={
<Menu.Trigger render={<MyButton size="md" />}>Open menu</Menu.Trigger>
} />
} />
<Tooltip.Portal>...</Tooltip.Portal>
</Tooltip.Root>
<Dialog.Portal>...</Dialog.Portal>
</Dialog.Root>
/* Function form — state-driven rendering */
<Switch.Thumb render={(props, state) => (
<span {...props}>{state.checked ? <CheckedIcon /> : <UncheckedIcon />}</span>
)} />
render unifies three separate concerns (swap component, change tag, react to state) behind one prop, rather than three separate APIs.render — remember the target component must forward ref and spread props, or interactions silently break.render only when you need to branch output on the component's own state or you're in a proven performance-sensitive path — the element form covers the common case with less code.render replaces asChild conceptually, but the syntax differs — render={<X/>} instead of asChild + a child <X/>.className/style function forms follow the same (state) => ... pattern as render's function form.render prop contract — read it before building a custom component meant to accept render itself.ch005-composition.md: the equivalent Radix concept (asChild), for side-by-side comparison.