Receber por email
← Voltar para o catálogo

ai-sdk-ui

Frontend React hooks for AI-powered chat interfaces, completions, and streaming UIs with Vercel AI SDK v5. Includes useChat, useCompletion, and useObject hooks for building interactive AI applications. Use when: building React chat interfaces, implementing AI completions in UI, streaming AI responses to frontend, handling chat message state, building Next.js AI apps, managing file attachments with

15estrelas
Atualizado há 11 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add jackspace/ClaudeSkillz

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/ai-sdk-ui/svg)](https://www.skillteca.com.br/skills/ai-sdk-ui)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

AI SDK UI - Frontend React Hooks

Frontend React hooks for AI-powered user interfaces with Vercel AI SDK v5.

Version: AI SDK v5.0.76+ (Stable) Framework: React 18+, Next.js 14+ Last Updated: 2025-10-22

Quick Start (5 Minutes)

Installation

npm install ai @ai-sdk/openai

Basic Chat Component (v5)

// app/chat/page.tsx
'use client';
import { useChat } from 'ai/react';
import { useState, FormEvent } from 'react';

export default function Chat() {
  const { messages, sendMessage, isLoading } = useChat({
    api: '/api/chat',
  });
  const [input, setInput] = useState('');

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    sendMessage({ content: input });
    setInput('');
  };

  return (
    <div>
      <div>
        {messages.map(m => (
          <div key={m.id}>
            <strong>{m.role}:</strong> {m.content}
          </div>
        ))}
      </div>
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Type a message..."
          disabled={isLoading}
        />
      </form>
    </div>
  );
}

API Route (Next.js App Router)

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

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

  const result = streamText({
    model: openai('gpt-4-turbo'),
    messages,
  });

  return result.toDataStreamResponse();
}

Result: A functional chat interface with streaming AI responses in ~10 lines of frontend code.

useChat Hook - Complete Reference

Basic Usage (v5 Pattern)

'use client';
import { useChat } from 'ai/react';
import { useState, FormEvent } from 'react';

export default function ChatComponent() {
  const { messages, sendMessage, isLoading, error } = useChat({
    api: '/api/chat',
  });
  const [input, setInput] = useState('');

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    if (!input.trim()) return;

    sendMessage({ content: input });
    setInput('');
  };

  return (
    <div className="flex flex-col h-screen">
      {/* Messages */}
      <div className="flex-1 overflow-y-auto p-4">
        {messages.map(message => (
          <div
            key={message.id}
            className={message.role === 'user' ? 'text-right' : 'text-left'}
          >
            <div className="inline-block p-2 rounded bg-gray-100">
              {message.content}
            </div>
          </div>
        ))}
        {isLoading && <div className="text-gray-500">AI is thinking...</div>}
      </div>

      {/* Input */}
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Type a message..."
          disabled={isLoading}
          className="w-full p-2 border rounded"
        />
      </form>

      {/* Error */}
      {error && <div className="text-red-500 p-4">{error.message}</div>}
    </div>
  );
}

Full API Reference

const {
  // Messages
  messages,           // Message[] - Chat history
  setMessages,        // (messages: Message[]) => void - Update messages

  // Actions
  sendMessage,        // (message: { content: string }) => void - Send message (v5)
  reload,             // () => void - Reload last response
  stop,               // () => void - Stop current generation

  // State
  isLoading,          // boolean - Is AI responding?
  error,              // Error | undefined - Error if any

  // Data
  data,               // any[] - Custom data from stream
  metadata,           // object - Response metadata
} = useChat({
  // Required
  api: '/api/chat',   // API endpoint

  // Optional
  id: 'chat-1',       // Chat ID for persistence
  initialMessages: [], // Initial messages (controlled mode)

  // Callbacks
  onFinish: (message, options) => {},  // Called when response completes
  onError: (error) => {},              // Called on error

  // Configuration
  headers: {},        // Custom headers
  body: {},           // Additional body data
  credentials: 'same-origin', // Fetch credentials

  // Streaming
  streamProtocol: 'data', // 'data' | 'text' (default: 'data')
});

v4 → v5 Breaking Changes

CRITICAL: useChat no longer manages input state in v5!

v4 (OLD - DON'T USE):

const { messages, input, handleInputChange, handleSubmit, append } = useChat();

<form onSubmit={handleSubmit}>
  <input value={input} onChange={handleInputChange} />
</form>

v5 (NEW - CORRECT):

const { messages, sendMessage } = useChat();
const [input, setInput] = useState('');

<form onSubmit={(e) => {
  e.preventDefault();
  sendMessage({ content: input });
  setInput('');
}}>
  <input value={input} onChange={(e) => setInput(e.target.value)} />
</form>

Summary of v5 Changes:

  1. Input management removed: input, handleInputChange, handleSubmit no longer exist
  2. append() → sendMessage(): New method for sending messages
  3. onResponse removed: Use onFinish instead
  4. initialMessages → controlled mode: Use messages prop for full control
  5. maxSteps removed: Handle on server-side only

See references/use-chat-migration.md for complete migration guide.

Tool Calling in UI

When your API uses tools, useChat automatically handles tool invocations in the message stream:

'use client';
import { useChat } from 'ai/react';

export default function ChatWithTools() {
  const { messages } = useChat({ api: '/api/chat' });

  return (
    <div>
      {messages.map(message => (
        <div key={message.id}>
          {/* Text content */}
          {message.content && <p>{message.content}</p>}

          {/* Tool invocations */}
          {message.toolInvocations?.map((tool, idx) => (
            <div key={idx} className="bg-blue-50 p-2 rounded my-2">
              <div className="font-bold">Tool: {tool.toolName}</div>
              <div className="text-sm">
                <strong>Args:</strong> {JSON.stringify(tool.args, null, 2)}
              </div>
              {tool.result && (
                <div className="text-sm">
                  <strong>Result:</strong> {JSON.stringify(tool.result, null, 2)}
                </div>
              )}
            </div>
          ))}
        </div>
      ))}
    </div>
  );
}

File Attachments

Upload files (images, PDFs, etc.) alongside messages:

'use client';
import { useChat } from 'ai/react';
import { useState, FormEvent } from 'react';

export default function ChatWithAttachments() {
  const { messages, sendMessage, isLoading } = useChat({ api: '/api/chat' });
  const [input, setInput] = useState('');
  const [files, setFiles] = useState<FileList | null>(null);

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();

    sendMessage({
      content: input,
      experimental_attachments: files
        ? Array.from(files).map(file => ({
            name: file.name,
            contentType: file.type,
            url: URL.createObjectURL(file),
          }))
        : undefined,
    });

    setInput('');
    setFiles(null);
  };

  return (
    <div>
      {/* Messages */}
      {messages.map(m => (
        <div key={m.id}>
          {m.content}
          {m.experimental_attachments?.map((att, idx) => (
            <div key={idx}>
              <img src={att.url} alt={att.name} />
            </div>
          ))}
        </div>
      ))}

      {/* Input */}
      <form onSubmit={handleSubmit}>
        <input
          type="file"
          multiple
          onChange={(e) => setFiles(e.target.files)}
          accept="image/*"
        />
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
        />
        <button type="submit" disabled={isLoading}>Send</button>
   
Compartilhar X LinkedIn