Typed Polymorphic Rendering
Headless Typed Polymorphic Rendering; covers Radix Polymorphic, Ark UI polymorphic factory, Reka UI Primitive as.
| Package | @vizejs/ui/polymorphic |
| Maturity | stable |
| Own the source | vize lib pull polymorphic |
| Requires | — |
| Aliases | as prop, polymorphic component, element type inference |
| Covers | Radix Polymorphic, Ark UI polymorphic factory, Reka UI Primitive as |
API
isPolymorphicTag
Whether as names a native element (a string) rather than a component.
function isPolymorphicTag(as: PolymorphicAs): as is PolymorphicTag
resolvePolymorphicAttributes
Add the implicit attributes a native element needs to keep its semantics when rendered polymorphically. - button defaults to type="button" (never an accidental form submit). - a with a truthy disabled drops href, gains aria-disabled="true" and tabindex="-1", since anchors have no native disabled state. - Any other non-button tag with a truthy disabled gains aria-disabled="true". Explicit attributes always win. An empty tag name throws VIZE_UI_POLYMORPHIC_AS. Pure, so SSR output is deterministic.
function resolvePolymorphicAttributes<As extends PolymorphicAs>( as: As, attributes: PolymorphicAttributes<As>, ): PolymorphicAttributes<As>
renderPolymorphic
Render as with attributes type-checked against the chosen element or component (render-function counterpart of a polymorphic as prop). ts renderPolymorphic("a", { href: "/docs" }, () => "Docs"); renderPolymorphic("a", { hreff: "/docs" }); // compile error
function renderPolymorphic<As extends PolymorphicAs>( as: As, attributes: PolymorphicAttributes<As>, children?: string | VNodeArrayChildren | (() => VNodeChild), ): VNode
Behavior
Normative state x input -> outcome table for @vizejs/ui/polymorphic. Every row
is proven by src/families/foundations/polymorphic/polymorphic.test.ts;
compile-only assertions live in
src/families/foundations/polymorphic/polymorphic.types.test-d.ts.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| PM1 | tag or component | isPolymorphicTag(as) |
strings are tags; components are not | distinguishes native tags from components |
| PM2 | as="button" |
resolve attributes | type="button" is added unless a type is given |
buttons default to type=button while explicit types win |
| PM3 | disabled non-button | resolve attributes | anchors drop href, gain aria-disabled and tabindex=-1; others gain aria-disabled; empty tags throw VIZE_UI_POLYMORPHIC_AS |
disabled anchors and generic elements expose aria-disabled |
| PM4 | tags and components | renderPolymorphic(as, attrs, children) |
native children or a default slot render in the DOM | renders native tags and components with slot children in the DOM |
| PM5 | server render | render twice, then hydrate | byte-identical markup and no hydration diagnostics | renders identical markup on the server, hydrates server markup without mismatch diagnostics |
| PM6 | public type API | as plus attributes |
native attributes / component props are inferred from as; wrong attributes fail |
src/families/foundations/polymorphic/polymorphic.types.test-d.ts |
Primitive remains the SFC for template use; these helpers type the same as
contract for render functions and wrapper component props.