Input

A text field, and the box that can hold content beside, above and below it.

Open Source

Installation

Input ships with the library — install it once, then import:

import { Input } from "@delacour/react-native-ui/input";

Or copy the source into your project, to own and edit it:

bunx delacour@latest add input

Usage

app/index.tsx
import { Input } from "@/components/ui/input";
import { useState } from "react";
import { View } from "react-native";

export default function Example() {
  const [email, setEmail] = useState("");

  return (
    <View className="flex-1 justify-center p-4">
      <Input value={email} onChangeText={setEmail} placeholder="you@example.com" />
    </View>
  );
}

Variants

Use the variant prop to change how the box is painted.

At rest
<Input variant="primary" />
<Input variant="secondary" />

Sizes

One axis drives the box height, the value's type scale and a decorator's icon step.

Sizes
Sizes
<Input size="sm" />
<Input size="md" />
<Input size="lg" />

Groups

Use Input.Group to put a prefix or a suffix inside the field's own border.

Icons
Icons
Text affixes
Text affixes
Controls
<Input.Group>
  <Input.Group.Prefix>$</Input.Group.Prefix>
  <Input value={amount} onChangeText={setAmount} keyboardType="decimal-pad" />
  <Input.Group.Suffix>NZD</Input.Group.Suffix>
</Input.Group>

variant, size, isInvalid and isDisabled live on the group, and an Input inside one reads them from context. Pressing anywhere on the group focuses the field; a Button inside a decorator still receives its own press.

Decorative and interactive decorators

A decorator holding only bare text and Icons is decorative: screen readers skip it and a tap on it falls through to the group, which focuses the field. Anything else — a Button, a custom view — stays reachable. Pass isDecorative to override either way. When the affix carries meaning, say it in the field's accessibilityLabel.

Decorative
Decorative
<Input.Group>
  <Input accessibilityLabel="Discount, percent" inputMode="decimal" />
  <Input.Group.Suffix>
    <Icon icon={IconPercent} />
  </Input.Group.Suffix>
</Input.Group>

Group buttons

Input.Group.Button is a Button sized one step below the box, squared when it holds only an icon, and disabled with the group without fading twice. Its press is its own. Name an icon-only one with accessibilityLabel; development builds warn when it is missing.

Group buttons
<Input.Group>
  <Input value={query} onChangeText={setQuery} placeholder="Search" />
  <Input.Group.Button accessibilityLabel="Clear" onPress={() => setQuery("")}>
    <Icon icon={IconCrossSmall} />
  </Input.Group.Button>
</Input.Group>

Separators

Input.Group.Separator draws a hairline. Its direction comes from where it is written: in the row it is vertical, between a block and the row it is horizontal and meets the border on both sides.

Separator
Separator
<Input.Group size="lg">
  <Input.Group.Prefix>$</Input.Group.Prefix>
  <Input.Group.Separator />
  <Input inputMode="decimal" placeholder="0.00" />
</Input.Group>

Blocks and Textarea

Input.Group.Block adds a full-width section above (position="start") or below (position="end") the field, inside the same border and focus ring. A Textarea can take the field's place, which makes a message composer one box. A block must be a direct child of the group.

Composer
<Input.Group>
  <Textarea autoGrow rows={2} placeholder="Write a message" />
  <Input.Group.Separator />
  <Input.Group.Block position="end">
    <Input.Group.Button variant="secondary" onPress={send}>Send</Input.Group.Button>
  </Input.Group.Block>
</Input.Group>

Disabling or invalidating the group reaches every part at once: the box fades once, decorators and block text turn destructive, and every Input.Group.Button goes inert.

States

The two state axes resolve Input.Group → the Input's own prop → the enclosing Field, and both are ?? chains, so an explicit false is a value rather than an absence.

Invalid
Invalid
Live validation
<Input isInvalid />
<Input isDisabled />

Placeholder, caret and selection colours

These take a colour value, not a style, so they are named as classes and resolved rather than applied.

<Input
  placeholderColorClassName="accent-muted-foreground"
  selectionColorClassName="accent-info"
/>

They must be accent-* utilities

accent-muted-foreground, never text-muted-foreground. Uniwind reads only accentColor off the compiled class, so anything else resolves to nothing: it warns once in development and leaves the prop undefined, which renders as the platform default rather than as an error.

API Reference

Input

Extends React Native's TextInputProps. placeholderTextColorClassName is withheld in favour of placeholderColorClassName, which is the name the rest of the library uses.

Prop

Type

Input.Group

Puts a prefix or a suffix inside the input's own border, and publishes the shared axes so the input inside does not restate them.

Prop

Type

Input.Group.Prefix / Input.Group.Suffix

Content drawn inside the group's border, before or after the field. An Icon inside one inherits the input's icon size and a muted foreground.

Prop

Type

Input.Group.Button

Takes every Button prop except size and isDisabled, which come from the group.

Prop

Type

Input.Group.Separator

Prop

Type

Input.Group.Block

Prop

Type

On this page