renderable Render
The resolver behind every primitive, exported so you can build your own in the same convention.
API
Signature
renderableRender(renderable: Renderable, children?: ReactNode): ReactNode;Examples
Usage
Every primitive in the library ends up here. Exposing it means a component of your own can accept a Renderable slot and behave exactly like the built-ins, instead of inventing a third convention for the same problem.
const Panel = ({ title, children }: { title: Renderable; children: ReactNode }) => (
<section>
<header>{renderableRender(title)}</header>
{children}
</section>
);Details
The three branches
A function is a component and gets called through createElement. An element with children to inject is cloned, its own children replaced. Anything else — an element with no children to inject, a string, a number, null — is returned untouched.
renderableRender(Panel); // <Panel />
renderableRender(<Panel tone="warn" />); // the element, untouched
renderableRender(Panel, children); // <Panel>{children}</Panel>
renderableRender(<Panel />, children); // cloned, its own children replaced
renderableRender("just text"); // returned as isThe test is typeof === "function", nothing more: a component is always a function and a ReactNode never is, so the two forms are told apart with certainty rather than with a heuristic. Note that the argument is children, plural in name only: it is a single node, and passing it is what distinguishes a wrapper from a plain slot.
Building your own primitive
Two rules keep a custom primitive consistent with the built-ins. Make a prop a Renderable when it is rendered conditionally, so the component form can defer its evaluation; keep it a plain ReactNode when it always passes through, since there is nothing to defer and a second form would only add a way to get it wrong.
And forward children only when your component genuinely wraps: calling renderableRender(slot, children) on a slot that is meant to stand alone will silently replace whatever that element already contained.
Forwarding children
Pass children as the second argument only for wrappers: the component form receives them as its children, and the element form has its own children replaced by them.