Real-time Response Streaming from Backend to Frontend with Next.js AI SDK

I’m working on a Next.js chat application that uses the AI SDK with Google’s Gemini model. The goal is to show responses as they come in, character by character, instead of waiting for the complete answer.

Current Setup:

My backend API route looks like this:

import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { streamText } from "ai";
import { NextRequest, NextResponse } from "next/server";

const googleAI = createGoogleGenerativeAI({
  apiKey: process.env.GOOGLE_API_KEY,
});

const aiModel = googleAI("gemini-1.5-flash-8b");

export async function POST(request: NextRequest) {
  try {
    const { userMessages } = await request.json();
    
    const streamResult = streamText({
      model: aiModel,
      system: "You are a helpful AI assistant.",
      prompt: userMessages[0].content,
    });

    // This shows streaming works on server side
    for await (const chunk of streamResult.textStream) {
      console.log(chunk);
    }

    return streamResult.toDataStreamResponse();
  } catch (err) {
    console.error(err);
    return new NextResponse("Request failed", { status: 500 });
  }
}

Frontend component:

"use client";
import React from "react";
import { Input } from "./ui/input";
import { useChat } from "ai/react";
import { Button } from "./ui/button";
import { Send } from "lucide-react";
import ChatMessages from "./ChatMessages";

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

  return (
    <div className="flex flex-col h-screen">
      <div className="p-4 border-b">
        <h2 className="text-2xl font-semibold">AI Chat</h2>
      </div>
      <ChatMessages messages={messages} />
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <Input 
            value={input} 
            onChange={handleInputChange} 
            placeholder="Type your message here" 
            className="flex-1" 
          />
          <Button type="submit" className="bg-green-600">
            <Send className="w-4 h-4" />
          </Button>
        </div>
      </form>
    </div>
  );
};

export default ChatInterface;

The Issue:
While the server logs show that chunks are being processed one by one, the frontend only displays the complete response after everything is done. The streaming effect doesn’t work on the client side.

What I Need:
I want users to see the AI response appear gradually as it’s being generated, similar to how ChatGPT works. Should I handle the streaming manually on the frontend or is there something wrong with my current approach?

Yeah, you’re consuming the stream twice. The for await loop reads all the chunks before returning them to the frontend. Delete those logging lines and it’ll work fine. The useChat hook needs a fresh stream, not one that’s already been read.

Interesting issue! You’re probably consuming the stream in your backend loop before returning it - that’s why the frontend gets an empty stream. Remove the for await loop that logs chunks and just return streamResult.toDataStreamResponse() directly. The useChat hook handles streaming automatically once the response flows through properly. Quick question - does your network tab show the response coming in chunks or all at once?

Your backend has a critical flaw that’s breaking the streaming. You’re consuming the entire stream with that for-await loop through streamResult.textStream before returning the response. The frontend gets an already-exhausted stream with nothing left to process. Just remove the console.log loop entirely and return the stream response immediately. The AI SDK’s toDataStreamResponse() method creates a proper streaming response that works with the useChat hook. I hit this exact same issue and spent hours debugging before I realized I was consuming the stream server-side. Fix this and useChat will automatically handle the streaming on the frontend, updating messages incrementally as chunks arrive. No frontend changes needed.