Events
Native Fabric events come back to the engine. Each adapter then exposes them in
that framework’s idiom: React callback props, Vue typed emits (plus v-model
on controlled components), Angular real @Output() EventEmitters, and
Svelte plain callback props — the DOM-shim adapter passes the same event
object straight through, with no framework-specific wrapper or emit
translation — with one permanent exception, the scroll-family events, which
stay callback inputs (see the caveat below).
Same event, different framework shape
Section titled “Same event, different framework shape”<Switch value={enabled} onValueChange={setEnabled} /><TextInput value={name} onValueChange={setName} /><Pressable onPress={() => setCount(count + 1)} />Vue uses typed emits, or v-model on the controlled components that support it:
<Switch v-model="enabled" /><TextInput v-model="name" /><Pressable @press="count++" />Angular uses real @Output() EventEmitters everywhere, including Switch and
TextInput:
template: ` <Switch [value]="enabled" (valueChange)="setEnabled($event)" /> <TextInput [value]="name" (valueChange)="setName($event)" /> <Pressable (press)="count = count + 1" />`Svelte receives the same native event object shape as React — props pass through as-is, no framework-specific event wrapper:
<Switch value={enabled} onValueChange={next => (enabled = next)} /><TextInput value={name} onValueChange={next => (name = next)} /><Pressable onPress={() => (count += 1)} />The payload is the same concept. The public API is framework-shaped.
Passthrough events vs adapter events
Section titled “Passthrough events vs adapter events”Some events are native passthrough events. They travel through the engine as
onX listeners and should not be declared as Vue emits unless the Vue component
actually consumes and re-emits them.
Other events are adapter-level conveniences. Examples:
| Concept | React | Vue | Angular | Svelte |
|---|---|---|---|---|
| Press | onPress |
@press |
(press) |
onPress |
| Switch value | onValueChange(value, event) |
@value-changeor v-model |
(valueChange) (value only) |
onValueChange(value, event) |
| Text input text | onValueChange(text, event) |
@value-changeor v-model |
(valueChange) (text only) |
onValueChange(text, event) |
| Text input raw event | (same callback, 2nd arg) | (same emit, 2nd arg) | (change) (separate output) |
(same callback, 2nd arg) |
React, Vue, and Svelte merged TextInput’s old onChangeText/onChange pair
into one onValueChange(text, event) callback — there’s only ever one
underlying native event. Angular keeps two separate @Output()s instead,
because an EventEmitter carries exactly one value: valueChange stays
text-only (so [(value)] two-way binding keeps working) and change is a
second output for the raw event. Switch follows the same shape one level
simpler: React’s onValueChange, Vue’s @value-change, and Svelte’s
onValueChange all receive the native event as a second argument, but
Angular’s valueChange stays single-value (just the boolean) since there’s
no second output to split it into.
This split matters because Vue removes declared emit listeners from attrs. If a native passthrough listener is declared as an emit by mistake, the engine may never see it.
Controlled components
Section titled “Controlled components”Switch and TextInput follow React Native’s controlled-component rules. If the
native view reports a new value but the parent does not update the value prop,
SymbioteNative commands native back to the JavaScript value.
That correction logic is shared in @symbiote-native/components; React, Vue,
Angular, and Svelte each only wire it to their own lifecycle systems.