---
title: "First AI Summary"
description: "Build your first AI feature using the Vercel AI SDK's generateText function. Create a summarizeReviews function that uses Claude to generate concise review summaries."
canonical_url: "https://vercel.com/academy/ai-summary-app-with-nextjs/first-ai-summary"
md_url: "https://vercel.com/academy/ai-summary-app-with-nextjs/first-ai-summary.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-11T14:23:33.353Z"
content_type: "lesson"
course: "ai-summary-app-with-nextjs"
course_title: "Creating an AI Summary App with Next.js"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# First AI Summary

# First AI summary

You've built a great review site, but users have to read all reviews to understand a product. AI can summarize hundreds of reviews into a few sentences, saving users time and highlighting key themes.

## Outcome

Create a `summarizeReviews` function that uses Claude via AI Gateway to generate review summaries, and display them on product pages.

## Fast Track

1. Create `lib/ai-summary.ts` with `summarizeReviews(product)` using `generateText({ model: "anthropic/claude-sonnet-4.5", prompt })`
2. Create `components/ai-review-summary.tsx` as async Server Component that calls `await summarizeReviews(product)`
3. Add `<AIReviewSummary product={product} />` to `app/[productId]/page.tsx`, test at `/mower`

## Hands-on Exercise 2.2

Implement AI-powered review summarization:

**Requirements:**

1. Create `lib/ai-summary.ts` with a `summarizeReviews` function
2. Use `generateText` from the AI SDK with model `"anthropic/claude-sonnet-4.5"`
3. Write a basic prompt that includes all reviews
4. Create an `AIReviewSummary` component to display the summary
5. Add the component to product pages

**Implementation hints:**

- `generateText` returns `{ text }` with the generated content
- Use template literals to build the prompt
- The function should be async (AI calls take time)
- Keep the prompt simple for now (we'll improve it in the next lesson)
- The component should be a Server Component (it awaits the async function)

## Understanding generateText

The AI SDK provides `generateText` for one-shot text generation:

```typescript
import { generateText } from "ai";

const { text } = await generateText({
  model: "anthropic/claude-sonnet-4.5",
  prompt: "Your instructions here",
});
```

**Key parameters:**

- `model`: AI Gateway model string
- `prompt`: Your instructions and context

**Response:**
Returns an object with `text` (the generated string) plus metadata like token usage.

## Step 1: Create AI Summary Function

Create `lib/ai-summary.ts`:

```typescript title="lib/ai-summary.ts"
import { generateText } from "ai";
import { Product } from "./types";

export async function summarizeReviews(product: Product): Promise<string> {
  const prompt = `Summarize the following customer reviews for the ${product.name} product:

${product.reviews.map((review) => review.review).join("\n\n")}

Provide a concise summary of the main themes and sentiments in 2-3 sentences.`;

  try {
    const { text } = await generateText({
      model: "anthropic/claude-sonnet-4.5",
      prompt,
    });

    return text;
  } catch (error) {
    console.error("Failed to generate summary:", error);
    throw new Error("Unable to generate review summary. Please try again.");
  }
}
```

**What this does:**

1. Takes a Product object
2. Builds a prompt with product name and all reviews
3. Calls Claude via AI Gateway
4. Returns the generated summary
5. Handles errors gracefully

## Step 2: Create AI Summary Component

Create `components/ai-review-summary.tsx`:

```tsx title="components/ai-review-summary.tsx"
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Product } from "@/lib/types";
import { summarizeReviews } from "@/lib/ai-summary";
import { FiveStarRating } from "./five-star-rating";

export async function AIReviewSummary({ product }: { product: Product }) {
  const summary = await summarizeReviews(product);

  const averageRating =
    product.reviews.reduce((acc, review) => acc + review.stars, 0) /
    product.reviews.length;

  return (
    <Card className="w-full max-w-prose p-10 grid gap-10">
      <CardHeader className="items-center space-y-0 gap-4 p-0">
        <div className="grid gap-1 text-center">
          <CardTitle className="text-lg">AI Summary</CardTitle>
          <p className="text-xs text-muted-foreground">
            Based on {product.reviews.length} customer ratings
          </p>
        </div>
        <div className="bg-gray-100 px-3 rounded-full flex items-center py-2 dark:bg-gray-800">
          <FiveStarRating rating={Math.round(averageRating)} />
          <span className="text-sm ml-4 text-gray-500 dark:text-gray-400">
            {averageRating.toFixed(1)} out of 5
          </span>
        </div>
      </CardHeader>
      <CardContent className="p-0 grid gap-4">
        <p className="text-sm leading-loose text-gray-500 dark:text-gray-400">
          {summary}
        </p>
      </CardContent>
    </Card>
  );
}
```

**Key features:**

- Server Component (no `"use client"`)
- Awaits `summarizeReviews()` before rendering
- Displays average rating with stars
- Shows review count
- Clean card layout

## Step 3: Add to Product Page

Update `app/[productId]/page.tsx`:

```tsx title="app/[productId]/page.tsx" {3,23}
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { getProduct, getProducts } from "@/lib/sample-data";
import { Reviews } from "@/components/reviews";
import { AIReviewSummary } from "@/components/ai-review-summary";

export default async function ProductPage({
  params,
}: {
  params: Promise<{ productId: string }>;
}) {
  const { productId } = await params;

  let product;
  try {
    product = await getProduct(productId);
  } catch (error) {
    notFound();
  }

  return (
    <main className="min-h-screen p-8">
      <div className="max-w-4xl mx-auto space-y-8">
        <div>
          <h1 className="text-4xl font-bold">{product.name}</h1>
          <p className="text-lg text-muted-foreground mt-2">
            {product.description}
          </p>
        </div>

        <AIReviewSummary product={product} />

        <Reviews product={product} />
      </div>
    </main>
  );
}

// ... (generateStaticParams and generateMetadata remain the same)
```

## Try It

1. **Run your dev server:**
   ```bash
   pnpm dev
   ```

2. **Visit a product page:**
   ```
   http://localhost:3000/mower
   ```

3. **Watch the terminal:**

   ```
   GET /mower 200 in 2.3s
   ```

   That 2+ second delay is AI generation time.

4. **See the AI summary:**
   - Summary card appears above reviews
   - Claude-generated text summarizes all reviews
   - Average rating and review count displayed

5. **Try different products:**

   ```
   /ecoBright
   /aquaHeat
   ```

   Each gets its own unique AI-generated summary.

6. **Check AI Gateway dashboard:**
   - Go to Vercel dashboard → AI Gateway
   - See API calls increasing
   - Check token usage
   - Monitor costs

**What you'll notice:**

The summaries work but vary in format and quality. Sometimes they include ratings, sometimes word counts, sometimes extra metadata. That's expected with a basic prompt. We'll fix this in the next lesson with prompt engineering.

## How It Works

**Request Flow:**

1. User visits `/mower`
2. Next.js renders `ProductPage` (Server Component)
3. `<AIReviewSummary>` component renders
4. Calls `await summarizeReviews(product)`
5. Function sends prompt to AI Gateway
6. AI Gateway forwards to Claude API
7. Claude generates summary (\~2s)
8. Returns text to your app
9. Component renders with summary
10. Full HTML sent to browser

**Server-side rendering:**
Everything happens on the server. The user's browser never sees the AI Gateway API key or makes direct API calls.

## Performance Note

**Current behavior:**

- Every page load calls Claude
- \~2 second delay per request
- Costs tokens every time

**Coming in Section 3:**

- Smart Caching (Lesson 3.1)
- Cache summaries for 1 hour
- First request: 2s (generates)
- Subsequent requests: 50ms (cached)
- 97% cost reduction

## AI Gateway Dashboard

After visiting a few product pages, check your dashboard:

**Metrics you'll see:**

- API Calls: 3+ (one per product visited)
- Tokens Used: \~800-1000 per summary
- Cost: \~$0.002 per summary
- Model: `anthropic/claude-sonnet-4.5`
- Success Rate: 100%

**Example summary generation:**

- Input tokens: \~600 (your prompt + reviews)
- Output tokens: \~100 (generated summary)
- Total: \~700 tokens
- Cost: \~$0.0021

## Commit

```bash
git add lib/ai-summary.ts components/ai-review-summary.tsx app/\[productId\]/page.tsx
git commit -m "feat(ai): add basic AI review summarization"
git push
```

## Done-When

- [ ] `lib/ai-summary.ts` created with `summarizeReviews` function
- [ ] `generateText` working with Claude via AI Gateway
- [ ] `AIReviewSummary` component displays summaries
- [ ] Summaries appear on all product pages
- [ ] AI Gateway dashboard shows API calls and costs
- [ ] No errors in terminal or browser console

## What's Next

Your AI feature works, but the summaries are inconsistent. In the next lesson, you'll use prompt engineering techniques—few-shot examples, tone guidance, and output constraints—to make summaries production-ready with consistent format and quality.

***

**Sources:**

- [AI SDK generateText](https://sdk.vercel.ai/docs/reference/ai-sdk-core/generate-text)
- [Anthropic Claude Models](https://docs.anthropic.com/claude/docs/models-overview)
- [Next.js Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components)


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
