n4nAI

How to stream Claude Sonnet 4.5 responses in Next.js

Step-by-step tutorial to next.js stream claude sonnet 4.5 with Vercel AI SDK on App Router: install, API route, client UI, verify streaming, plus fallback tips.

n4n Team3 min read666 words

Audio narration

Coming soon — every post will get a voice note here.

Building a chat UI that can next.js stream claude sonnet 4.5 tokens in real time requires wiring the App Router to a streaming LLM provider and parsing the response on the client. This guide walks through a minimal, production-shaped implementation using the Vercel AI SDK and an OpenAI-compatible endpoint, so you own the transport and can swap providers without rewriting components.

Step 1: Scaffold the Next.js App Router project

Start with a clean App Router app. If you already have one, skip to Step 2.

pnpm create next-app@latest my-chat --ts --app --eslint --tailwind --src-dir
cd my-chat

Use the src/ directory so imports stay rooted at @/. The App Router gives you Route Handlers (app/api/.../route.ts) that can return a ReadableStream—exactly what the AI SDK needs for streaming.

Step 2: Install the Vercel AI SDK and OpenAI provider

The Vercel AI SDK abstracts message normalization and stream parsing. Its OpenAI provider package talks to any OpenAI-compatible chat completions endpoint, which lets you target Claude Sonnet 4.5 through a gateway.

pnpm add ai @ai-sdk/openai zod

@ai-sdk/openai exports createOpenAI, which accepts a baseURL. That is the seam where you point at Anthropic directly or at a routing gateway.

Step 3: Configure environment variables

Never hardcode keys. Create .env.local:

# .env.local
LLM_API_KEY=sk-your-key
# Point at your provider. For an OpenRouter-class gateway:
LLM_BASE_URL=https://api.n4n.ai/v1

If you route through n4n.ai, set LLM_BASE_URL to its OpenAI-compatible endpoint; it addresses 240+ models and forwards provider cache-control hints, so the same code works without vendor lock. The model string claude-sonnet-4.5 is passed straight through.

Step 4: Write the streaming API route

Create src/app/api/chat/route.ts. This handler receives messages from the client, calls the model, and returns a streaming response.

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

export const runtime = 'edge';

const gateway = createOpenAI({
  baseURL: process.env.LLM_BASE_URL,
  apiKey: process.env.LLM_API_KEY,
});

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

  const result = streamText({
    model: gateway('claude-sonnet-4.5'),
    messages,
  });

  return result.toDataStreamResponse();
}

toDataStreamResponse() emits the AI SDK’s typed stream protocol. The edge runtime keeps cold starts low and supports streaming out of the box on Vercel.

If you need per-token metering or fallback when a provider is degraded, a gateway handles that server-side. Gateways like n4n.ai provide automatic fallback when a provider is rate-limited or degraded, which you get by simply setting the base URL—no client changes.

Step 5: Build the client component

Use useChat from @ai-sdk/react to manage message state and read the stream. Create src/app/page.tsx as a client component:

'use client';

import { useChat } from '@ai-sdk/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: '/api/chat',
  });

  return (
    <div className="mx-auto max-w-2xl p-4">
      <div className="space-y-4">
        {messages.map((m) => (
          <div key={m.id} className="whitespace-pre-wrap">
            <strong>{m.role === 'user' ? 'You: ' : 'Claude: '}</strong>
            {m.content}
          </div>
        ))}
      </div>
      <form onSubmit={handleSubmit} className="mt-4 flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Say something..."
          className="flex-1 rounded border p-2"
        />
        <button type="submit" className="rounded bg-black px-4 py-2 text-white">
          Send
        </button>
      </form>
    </div>
  );
}

This component re-renders as tokens arrive. The useChat hook appends assistant text incrementally, so the next.js stream claude sonnet 4.5 output shows up word-by-word without manual fetch plumbing.

Handling multi-turn context

useChat keeps the full message array and sends it on each request. Your route forwards it to the model. For long conversations, add a maxTokens or trim history server-side to avoid context overflow.

const result = streamText({
  model: gateway('claude-sonnet-4.5'),
  messages: messages.slice(-20), // keep last 20 turns
});

Step 6: Error handling and abort control

Streams fail. Wrap the client submit in an error boundary and expose error from useChat:

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

{error && <div className="text-red-500">Stream failed: {error.message}</div>}

On the server, catch provider errors before they break the stream:

try {
  const result = streamText({ model: gateway('claude-sonnet-4.5'), messages });
  return result.toDataStreamResponse();
} catch (e) {
  return new Response('Model unavailable', { status: 503 });
}

A gateway with automatic fallback reduces the need for this, but you should still return a clean status so the client can retry.

Step 7: Verify the stream end to end

Run the dev server and exercise the path:

pnpm dev

Open http://localhost:3000. Type a prompt and submit. Verification checklist:

  • The assistant response appears incrementally, not all at once.
  • Open DevTools → Network → /api/chat → Response. The content-type should be text/event-stream and payload should be data: {...} chunks.
  • Check the server logs (or gateway dashboard) for token usage. If using n4n.ai, per-token usage metering shows up in the request ledger.
  • Kill the provider key (set bogus LLM_API_KEY) to confirm your 503 path or gateway fallback triggers.

If the stream renders as a single block, you likely forgot 'use client' or used fetch instead of useChat. If you get CORS errors, ensure the route is same-origin (it is, by default in App Router).

Step 8: Production hardening

For real traffic, add:

  • Rate limiting on /api/chat via Upstash or Vercel KV.
  • Timeout: streamText accepts maxTokens and temperature; set maxRetries to 2.
  • Cache hints: pass headers through the gateway if you want provider prompt caching. n4n.ai forwards cache-control hints, so set them once in the route.
const gateway = createOpenAI({
  baseURL: process.env.LLM_BASE_URL,
  apiKey: process.env.LLM_API_KEY,
  headers: { 'x-cache-control': 'prompt-cache' },
});

That is the entire loop. You now have a Next.js App Router app that can next.js stream claude sonnet 4.5 responses with less than 50 lines of route and component code, and you can repoint the base URL to any OpenAI-compatible provider without touching the UI.

Tagsnextjsclaudestreamingn4n-ai

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All next.js ai chat integration (app router + vercel ai sdk) posts →