Saltar al contenido principal
NachUI

Prompt Input

El composer. Un textarea que crece, archivos adjuntos, una barra de herramientas y un botón de envío que conoce el estado del stream.

1'use client';
2
3import { useState } from 'react';
4import { PromptInput, type PromptInputMessage } from '@/components/ui/prompt-input';
5
6export function Default() {
7 const [sent, setSent] = useState<string | null>(null);
8
9 const handleSubmit = (message: PromptInputMessage) => {
10 setSent(message.text);
11 };
12
13 return (
14 <div className="flex w-full max-w-lg flex-col gap-3">
15 <PromptInput onSubmit={handleSubmit}>
16 <PromptInput.Body>
17 <PromptInput.Textarea placeholder="Ask about any component…" />
18 </PromptInput.Body>
19 <PromptInput.Footer>
20 <PromptInput.Tools>
21 <PromptInput.AddAttachments />
22 </PromptInput.Tools>
23 <PromptInput.Submit />
24 </PromptInput.Footer>
25 </PromptInput>
26 {sent && <p className="text-muted-foreground text-xs">Submitted: {sent}</p>}
27 </div>
28 );
29}

Instalación

pnpm dlx nachui add prompt-input

Anatomía

1import { PromptInput } from '@/components/ui/prompt-input';
1<PromptInput onSubmit={(message) => enviar(message.text, message.files)}>
2 <PromptInput.Header>
3 <PromptInput.Attachments />
4 </PromptInput.Header>
5 <PromptInput.Body>
6 <PromptInput.Textarea placeholder="Preguntá lo que quieras…" />
7 </PromptInput.Body>
8 <PromptInput.Footer>
9 <PromptInput.Tools>
10 <PromptInput.AddAttachments />
11 </PromptInput.Tools>
12 <PromptInput.Submit status={status} />
13 </PromptInput.Footer>
14</PromptInput>
La raíz es un form de verdad. Al enviar llama a onSubmit con { text, files } y después se limpia sola, algo que podés apagar con clearOnSubmit={false} si tu app maneja el reset.

Composición

Usa la siguiente composición para construir un PromptInput:
PromptInput
├── PromptInput.Header
│ └── PromptInput.Attachments
├── PromptInput.Body
│ └── PromptInput.Textarea
└── PromptInput.Footer
├── PromptInput.Tools
│ ├── PromptInput.AddAttachments
│ └── PromptInput.Button
└── PromptInput.Submit

Teclado

Enter envía, Shift y Enter hacen un salto de línea, y la escritura por IME se deja en paz para que un candidato sin confirmar nunca se mande de casualidad. El textarea crece con el texto hasta maxRows y después scrollea.

Archivos

Los archivos entran por tres lados: el botón del clip, un drop en cualquier parte del composer, y un drop en cualquier parte de la página cuando se activa globalDrop. accept, maxFiles y maxFileSize se revisan antes de quedarse con el archivo, y lo que se rechaza va a onError con un código que podés mostrar.
1'use client';
2
3import { PromptInput } from '@/components/ui/prompt-input';
4
5export function Attachments() {
6 return (
7 <div className="w-full max-w-lg">
8 <PromptInput accept="image/*,.pdf,.md" multiple maxFiles={4} globalDrop>
9 <PromptInput.Header>
10 <PromptInput.Attachments />
11 </PromptInput.Header>
12 <PromptInput.Body>
13 <PromptInput.Textarea placeholder="Drop a file anywhere, or use the clip…" />
14 </PromptInput.Body>
15 <PromptInput.Footer>
16 <PromptInput.Tools>
17 <PromptInput.AddAttachments />
18 <PromptInput.Button label="Model">gpt-5</PromptInput.Button>
19 </PromptInput.Tools>
20 <PromptInput.Submit />
21 </PromptInput.Footer>
22 </PromptInput>
23 </div>
24 );
25}
Cada archivo aceptado se convierte en un PromptInputFile con una object URL lista para previsualizar y el File original para subir:
1interface PromptInputFile {
2 id: string;
3 filename: string;
4 mediaType: string;
5 size: number;
6 url: string;
7 file: File;
8}
usePromptInputAttachments() expone files, add, remove, clear y openFileDialog, y así construís tu propia tira con Attachments en vez de los chips incluidos.

Estado

status en el botón de envío cubre la ida y la vuelta completa: ready manda, submitted gira, streaming se vuelve un cuadrado de stop y error se pone destructivo.
1'use client';
2
3import { useState } from 'react';
4import { PromptInput, type PromptInputStatus } from '@/components/ui/prompt-input';
5
6const STATUSES: PromptInputStatus[] = ['ready', 'submitted', 'streaming', 'error'];
7
8export function Statuses() {
9 const [status, setStatus] = useState<PromptInputStatus>('ready');
10
11 return (
12 <div className="flex w-full max-w-lg flex-col gap-3">
13 <div className="flex flex-wrap gap-2">
14 {STATUSES.map((value) => (
15 <button
16 key={value}
17 type="button"
18 onClick={() => setStatus(value)}
19 data-active={status === value ? '' : undefined}
20 className="border-border hover:bg-muted data-active:bg-muted rounded-full border px-3 py-1 text-xs transition-colors"
21 >
22 {value}
23 </button>
24 ))}
25 </div>
26 <PromptInput>
27 <PromptInput.Body>
28 <PromptInput.Textarea placeholder="The submit button reflects the status…" />
29 </PromptInput.Body>
30 <PromptInput.Footer>
31 <PromptInput.Tools>
32 <PromptInput.AddAttachments />
33 </PromptInput.Tools>
34 <PromptInput.Submit status={status} disabled={status !== 'ready'} />
35 </PromptInput.Footer>
36 </PromptInput>
37 </div>
38 );
39}

Referencia de API

PromptInput

PropTypeDefaultDescription
onSubmit(message: PromptInputMessage, event: FormEvent) => void-Se llama con el texto y los archivos
acceptstring-Tipos de archivo aceptados
multiplebooleanfalsePermite más de un archivo
maxFilesnumber-Tope de archivos adjuntos
maxFileSizenumber-Tope en bytes por archivo
globalDropbooleanfalseAcepta un drop en cualquier parte de la página
clearOnSubmitbooleantrueLimpia el texto y los archivos al enviar
onError(error: { code, message }) => void-Se llama cuando se rechaza un archivo
classNamestring-Clases CSS adicionales

PromptInput.Textarea

PropTypeDefaultDescription
minRowsnumber1Filas antes de crecer
maxRowsnumber8Filas antes de scrollear
classNamestring-Clases CSS adicionales

PromptInput.Submit

PropTypeDefaultDescription
status'ready' | 'submitted' | 'streaming' | 'error''ready'Icono, color y deshabilitado
labelstring'Send message'Nombre accesible
classNamestring-Clases CSS adicionales

PromptInput.Button

PropTypeDefaultDescription
labelstring-Nombre accesible y título
variant'ghost' | 'outline''ghost'Tratamiento visual
classNamestring-Clases CSS adicionales

PromptInput.AddAttachments

PropTypeDefaultDescription
labelstring'Add files'Nombre accesible
classNamestring-Clases CSS adicionales
PropTypeDefaultDescription
classNamestring-Clases CSS adicionales
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue