Logo FormaUI

FormaUI

All providers

Google Gemini AI - Vercel SDK

Copy-paste module for integrating Google Gemini into a Next.js App Router project using the Vercel AI SDK. Covers text generation (sync and streaming), structured object generation, image generation, embeddings, and tool calling — logic only, no UI.

Dependencies

  • ai: The Vercel AI SDK core — generateText, streamText, generateObject, embed, embedMany, tool, and related types.
  • @ai-sdk/google: The Google Generative AI provider for the AI SDK — turns a Gemini model id into a model instance the SDK can call.
  • zod: Schema validation — both for validating request input and for describing structured generation output.

Folder structure

ai/google/
├── actions.ts                    # "use server" — public API, call directly from Server/Client Components
├── service.ts                     # Internal — provider init + raw AI SDK calls
├── client.ts                       # Internal — fetch helper for the one streaming Route Handler
├── hooks.ts                         # React hooks for Client Components
├── constants.ts                      # Internal — models, defaults, limits (single source of truth)
├── types.ts                           # Shared types — freely importable from anywhere
├── errors.ts                           # Internal — error normalization (AIResult<T> / AIError)
├── models.ts                            # Internal — model selection, validation, metadata
├── schemas.ts                            # Internal — Zod input validation + reusable output schemas
├── rate-limit.ts                          # Internal — rate limiting (in-memory default, pluggable store)
├── prompts/
│   ├── system.ts                            # Reusable system prompt presets
│   ├── builder.ts                             # Prompt composition/templating/trimming helpers
│   ├── summarize.ts                             # Summarization prompt builder
│   ├── translate.ts                               # Translation prompt builder
│   └── extract.ts                                   # Field-extraction prompt builder
├── tools/
│   ├── calculator.ts                                  # Example tool: arithmetic evaluation
│   ├── search.ts                                        # Example tool: web search (placeholder — needs a provider)
│   └── index.ts                                           # Central tool registry
├── routes/
│   └── stream-text.route.ts                                 # Copy as-is to app/api/ai/stream-text/route.ts
├── env.example
└── setup.md

File usage

service.ts is internal. It owns provider initialization (reading GOOGLE_GENERATIVE_AI_API_KEY) and every raw AI SDK call — generateText, streamText, generateObject, image generation, embeddings, and tool calling. It doesn't validate input or check rate limits; it assumes whatever it's given is already trustworthy.

actions.ts is the public server-side entry point — a "use server" file. Import from here in Server Components, other Server Actions, or directly from a Client Component's event handler. Each function validates its input with schemas.ts, enforces an internal IP-based rate limit, calls service.ts, and returns a consistent AIResult<T> ({ success: true, data } or { success: false, error }). Because Server Actions can only accept and return serializable data, actions.ts never exposes abortSignal or a rate-limit store as parameters, and strips non-serializable error detail before returning to the client.

client.ts and hooks.ts are the client-side entry points, but their scope is narrower than in a typical module: since actions.ts functions can be called directly from Client Components (no fetch needed), client.ts only exists to consume the one thing a Server Action can't do — a live stream. It talks to the Route Handler generated from routes/stream-text.route.ts. hooks.ts wraps both patterns: useGenerateText, useGenerateObject, and useGenerateImage call actions.ts directly, while useStreamText goes through client.ts.

What actions.ts covers

  • generateText — single-shot text generation (chat, Q&A, general prompting).
  • generateObject — structured output validated against a Zod schema (form-filling, classification, any typed JSON response).
  • generateImage — image generation via Gemini's multimodal "Nano Banana" models, including reference-image-based editing/consistency.
  • embed / embedMany — text embeddings for semantic search, RAG retrieval, or clustering.
  • generateWithTools — tool-calling / agentic loop over the registry in tools/index.ts (extend with your own tools).
  • summarize / translate / extractFields — ready-made task helpers pairing a prompt builder (prompts/) with a schema (schemas.ts).

Streaming text generation is the one capability not in actions.ts — a Server Action can't return a live stream, so it's hooks.ts's useStreamText (client) talking to routes/stream-text.route.ts (the one Route Handler this module ships).

Usage examples

Text generation (server-side)

import { generateText } from "@/ai/google/actions";

const result = await generateText({ prompt: "Explain the AI SDK in one sentence." });

if (result.success) {
  console.log(result.data.text);
} else {
  console.error(result.error.code, result.error.message);
}

Text generation (Client Component)

"use client";
import { useGenerateText } from "@/ai/google/hooks";

export function AskBox() {
  const { generate, data, isLoading, error } = useGenerateText();

  return (
    <div>
      <button onClick={() => generate({ prompt: "Write a haiku about databases." })} disabled={isLoading}>
        Generate
      </button>
      {error && <p>{error.message}</p>}
      {data && <p>{data.text}</p>}
    </div>
  );
}

Streaming (Client Component)

"use client";
import { useStreamText } from "@/ai/google/hooks";

export function ChatBox() {
  const { streamedText, isStreaming, start, abort } = useStreamText();

  return (
    <div>
      <button onClick={() => start({ prompt: "Tell me a story." })} disabled={isStreaming}>
        Generate
      </button>
      {isStreaming && <button onClick={abort}>Stop</button>}
      <p>{streamedText}</p>
    </div>
  );
}

Structured output

import { generateObject } from "@/ai/google/actions";
import { z } from "zod";

const result = await generateObject({
  prompt: "Extract the recipe name and ingredient count from: 'Grandma's Chili, 9 ingredients'.",
  schema: z.object({ name: z.string(), ingredientCount: z.number() }),
});

Image generation

import { generateImage } from "@/ai/google/actions";

const result = await generateImage({
  prompt: "A minimalist logo of a fox reading a book, flat vector style.",
  aspectRatio: "1:1",
});

if (result.success) {
  const { base64, mimeType } = result.data.images[0];
  // e.g. `data:${mimeType};base64,${base64}`
}

Embeddings

import { embed, embedMany } from "@/ai/google/actions";

const single = await embed({ value: "The quick brown fox.", taskType: "RETRIEVAL_DOCUMENT" });
const batch = await embedMany({ values: ["doc one", "doc two", "doc three"] });

Tool calling

import { generateWithTools } from "@/ai/google/actions";

const result = await generateWithTools({
  prompt: "What's 42 * 17?",
  toolNames: ["calculator"], // omit to expose the full tool registry
});

if (result.success) {
  console.log(result.data.text);
  console.log(result.data.toolCalls);
}

Task helpers: summarize, translate, extractFields

import { summarize, translate, extractFields } from "@/ai/google/actions";
import { z } from "zod";

const s = await summarize({ text: longArticle, length: "short" });
// s.data => { summary: string, keyPoints: string[] }

const t = await translate({ text: "Hello, world!", targetLanguage: "Spanish" });
// t.data => { translatedText, sourceLanguage, targetLanguage }

const e = await extractFields({
  text: "Contact: Jane Doe, [email protected]",
  fieldDescriptions: { name: "Full name", email: "Email address" },
  schema: z.object({ name: z.string(), email: z.string().email() }),
});
// e.data => { name, email }

What you can build with this

This module is a general-purpose AI toolkit, not a single-feature integration — what you build with it depends on which pieces you combine:

  • Conversational AI: pair useStreamText with a message-history UI for a chat assistant, or generateText/generateWithTools for a backend agent.
  • Content tooling: summarize, translate, and extractFields cover article summarizers, localization pipelines, and document-to-structured-data extraction (invoices, resumes, support tickets).
  • Search and RAG: embed/embedMany plus your own vector store gives semantic search, recommendation, and retrieval-augmented generation.
  • Creative/product features: generateImage covers on-demand asset generation, product mockups, or avatar/logo generators, including iterative editing via reference images.
  • Agentic workflows: generateWithTools plus the tools/ registry is the base for anything that needs the model to call functions — extend tools/index.ts with your own (database lookups, internal APIs, etc.) following the calculator.ts/search.ts pattern.

All of the above share the same validation, error handling (AIResult<T>), and rate-limiting layer, so mixing several of these in one app doesn't mean duplicating that plumbing.