Basic
Uncontrolled — clears itself on submit.
<PromptInput onSubmit={(text) => sendMessage(text)} />The AI composer. Auto-grows with content, holds attachment chips, and morphs its send button into a stop with a spinning ring while generating.
Scan with Expo Go to run it live
Install Expo Goon Android or iPhone, scan, and the entire appCN gallery loads — real native motion & gestures, not a web shim.
https://expo.dev/preview/update?message=v1.0.0+initial&updateRuntimeVersion=1.0.0&createdAt=2026-05-28T20%3A07%3A21.247Z&slug=exp&projectId=a2d02caa-be26-436a-acd6-f3007862ba0a&group=3ca7e750-9506-4146-8394-1a16c3a917a8npx @app-cn/cli@latest add prompt-inputRecommended. Configures NativeWind + Reanimated and registers @app-cn on first run.
A rounded card containing an optional row of attachment chips, an auto-growing multiline TextInput, and a circular send button. The send button is the affordance: it animates between three states (disabled, ready, generating) and is the canonical control surface for AI chat composers.
The send glyph cross-fades into a stop square the moment generation starts, with a 2px progress ring spinning at 900ms/turn wrapped around it — one element doing the work of three.
| Name | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled text value. Omit to use uncontrolled mode. |
defaultValue | string | — | Initial text in uncontrolled mode. |
onChangeText | (text: string) => void | — | Fires on every keystroke. |
onSubmit | (text: string) => void | — | Called with the trimmed message when the send button is tapped. |
onStop | () => void | — | Called when the user taps the stop button while `generating` is true. |
generating | boolean | false | When true, the send button morphs into a stop with a spinning ring. |
placeholder | string | "Message appCN…" | TextInput placeholder. |
minHeight | number | 24 | Minimum input height in px before auto-grow kicks in. |
maxHeight | number | 140 | Maximum height the input will grow to before scrolling. |
attachments | PromptAttachment[] | — | Chips rendered above the input. Each is `{ id, label }`. |
onAddAttachment | () => void | — | When provided, a leading "+" button appears that calls this on tap. |
onRemoveAttachment | (id: string) => void | — | When provided, each chip renders a small "×" that calls this with its id. |
disabled | boolean | false | Greys out the whole composer and disables all controls. |
className | string | — | Extra NativeWind classes merged onto the outer container. |
Uncontrolled — clears itself on submit.
<PromptInput onSubmit={(text) => sendMessage(text)} />Flip `generating` to morph send into stop. `onStop` cancels the request.
const [generating, setGenerating] = React.useState(false);
return (
<PromptInput
generating={generating}
onSubmit={async (text) => {
setGenerating(true);
await fetchReply(text);
setGenerating(false);
}}
onStop={() => cancelInFlight()}
/>
);A "+" button appears when `onAddAttachment` is provided; chips animate in and out.
const [files, setFiles] = React.useState<PromptAttachment[]>([]);
return (
<PromptInput
attachments={files}
onAddAttachment={() => pickFile().then((f) => setFiles((a) => [...a, f]))}
onRemoveAttachment={(id) => setFiles((a) => a.filter((f) => f.id !== id))}
onSubmit={(text) => sendMessage(text, files)}
/>
);