How to Integrate Gemini 1.5 Pro API in Next.js (App Router)

A practical step-by-step guide to consuming Google's multimodal AI API in Next.js, setting up environment keys, text streaming, and multimodal calls.

The release of the **Gemini 1.5** model family by Google introduced significant advancements for software developers, most notably a massive context window of up to 2 million tokens and native support for multimodal analysis (processing audio, video, images, and PDFs).

In this article, we will build a professional integration using **Next.js App Router** and the official Google Generative AI SDK for Node.js. We will show how to secure your API endpoints, handle responses correctly, and implement real-time streaming for a premium UX.

1. Environment Setup and Installation

First, you will need to obtain a free API Key from the **Google AI Studio**. Once you have your key, add it to your local environment file.env.local:

GEMINI_API_KEY=your_key_here

Inside your Next.js project directory, install the official Google Generative AI SDK package:

npm install @google/generative-ai

2. Creating the Next.js API Endpoint (Route Handler)

To keep your API key secure and prevent exposing it in the client browser, all calls to the Gemini API must be routed through your server. Let's build a Next.js Route Handler that streams responses using Server-Sent Events (SSE).

Create the file app/api/chat/route.ts:

import { GoogleGenerativeAI } from "@google/generative-ai";
import { NextResponse } from "next/server";

const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || "");

export async function POST(req: Request) {
  try {
    const { prompt } = await req.json();
    
    // Initialize the Gemini 1.5 Pro model
    const model = genAI.getGenerativeModel({ model: "gemini-1.5-pro" });
    
    // Execute the generative call via streaming
    const result = await model.generateContentStream(prompt);
    
    // Setup the response stream
    const encoder = new TextEncoder();
    const stream = new ReadableStream({
      async start(controller) {
        for await (const chunk of result.stream) {
          const text = chunk.text();
          controller.enqueue(encoder.encode(text));
        }
        controller.close();
      },
    });

    return new Response(stream, {
      headers: {
        "Content-Type": "text/plain; charset=utf-8",
        "Transfer-Encoding": "chunked",
      },
    });
  } catch (error) {
    return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
  }
}

3. Consuming the AI Stream in React (Client-Side Component)

On the client side, we will read the response body as a ReadableStream to update the UI progressively, ensuring the user sees the response stream word-by-word in real time.

Here is the basic implementation of the chat window:

"use client";

import { useState } from "react";

export default function ChatWindow() {
  const [prompt, setPrompt] = useState("");
  const [response, setResponse] = useState("");
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setResponse("");
    setLoading(true);

    try {
      const res = await fetch("/api/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ prompt }),
      });

      if (!res.body) return;
      const reader = res.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        const chunk = decoder.decode(value, { stream: true });
        setResponse((prev) => prev + chunk);
      }
    } catch (err) {
      console.error(err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="chat-container">
      <form onSubmit={handleSubmit}>
        <input 
          value={prompt} 
          onChange={(e) => setPrompt(e.target.value)} 
          placeholder="Ask a question..." 
        />
        <button type="submit" disabled={loading}>Send</button>
      </form>
      <div className="output">{response}</div>
    </div>
  );
}

4. Multimodal Calls: Uploading and Analyzing Images

To process media inputs (such as screenshots or photos), we must convert the uploaded file into a Base64 buffer and feed it to the Gemini SDK content payload array:

// Helper function to format image files for the Gemini SDK
function fileToGenerativePart(buffer: Buffer, mimeType: string) {
  return {
    inlineData: {
      data: buffer.toString("base64"),
      mimeType
    },
  };
}

// Running a multimodal request on the server side
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
const imagePart = fileToGenerativePart(imageBuffer, "image/png");

const result = await model.generateContent([
  "Describe what you see in this screenshot and flag any potential UI design bugs.",
  imagePart
]);
console.log(result.response.text());

Conclusion

Integrating Gemini 1.5 Pro within Next.js allows you to construct cutting-edge artificial intelligence solutions featuring optimized server performance and interactive browser UIs. The mix of secure server routes, Server-Sent Events, and native multimodal capabilities opens up massive opportunities to enhance your web workflows.