Next.js
You have a Next.js app and want effect-uai behind its chat. There are two ways, and the example app runs both side by side over the same tools.
@effect-uai/ui (/) | Keep useChat (/ai-sdk) |
|---|---|
| A small client page over atoms | Your page doesn’t change |
| The server keeps history; the client sends one message | The client sends the whole history |
Tool outputs are typed (a Date is a Date) | Tool outputs are JSON |
Approvals, live metrics as typed live values | Data parts and message metadata |
Route is a plain Effect HttpRouter route | Route speaks the AI SDK stream protocol |
Already on useChat? Keep it, and move when you need the left column.
Use @effect-uai/ui
The route hands its Request to an Effect route:
export const POST = (request: Request) => handler(request)
// lib/ui-server.tsexport const { handler } = HttpRouter.toWebHandler(ChatRoute.pipe(Layer.provide(...)))ChatRoute is the same route as on TanStack Start:
tools, the approval policy, metrics.
The page is a client component over atoms:
"use client"
export default function Page() { const thread = useAtomValue(chat.state) const submit = useAtomSet(chat.submit) // thread.messages, thread.active: plain data to render}chat is Atoms.thread({ contract: ChatUi, ... }) in lib/ui-chat.ts, at
module scope, so the state survives client-side navigation. Answers render as
markdown with Streamdown, which copes
with half-written markdown while it streams.
Keep useChat
Only the route handler changes: decode the AI SDK messages, run your loop, encode the result in the AI SDK’s stream format.
export async function POST(request: Request): Promise<Response> { const { messages } = await request.json() const body = agent(Messages.decodeMessages(messages)).pipe( UIMessageStream.toUIMessageStream(crypto.randomUUID()), SSE.toBytes, Stream.toReadableStream, ) return new Response(body, { headers: UIMessageStream.responseHeaders })}Coming from the AI SDK walks through it.
Run it
cd examples/next-chatcp .env.example .env # add your keypnpm installpnpm devOpen http://localhost:4330 for @effect-uai/ui and /ai-sdk for useChat.