TextInput example
text-input is controlled through a value prop and a text-change event. The
controlled write handshake is shared; the public event name is framework-shaped.
import './NameInput.css';import { useState } from 'react';
export function NameInput() { const [name, setName] = useState('Symbiote');
return ( <view className="root"> <text-input value={name} onValueChange={event => setName(event.text)} className="input" /> <text>Hello, {name}</text> </view> );}.root { gap: 12px; }.input { min-width: 220px; padding: 12px; border-width: 1px; }<script setup lang="ts">import { ref } from 'vue';
const name = ref('Symbiote');</script>
<template> <view class="root"> <text-input v-model="name" class="input" /> <text>Hello, {{ name }}</text> </view></template>
<style scoped>.root { gap: 12px; }.input { min-width: 220px; padding: 12px; border-width: 1px; }</style>v-model="name" is sugar over the same :value/@value-change pair — write
that explicit form instead if you also need the raw native event:
@value-change="event => setName(event.text)". The event handed to
@value-change IS the native change event, with text merged onto it —
there is no separate second argument.
import { Component } from '@angular/core';import { SYMBIOTE_ELEMENTS } from '@symbiote-native/angular';import './name-input.css';
@Component({ standalone: true, imports: [SYMBIOTE_ELEMENTS], template: ` <view class="root"> <text-input [(value)]="name" class="input" /> <text>Hello, {{ name }}</text> </view> `,})export class NameInput { name = 'Symbiote';}.root { gap: 12px; }.input { min-width: 220px; padding: 12px; border-width: 1px; }[(value)]="name" is Angular’s banana-in-a-box two-way binding, sugar over
the same [value]/(valueChange) pair — write that explicit form instead
([value]="name" (valueChange)="setName($event)") if you also need the raw
native event, which arrives on the separate (change) output: an
EventEmitter only carries one value, so (valueChange) stays text-only to
keep [(value)] working, and (change) exists purely for the raw event.
<script lang="ts"> let name = $state('Symbiote');</script>
<view class="root"> <text-input value={name} onValueChange={event => (name = event.text)} class="input" ></text-input> <text>Hello, {name}</text></view>
<style> .root { gap: 12px; } .input { min-width: 220px; padding: 12px; border-width: 1px; }</style>Svelte’s own element bind:value is explicitly unsupported by this
adapter’s DOM shim — there is no sugar over value/onValueChange here,
same as React. Unlike a plain onPress, onValueChange is safe to write
as an individual attribute directly: it is always called with a single
change-event object, so it never trips the crash an individual callback
prop can otherwise hit on a bare host tag.
import './NameInput.css';import { createSignal } from 'solid-js';
export function NameInput() { const [name, setName] = createSignal('Symbiote');
return ( <view class="root"> <text-input value={name()} onValueChange={event => setName(event.text)} class="input" /> <text>Hello, {name()}</text> </view> );}.root { gap: 12px; }.input { min-width: 220px; padding: 12px; border-width: 1px; }No v-model-style sugar here either: value/onValueChange is the
whole contract, same as React and Svelte.
API takeaway
Section titled “API takeaway”| Concern | React | Vue | Angular | Svelte | Solid |
|---|---|---|---|---|---|
| Text prop | value |
:valueor v-model |
[value]or [(value)] |
value |
value |
| Text change | onValueChange={event => …} |
@value-changeor v-model |
(valueChange)="handler($event)" (text only) |
onValueChange={event => …} |
onValueChange={event => …} |
| Native event | (same event object) | (same emit, same event object) | (change)="handler($event)" (separate output) |
(same event object) | (same event object) |
All five now share one contract: onValueChange (or its per-adapter emit/output
spelling) is called with a single change-event object, event.text carries
the current text, and event itself is the native change event — there is no
separate second argument anywhere. Angular is the one exception, since its
(valueChange) output is deliberately text-only to keep [(value)] working,
with (change) carrying the full event instead.
StyleSheet.create works identically to a CSS class if you’d rather keep
style objects inline — see the Styling guide.