Components

TextField

TextField is a single-line text input with an optional label above it, a border that changes color while focused, and a clear button. It is built on the <Input> element.

ui-text-field.tsx Edit in Playground Open in new tab
// TextField: labels, placeholders, controlled and uncontrolled text, secure entry.
import { mount, signal, Text, View } from "nsh";
import { Button, TextField } from "nsh/ui/mobile";

function App() {
  const name = signal("Ada");
  const submitted = signal("");
  const locked = signal(false);
  return (
    <View style={{ flex: 1, backgroundColor: "#ffffff", padding: 16, paddingTop: 24, gap: 16 }}>
      <Text style={{ fontSize: 22, fontWeight: "700" }}>TextField</Text>
      <TextField
        label="Name (controlled)"
        placeholder="Your name"
        value={name()}
        onChange={(v) => name.set(v)}
        onSubmit={(v) => submitted.set(v)}
        disabled={locked()}
      />
      <Text style={{ color: "#737373" }}>{`Hello, ${name() || "nobody"}`}</Text>
      <TextField label="Password" placeholder="At least 8 characters" secure />
      <TextField label="Notes (no clear button)" defaultValue="Uncontrolled" clearable={false} />
      <Button
        variant="secondary"
        title={locked() ? "Enable the name" : "Disable the name"}
        onPress={() => locked.set(!locked())}
      />
      <Text style={{ color: "#737373" }}>{submitted() ? `Submitted: ${submitted()}` : "Press return to submit."}</Text>
    </View>
  );
}

mount(App);
import { TextField } from "nsh/ui/mobile";

#Usage

import { mount, signal, Text, View } from "nsh";
import { TextField } from "nsh/ui/mobile";

function App() {
  const name = signal("");
  return (
    <View style={{ flex: 1, padding: 16, paddingTop: 48, gap: 12 }}>
      <TextField label="Name" placeholder="Your name" value={name()} onChange={(v) => name.set(v)} />
      <Text>{`Hello, ${name() || "nobody"}`}</Text>
    </View>
  );
}

mount(App);

#Controlled and uncontrolled

  • Controlled: pass value and update it in onChange. The field shows value.
  • Uncontrolled: leave out value. The field keeps its own text, starting at defaultValue, and reports changes with onChange.
<TextField label="Notes" defaultValue="Draft" onChange={(text) => save(text)} />

#Variants

<TextField label="Password" placeholder="At least 8 characters" secure />
<TextField label="Code" clearable={false} />
<TextField label="Account" value="ada@example.com" disabled />
  • secure hides what is typed, for passwords.
  • clearable (on by default) shows a × button at the end while there is text. Pressing it clears the text, reports "" with onChange and keeps the input focused, so the user can type again.
  • disabled makes the field read-only, dims it to 50% opacity and hides the clear button.

#Submitting

onSubmit is called with the text when the user presses the keyboard’s return key:

<TextField
  placeholder="Search"
  value={query()}
  onChange={(v) => query.set(v)}
  onSubmit={(text) => search(text)}
/>

#Props

PropTypeDefaultDescription
labelstring | nullnoneShown above the input.
placeholderstring | nullnoneShown while the input is empty.
valuestring | nullnone (uncontrolled)The text, when the app controls it.
defaultValuestring""The first text, when the app does not control it.
securebooleanfalseHides what is typed (passwords).
clearablebooleantrueShows a clear button while there is text.
disabledbooleanfalseNot editable, 50% opacity.
class, classList, style, testID, onLayout, refApplied to the field’s root view (the column holding the label and the input box).

#Events

EventPayloadWhen
onChangestring: the new textThe user typed, deleted or pressed the clear button.
onSubmitstring: the textThe user pressed the return key.

#Theming

PartTheme key
Labelmuted.foreground, small text
Input boxbackground, corner radius radius, at least 44 pt tall
Borderinput.border; ring while focused
Textforeground
Placeholdermuted.foreground
Selectionprimary.background
Clear buttonmuted.background; secondary.active.background while pressed

See Theming.

#Accessibility

The input is a native text field, so screen readers announce it as editable text. The clear button has the button role and the label “Clear”, and its touch area extends 8 pt beyond its 24 pt circle. The label is a separate text above the input; it is not attached to the input as its accessibility label.