Vize

Prop, Emit, and Expose Forwarding

Headless Prop, Emit, and Expose Forwarding; covers Reka UI useForwardProps, Reka UI useEmitAsProps, Reka UI useForwardPropsEmits.

Package @vizejs/ui/forwarding
Maturity stable
Own the source vize lib pull forwarding
Requires —
Aliases forward props, forward emits, forward expose, wrapper component helpers
Covers Reka UI useForwardProps, Reka UI useEmitAsProps, Reka UI useForwardPropsEmits, Reka UI useForwardExpose

API

toHandlerKey

Convert an event name to the handler prop key Vue uses.

function toHandlerKey<const Name extends string>(name: Name): EmitHandlerKey<Name>

useEmitAsProps

Turn selected events of the current component into onX props that re-emit them, for forwarding to a wrapped child. Instance-free: the event list is explicit (no getCurrentInstance()), and both names and payloads are inferred from the emit returned by defineEmits, so forwarding a misspelled event is a compile error.

function useEmitAsProps< Emit extends (...args: never[]) => unknown, const Names extends readonly EmitEventName<Emit>[], >(emit: Emit, events: Names): EmitsAsProps<Emit, Names[number]>

useForwardProps

Forward a wrapper's props to a child, dropping undefined values so the child's own defaults still apply. Note: Vue casts absent boolean props to false; declare such props with default: undefined on the wrapper to keep them forwardable as "unset".

function useForwardProps<const Props extends object>( props: Props, ): ComputedRef<ForwardedProps<Props>>

useForwardPropsEmits

useForwardProps and useEmitAsProps combined into one object to v-bind on the wrapped child.

function useForwardPropsEmits< const Props extends object, Emit extends (...args: never[]) => unknown, const Names extends readonly EmitEventName<Emit>[], >( props: Props, emit: Emit, events: Names, ): ComputedRef<ForwardedProps<Props> & EmitsAsProps<Emit, Names[number]>>

useForwardExpose

Re-expose a wrapped child's public API (and root element) from a wrapper. ts const { forwardRef, exposed } = useForwardExpose<{ focus: () => void }>(); defineExpose(exposed); Instance-free: the child is captured through a function ref and read lazily, so the exposed object always reflects the currently mounted child. Methods of a wrapped element are bound to it. Properties are undefined while nothing is mounted (always during SSR).

function useForwardExpose<Exposed extends object = {}>(): ForwardExposeControls<Exposed>

Behavior

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

# State Input Outcome Proven by
FW1 event name toHandlerKey(name) Vue's handler key (onUpdate:modelValue, onValueChange) converts event names to Vue handler keys
FW2 selected events useEmitAsProps(emit, names) frozen onX props re-emit with the original payload re-emits selected events with their payloads
FW3 reactive props useForwardProps(props) only defined props are forwarded, reactively, so child defaults survive forwards only defined props so child defaults survive
FW4 wrapper component click, then call exposed method props and emits reach the child and parent; the child's exposed API is reachable wrappers forward props, emits, and the child's exposed API in the DOM
FW5 forwarded element before and after forwardRef(el) properties are undefined before mount; $el and bound element methods after forwarded expose binds element methods and is empty before mount
FW6 server render render twice, hydrate, click byte-identical markup, no hydration diagnostics, interactive after hydration renders identical wrapper markup on the server and hydrates cleanly
FW7 public type API defineEmits emit, props, expose event names/payloads inferred from emit; undeclared events and wrong payloads fail src/families/foundations/forwarding/forwarding.types.test-d.ts

All helpers are instance-free (no getCurrentInstance()), so they work in Vapor components and plain effect scopes.