Vize

Slot and Prop Merging Utilities

Headless Slot and Prop Merging Utilities; covers React Aria mergeProps, Vue mergeProps, Reka UI renderSlotFragments.

Package @vizejs/ui/slot-utils
Maturity stable
Own the source vize lib pull slot-utils
Requires —
Aliases merge props, slot presence, has slot content
Covers React Aria mergeProps, Vue mergeProps, Reka UI renderSlotFragments

API

mergeProps

Merge prop objects for forwarding onto one element or component. - class values are normalized and joined (left to right). - style values are normalized and merged (later declarations win). - Event handlers (onX) are chained: every handler runs, in source order. - Other keys take the last value that is not undefined, so optional forwarded props never erase a defined value. Pure and SSR-safe.

function mergeProps<const Sources extends readonly object[]>( ...sources: Sources ): MergedProps<Sources>

isHandlerKey

Whether a prop key names an event handler (on followed by an upper-case letter or :-namespaced event).

function isHandlerKey(key: string): key is HandlerKey

hasSlotContent

Whether a slot renders meaningful content. Invokes the slot and ignores comments (from v-if), whitespace-only text, and empty fragments, recursing into fragments. Use it to skip wrapper elements for empty slots. Calling a slot during render is safe on the server; call it from render (or a computed read during render) so the component tracks the slot's dependencies.

function hasSlotContent<Props>(slot: Slot<Props> | undefined, props?: Props): boolean

presentSlotNames

Names of the slots that were provided and render content.

function presentSlotNames<Name extends string>( slots: Readonly<Partial<Record<Name, Slot | undefined>>>, ): Name[]

Behavior

Normative state x input -> outcome table for @vizejs/ui/slot-utils. Every row is proven by src/families/foundations/slot-utils/slot-utils.test.ts; compile-only assertions live in src/families/foundations/slot-utils/slot-utils.types.test-d.ts.

# State Input Outcome Proven by
SU1 several prop sources mergeProps(...) classes joined, styles merged (later wins), handlers chained in order, last defined value wins merges class, style, handlers, and last defined values
SU2 undefined / one handler mergeProps(...) an explicit undefined is kept only without an earlier value; a single handler passes through as-is keeps an explicit undefined when no earlier value exists and passes single handlers through
SU3 prop key isHandlerKey(key) onX and onX:y keys are handlers; once/on are not recognizes handler keys
SU4 slot output hasSlotContent(slot) comments, whitespace, and empty fragments count as empty; nested content counts detects slot content through comments, whitespace, and fragments
SU5 mounted component empty named slot wrappers for empty slots are skipped and presentSlotNames lists only non-empty slots skips wrappers for empty slots in the mounted DOM
SU6 server render render twice, hydrate byte-identical markup and no hydration diagnostics renders identical slot-aware markup on the server, hydrates server markup without mismatch diagnostics
SU7 public type API merged props, slot props handler unions, class: string, style: StyleValue, typed slot names and slot props src/families/foundations/slot-utils/slot-utils.types.test-d.ts

Everything is pure; slot checks invoke the slot during render so the calling component tracks its dependencies.