# Text field

> A text field whose label glides up as you focus it, with helper and error text that slide in, a shake and haptic on error, clear and reveal buttons, a character counter and a multiline mode that grows with its text.

- Type: Component (inputs)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/text-field
- Markdown: https://goodui.dev/native/components/text-field.md
- Registry item: https://goodui.dev/native/r/text-field.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native text input, floating label input, expo text field, material text field, password input show hide, input with error message, character counter input, auto grow textarea, form field

## When to use

- Form fields: name, email, address, card holder, notes.
- Passwords and secrets (secureTextEntry adds the reveal toggle).
- Free text with a limit, such as a bio or a review (showCount with maxLength).
- Notes and messages that should grow while typing (multiline with minRows and maxRows).

## When not to use

- Search: use search-bar, which has Cancel, scopes and debouncing.
- Verification codes: use otp-input.
- Numbers changed by small steps (guests, quantity): use quantity-stepper.
- Picking from a fixed list: use segmented-control or a radio group from checkbox.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/text-field.json
npx expo install --fix
```

The component lands in `components/goodui-native/text-field/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { useState } from "react";
import { TextField } from "@/components/goodui-native/text-field/text-field";

export function EmailField() {
  const [email, setEmail] = useState("");
  const [error, setError] = useState<string>();
  return (
    <TextField
      label="Email"
      placeholder="name@example.com"
      value={email}
      onChangeText={(t) => {
        setEmail(t);
        setError(undefined);
      }}
      onBlur={() => setError(email.includes("@") ? undefined : "Enter a valid email address")}
      error={error}
      helperText="We'll send the receipt here"
      keyboardType="email-address"
      autoComplete="email"
      autoCapitalize="none"
    />
  );
}
```

## Examples

### Password with reveal toggle

```tsx
<TextField label="Password" secureTextEntry textContentType="password" autoComplete="password" />
```

### Growing note with a limit

```tsx
<TextField label="Note for the driver" multiline minRows={2} maxRows={6} maxLength={200} showCount />
```

### Amount with a unit and an icon

```tsx
<TextField label="Amount" keyboardType="decimal-pad" leading={<CardIcon />} trailing={<Text tone="secondary">EUR</Text>} />
```

### Focus the next field

```tsx
const next = useRef<ComponentRef<typeof TextInput>>(null);

<TextField label="First name" returnKeyType="next" onSubmitEditing={() => next.current?.focus()} />
<TextField label="Last name" inputRef={next} />
```

## API reference

### TextField

`import { TextField } from "@/components/goodui-native/text-field/text-field";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` | – | Floating label. It rests inside the field like a placeholder and glides up to the top edge when the field is focused or has text. Sentence case. |
| `value` | `string` | – | Text value (controlled). Pair it with `onChangeText`. Omit it to let the field keep its own text. |
| `defaultValue` | `string` | `""` | Initial text when uncontrolled. |
| `onChangeText` | `(text: string) => void` | – | Called on every change, including the clear button (with ""). |
| `placeholder` | `string` | – | Example input such as "name@example.com". With a label it shows only once the label has floated and the field is still empty; without a label it is the placeholder. |
| `helperText` | `string` | – | Supporting text under the field. An error message replaces it while present. |
| `error` | `string \| boolean` | – | Error state. A string is shown under the field in place of the helper text. When an error appears (or changes) the field shakes once, plays the error haptic and is announced. `true` marks the field red without a message. |
| `leading` | `ReactNode` | – | Icon before the text, about 20 pt. Color it with `textSecondary`. |
| `trailing` | `ReactNode` | – | Icon or short text after the input, for example a unit ("kg") or a badge. |
| `clearable` | `boolean` | – | Shows a clear button while the field is focused and has text. Default `true` for single-line fields that are not secure. |
| `secureTextEntry` | `boolean` | `false` | Hides the text and adds an eye button that reveals it. |
| `maxLength` | `number` | – | Maximum number of characters. |
| `showCount` | `boolean` | `false` | Shows a "12/80" counter under the field. Needs `maxLength`. Reaching the limit plays a warning haptic. |
| `multiline` | `boolean` | `false` | Multiline field that grows with its text between `minRows` and `maxRows`, then scrolls. |
| `minRows` | `number` | `3` | Rows a multiline field starts with. Default 3. |
| `maxRows` | `number` | `8` | Rows a multiline field grows to before it scrolls. Default 8. |
| `variant` | `"filled" \| "outline"` | `"filled"` | `filled` is a quiet muted surface; `outline` draws a hairline border for dense forms on a plain surface. |
| `size` | `"sm" \| "md"` | `"md"` | Heights 48 (`sm`) and 56 pt (`md`). |
| `disabled` | `boolean` | `false` | Dims the field and makes it read-only. |
| `haptic` | `boolean` | `true` | `false` silences this field's haptics (error, limit reached, reveal toggle, clear). |
| `onFocus` | `() => void` | – | Called when the input gains focus. |
| `onBlur` | `() => void` | – | Called when the input loses focus. |
| `inputRef` | `Ref<ComponentRef<typeof TextInput>>` | – | Ref to the underlying TextInput, for example to focus the next field from `onSubmitEditing`. |
| `accessibilityLabel` | `string` | – | Screen reader label. Defaults to `label`, then `placeholder`. |
| `accessibilityHint` | `string` | – | Screen reader hint. The error or helper text is used when omitted. |
| `labels` | `Partial<TextFieldLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style of the outer wrapper (field plus message line). |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The TextInput is the accessible element. Its label is accessibilityLabel, then label, then placeholder; the floating label itself is hidden from screen readers.
- The hint is accessibilityHint, else the error or helper text, so the reason for an error is read with the field.
- A new error message is announced with AccessibilityInfo.announceForAccessibility.
- Clear is a button labelled 'Clear text' and is hidden from screen readers while invisible. The reveal toggle is a button labelled 'Show password' or 'Hide password'.
- Icon buttons have a 12 pt (clear) or 10 pt (reveal) hitSlop so they reach a 44 pt target.
- On the web a secure field autofills as current-password; Android's 'password' and 'password-new' map to current-password and new-password there.

## Motion

- Label: rests at body size inside the field and glides up and shrinks to caption size on the morph spring when focused or filled; its color follows focus (accent) and error (danger).
- Focus draws a 2 pt accent ring; an error turns the ring and label red and tints the filled surface.
- An error that appears or changes shakes the field once.
- Helper and error text slide down into place; the message line's height springs, so content below glides instead of jumping.
- The clear button scales in and its slot opens on the snappy spring; the reveal toggle's slash draws across or retracts.
- Multiline: the field's height springs to fit its text between minRows and maxRows, then scrolls.
- Reduced motion: the label moves with a short fade-speed timing instead of a spring, no shake, no scaling, heights change instantly; colors still change.

## Haptics

- error when an error appears or changes (never on mount)
- warning once when the text reaches maxLength
- select on the reveal toggle
- tap on clear

## Performance

- Label, ring, colors, shake and heights animate on the UI thread through Reanimated shared values.
- Typing re-renders the field once per keystroke like any controlled TextInput; nothing animates per keystroke except the multiline height when the line count changes.

## Notes for AI

- Use label for the field name and placeholder for an example value. Do not pass both as the same text.
- Pass error as a string to show a message, or true for a red state without one; set it back to undefined to clear it. The field shakes and buzzes by itself, so do not add your own shake or haptic.
- It works controlled (value + onChangeText) or uncontrolled (defaultValue).
- Use inputRef, not ref, to reach the TextInput (for focus() from returnKeyType='next').
- Inside a ScrollView set keyboardShouldPersistTaps='handled' so the clear and reveal buttons work while the keyboard is up.
- clearable defaults to true for single-line, non-secure fields; secureTextEntry fields get the reveal toggle instead.

## Related

- [Search bar](https://goodui.dev/native/components/search-bar.md): An iOS-style search field: focusing it shrinks the field while a Cancel button springs in beside it and scope chips cascade in below. Clear button, loading spinner and debounced queries built in.
- [OTP input](https://goodui.dev/native/components/otp-input.md): A verification code field where digits pop in, a pending check breathes, success ripples across the cells and a wrong code shakes and clears.
- [Quantity stepper](https://goodui.dev/native/components/quantity-stepper.md): A − value + stepper whose value rolls in the direction of change. Hold a button to repeat with acceleration; the ends push back with a rubber-band bounce and a warning haptic. In a cart the − turns into a trash button at the minimum.
- [Button](https://goodui.dev/native/components/button.md): A press-responsive button with haptics that runs async actions on its own: it gathers into a spinner, then opens into a success or error state.

## Guidance for AI tools

Text field: A text field whose label glides up as you focus it, with helper and error text that slide in, a shake and haptic on error, clear and reveal buttons, a character counter and a multiline mode that grows with its text. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
