skills /nextjs16-ai-sdk-v6
Next.js Referenced

nextjs16-ai-sdk-v6

Production-ready skill for building Next.js 16 applications with Vercel AI SDK v6. Use when developing web applications with the latest Next.js 16 features (Turbopack, Cache Components, PPR, React 19.2) combined with AI SDK v6 agent abstractions, tool approval, and streaming patterns. Triggers include requests to create Next.js 16 projects, implement AI chat/agents, use Cache Components, setup PPR, create agentic workflows, or any modern Next.js + AI development.

Next.js 16 + AI SDK v6 Development Skill

Modern web application development with Next.js 16 (stable October 2025) and AI SDK v6 (beta, stable end of 2025).

Quick Start

New Project Setup

hljs bash
# Create Next.js 16 project npx create-next-app@beta my-app # Add AI SDK v6 npm install ai@beta @ai-sdk/openai@beta @ai-sdk/react@beta

Essential Configuration

hljs typescript
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { // Enable Cache Components (PPR) cacheComponents: true, // Enable React Compiler (optional, production optimization) reactCompiler: true, // Turbopack filesystem caching (faster restarts) experimental: { turbopackFileSystemCacheForDev: true, }, }; export default nextConfig;

Version Requirements

PackageVersionNotes
Node.js20.9.0+Node 18 no longer supported
Next.js16.xnpm install next@beta
React19.2+Included with Next.js 16
AI SDK6.x betanpm install ai@beta
TypeScript5.1.0+Recommended

Core Concepts

Next.js 16 Key Features

  1. Turbopack Default - 2-5x faster builds, 10x faster hot reload
  2. Cache Components - Explicit caching with 'use cache' directive
  3. PPR (Partial Prerendering) - Static shells + dynamic streaming
  4. React 19.2 - View Transitions, useEffectEvent(), <Activity/>
  5. Async Params - Route params are now async (breaking change)

AI SDK v6 Key Features

  1. Agent Abstraction - ToolLoopAgent handles tool loops automatically
  2. Tool Approval - Built-in human-in-the-loop with needsApproval: true
  3. Type Safety - InferAgentUIMessage for type-safe UI messages
  4. Minimal Breaking Changes - Most v5 code works in v6

Architecture Patterns

Pattern 1: Basic Chat (No Agents)

hljs typescript
// app/api/chat/route.ts import { streamText } from 'ai'; import { openai } from '@ai-sdk/openai'; export async function POST(req: Request) { const { messages } = await req.json(); const result = streamText({ model: openai('gpt-4o'), messages, }); return result.toDataStreamResponse(); }
hljs tsx
// app/page.tsx 'use client'; import { useChat } from '@ai-sdk/react'; export default function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat(); return ( <div> {messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)} <form onSubmit={handleSubmit}> <input value={input} onChange={handleInputChange} /> <button>Send</button> </form> </div> ); }

Pattern 2: Agent with Tools (v6)

hljs typescript
// agents/weather-agent.ts import { ToolLoopAgent, tool } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; export const weatherAgent = new ToolLoopAgent({ model: openai('gpt-4o'), instructions: 'You are a helpful weather assistant.', tools: { getWeather: tool({ description: 'Get current weather for a city', parameters: z.object({ city: z.string().describe('City name'), }), execute: async ({ city }) => { // Fetch weather data return { city, temperature: 72, condition: 'sunny' }; }, }), }, // Default: stopWhen: stepCountIs(20) }); // app/api/agent/route.ts import { createAgentUIStreamResponse, convertToModelMessages } from 'ai'; import { weatherAgent } from '@/agents/weather-agent'; export async function POST(req: Request) { const { messages } = await req.json(); return createAgentUIStreamResponse({ agent: weatherAgent, messages: convertToModelMessages(messages), }); }

Pattern 3: Tool Approval (Human-in-the-Loop)

hljs typescript
// agents/database-agent.ts import { ToolLoopAgent, tool } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; export const dbAgent = new ToolLoopAgent({ model: openai('gpt-4o'), instructions: 'You help users manage their database records.', tools: { readData: tool({ description: 'Read records from database', parameters: z.object({ table: z.string() }), execute: async ({ table }) => { return await db.query(`SELECT * FROM ${table} LIMIT 10`); }, }), deleteData: tool({ description: 'Delete a record (requires approval)', parameters: z.object({ id: z.string(), table: z.string() }), needsApproval: true, // Requires user confirmation execute: async ({ id, table }) => { await db.query(`DELETE FROM ${table} WHERE id = ?`, [id]); return { success: true, deleted: id }; }, }), }, });
hljs tsx
// Client handling approvals 'use client'; import { useChat } from '@ai-sdk/react'; import { InferAgentUIMessage, lastAssistantMessageIsCompleteWithApprovalResponses } from 'ai'; import { dbAgent } from '@/agents/database-agent'; type Message = InferAgentUIMessage<typeof dbAgent>; export default function DBChat() { const { messages, input, handleInputChange, handleSubmit, addToolApprovalResponse } = useChat<Message>({ api: '/api/agent', sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses, }); return ( <div> {messages.map(m => ( <div key={m.id}> {m.content} {m.toolInvocations?.map(inv => ( inv.state === 'approval-requested' ? ( <div key={inv.id}> <p>Approve {inv.toolName}?</p> <button onClick={() => addToolApprovalResponse({ id: inv.approval.id, approved: true })}> Approve </button> <button onClick={() => addToolApprovalResponse({ id: inv.approval.id, approved: false })}> Deny </button> </div> ) : null ))} </div> ))} <form onSubmit={handleSubmit}> <input value={input} onChange={handleInputChange} /> </form> </div> ); }

Pattern 4: Cache Components with AI

hljs tsx
// app/page.tsx import { Suspense } from 'react'; import { ChatInterface } from '@/components/chat-interface'; // Cached static content async function ProductInfo({ productId }: { productId: string }) { 'use cache'; cacheLife('hours'); cacheTag(`product-${productId}`); const product = await db.products.findUnique({ where: { id: productId } }); return <div>{product.name} - ${product.price}</div>; } // Dynamic chat (not cached) export default function ProductPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; // Async params in Next.js 16 return ( <div> {/* Static - cached and prerendered */} <Suspense fallback={<div>Loading product...</div>}> <ProductInfo productId={id} /> </Suspense> {/* Dynamic - streams in */} <Suspense fallback={<div>Loading chat...</div>}> <ChatInterface productId={id} /> </Suspense> </div> ); }

Breaking Changes Checklist

Next.js 16 Breaking Changes

  • Async Params: Update params and searchParams to use await

    hljs typescript
    // Before (Next.js 15) export default function Page({ params }: { params: { id: string } }) { return <div>{params.id}</div>; } // After (Next.js 16) export default async function Page({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; return <div>{id}</div>; }
  • Image Changes: Default quality now 75, localPatterns required for query strings

  • Node.js 20+: Minimum version is now 20.9.0

AI SDK v6 Breaking Changes

  • Use createAgentUIStreamResponse for agent routes (instead of toDataStreamResponse)
  • Use convertToModelMessages to transform UI messages for agents
  • Add needsApproval: true to tools requiring confirmation

Reference Documentation

For detailed API reference and patterns, see:

  • references/nextjs16-api.md - Complete Next.js 16 API reference
  • references/ai-sdk-v6-api.md - Complete AI SDK v6 API reference
  • references/patterns.md - Advanced patterns and workflows
  • references/migration.md - Detailed migration guides

Common Issues & Solutions

Issue: "params is not a Promise"

Solution: Wrap params access in async function with await

Issue: Tool loops not working

Solution: Use ToolLoopAgent instead of manual maxSteps in streamText

Issue: Cache not invalidating

Solution: Add proper cacheTag() and call revalidateTag() or updateTag()

Issue: Approval UI not showing

Solution: Check toolInvocations array and state === 'approval-requested'

Best Practices

  1. Use ToolLoopAgent for any multi-step tool workflows
  2. Wrap dynamic content in Suspense for PPR benefits
  3. Use 'use cache' explicitly - nothing is cached by default in Next.js 16
  4. Add needsApproval to destructive operations (delete, update, send)
  5. Use TypeScript with InferAgentUIMessage for type-safe agent UIs
  6. Pin beta versions to avoid breaking changes in patches

Related Categories