TechLead
Lección 3 de 8
5 min de lectura
AI SDK

Hook useChat

Construye interfaces conversacionales con el hook useChat para aplicaciones de chat

El hook useChat

El hook useChat es la forma principal de construir interfaces de chat con Vercel AI SDK. Maneja el estado de mensajes, las respuestas en streaming, los estados de carga y la entrada del usuario automáticamente.

Qué ofrece useChat

  • messages: Arreglo de mensajes con actualizaciones automáticas
  • input: Valor actual del campo de entrada
  • handleInputChange: Handler para cambios del input
  • handleSubmit: Handler para enviar el formulario
  • isLoading: Estado de carga durante la generación
  • error: Estado de error si algo falla

Uso básico

'use client';

import { useChat } from 'ai/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <div className="flex flex-col h-screen">
      {/* Message List */}
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((message) => (
          <div
            key={message.id}
            className={`p-4 rounded-lg ${
              message.role === 'user'
                ? 'bg-blue-100 ml-auto max-w-[80%]'
                : 'bg-gray-100 mr-auto max-w-[80%]'
            }`}
          >
            <p className="font-semibold">
              {message.role === 'user' ? 'You' : 'AI'}
            </p>
            <p className="whitespace-pre-wrap">{message.content}</p>
          </div>
        ))}
      </div>

      {/* Input Form */}
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <input
            value={input}
            onChange={handleInputChange}
            placeholder="Type a message..."
            className="flex-1 p-2 border rounded-lg"
          />
          <button
            type="submit"
            className="px-4 py-2 bg-blue-500 text-white rounded-lg"
          >
            Send
          </button>
        </div>
      </form>
    </div>
  );
}

Ruta API del servidor

El hook useChat necesita una ruta API en el servidor que reciba los mensajes y devuelva la respuesta en streaming. Esta ruta usa streamText para generar tokens de forma progresiva.

// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';

export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4-turbo'),
    system: 'You are a helpful assistant.',
    messages,
  });

  return result.toDataStreamResponse();
}

Todas las opciones de useChat

El hook expone muchas propiedades y callbacks que permiten personalizar el comportamiento del chat: desde la URL de la API hasta callbacks para eventos de respuesta, error y finalización.

const {
  messages,          // Message array
  input,             // Current input value
  handleInputChange, // Input onChange handler
  handleSubmit,      // Form onSubmit handler
  isLoading,         // Loading state
  error,             // Error state
  reload,            // Regenerate last AI response
  stop,              // Stop current generation
  setMessages,       // Manually set messages
  append,            // Append a message programmatically
  setInput,          // Manually set input value
} = useChat({
  api: '/api/chat',           // API endpoint (default: '/api/chat')
  id: 'my-chat',              // Unique ID for multiple chats
  initialMessages: [],         // Pre-populate messages
  initialInput: '',           // Initial input value

  // Callbacks
  onResponse: (response) => {
    console.log('Response started:', response);
  },
  onFinish: (message) => {
    console.log('Message complete:', message);
  },
  onError: (error) => {
    console.error('Error:', error);
  },

  // Additional options
  headers: { 'X-Custom-Header': 'value' },
  body: { customField: 'value' },  // Extra data to send
  sendExtraMessageFields: true,    // Include extra message fields
});

Estados de carga

La propiedad isLoading indica si el modelo está generando una respuesta. Se puede usar para mostrar indicadores visuales y deshabilitar el campo de entrada durante la generación.

'use client';

import { useChat } from 'ai/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}

      {isLoading && (
        <div className="flex items-center gap-2 text-gray-500">
          <div className="animate-spin h-4 w-4 border-2 border-blue-500 rounded-full border-t-transparent" />
          AI is typing...
        </div>
      )}

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          disabled={isLoading}
        />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Sending...' : 'Send'}
        </button>
      </form>
    </div>
  );
}

Detener y regenerar

Las funciones stop y reload permiten al usuario controlar la generación: detener una respuesta en curso o regenerar la última respuesta del modelo si no fue satisfactoria.

'use client';

import { useChat } from 'ai/react';

export default function Chat() {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    isLoading,
    stop,
    reload,
  } = useChat();

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}

      <div className="flex gap-2">
        {isLoading ? (
          <button onClick={stop} className="text-red-500">
            Stop generating
          </button>
        ) : (
          messages.length > 0 && (
            <button onClick={() => reload()} className="text-blue-500">
              Regenerate response
            </button>
          )
        )}
      </div>

      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

Control programático de mensajes

Con append y setMessages puedes añadir mensajes de forma programática o modificar el historial completo. Esto es útil para mensajes predefinidos, sugerencias rápidas o limpiar la conversación.

'use client';

import { useChat } from 'ai/react';

export default function Chat() {
  const { messages, append, setMessages } = useChat();

  // Add a message programmatically
  const sendPredefinedMessage = () => {
    append({
      role: 'user',
      content: 'Tell me a joke!',
    });
  };

  // Clear all messages
  const clearChat = () => {
    setMessages([]);
  };

  // Edit the last message and regenerate
  const editLastMessage = (newContent: string) => {
    const newMessages = messages.slice(0, -1);
    setMessages(newMessages);
    append({
      role: 'user',
      content: newContent,
    });
  };

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}

      <button onClick={sendPredefinedMessage}>Tell me a joke</button>
      <button onClick={clearChat}>Clear chat</button>
    </div>
  );
}

Headers y body personalizados

'use client';

import { useChat } from 'ai/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    // Send additional data with each request
    body: {
      userId: 'user-123',
      sessionId: 'session-456',
    },
    // Custom headers
    headers: {
      'X-Custom-Header': 'my-value',
    },
  });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

💡 Puntos clave

  • useChat maneja estado y streaming automáticamente
  • • Úsalo con una ruta API del servidor usando streamText
  • • Usa isLoading para indicadores de carga
  • stop() cancela, reload() regenera
  • append() y setMessages() para control programático

📚 Más recursos

Continuar aprendiendo