AI no Frontend — Integração Prática
Integrar LLMs em apps React/Next.js: Vercel AI SDK, streaming UI, gestão de estado e UX patterns.
Definição
Integrar IA em apps frontend envolve: SDK para comunicação com LLMs (Vercel AI SDK, OpenAI SDK), gerenciamento de estado de conversação (mensagens, loading, error), streaming para UX responsiva, e patterns de UI específicos para IA (skeleton durante geração, auto-scroll, cópia de código, feedback de qualidade). O backend pode ser um Route Handler Next.js ou endpoint externo.
Problema → Solução
Problema
Gerenciar estado de chat complexo manualmente: lista de mensagens, loading per-message, retry, scroll automático, suporte a markdown. Reinventar a roda para cada projeto.Solução
Vercel AI SDK: useChat() para conversas multi-turn, useCompletion() para single completions, useObject() para geração de objetos estruturados. Combinar com react-markdown para renderizar MD, react-syntax-highlighter para código, e framer-motion para animações de entrada dos tokens.Dica de entrevista
Tip
Nunca exponha API keys no cliente. Toda chamada a LLM vai pelo servidor (Next.js Route Handler). O cliente chama seu próprio backend, que chama a API do LLM. Isso também permite adicionar auth, rate limiting, e logging sem mudar o frontend.Código
// Next.js Route Handler com autenticação
// app/api/chat/route.ts
import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'
import { getSession } from '@/lib/auth'
export async function POST(req: Request) {
const session = await getSession(req)
if (!session) return new Response('Unauthorized', { status: 401 })
const { messages } = await req.json()
const result = streamText({
model: openai('gpt-4o'),
system: 'Assistente técnico de React e Next.js.',
messages,
maxTokens: 2000,
})
return result.toDataStreamResponse()
}
// Componente de chat completo
'use client'
import { useChat } from 'ai/react'
import ReactMarkdown from 'react-markdown'
import { useEffect, useRef } from 'react'
export function ChatInterface() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat()
const bottomRef = useRef<HTMLDivElement>(null)
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
return (
<div className="chat-container">
<div className="messages">
{messages.map(m => (
<div key={m.id} className={`message ${m.role}`}>
<ReactMarkdown>{m.content}</ReactMarkdown>
</div>
))}
{isLoading && <div className="typing-indicator">⋯</div>}
<div ref={bottomRef} />
</div>
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit" disabled={isLoading}>Enviar</button>
</form>
</div>
)
}Perguntas de entrevista
Q.Como adicionar auto-scroll no chat?▾
A.Ref no elemento final da lista de mensagens + useEffect que chama scrollIntoView toda vez que messages muda: `useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth" }) }, [messages])`. Atenção: só fazer auto-scroll se o usuário não scrollou para cima manualmente — verificar se está próximo do bottom antes de scrollar.
Q.Como renderizar markdown e código no output do LLM?▾
A.react-markdown + remark-gfm para markdown básico. Para blocos de código com syntax highlight: substituir o componente code do react-markdown por react-syntax-highlighter. Adicionar botão de cópia em cada bloco de código. A maioria dos LLMs gera markdown — sempre parsear a resposta, nunca exibir como texto cru.
Q.Como implementar feedback de qualidade (thumbs up/down)?▾
A.Adicionar botões 👍/👎 em cada mensagem do assistente. Salvar o feedback com o messageId e a resposta completa no backend. Usar para: melhorar prompts (análise de thumbs down), criar golden dataset para evals, identificar queries problemáticas. LangSmith e Braintrust têm UI de feedback integrada.
97/122← →