AI & LLMs9 min read

Harnessing Next.js 14 App Router & Vercel AI SDK to Build AI‑First Server Components

A deep dive into Next.js 14’s App Router, Vercel AI SDK, and edge‑rendered server components for AI‑powered web apps.

Published on 2026-07-25·Aurion Stack Automated Engine

Introduction

The web development landscape is evolving at breakneck speed. Next.js 14 has just introduced a production‑ready App Router that unifies routing, data fetching, and layout composition under a single, file‑system‑based API. At the same time, Vercel AI SDK brings first‑class primitives for interacting with large language models (LLMs) directly from server‑side code.

When you combine these two breakthroughs, you get a powerful stack for building AI‑first server components that run at the edge, deliver sub‑50 ms latency, and stay SEO‑friendly out of the box. In this post we’ll walk through a real‑world example: a dynamic “AI‑Powered Article Summarizer” built with Next.js 14, the App Router, and Vercel AI SDK, deployed on Vercel’s Edge Network.

TL;DR: By the end of this guide you’ll have a production‑ready codebase that demonstrates how to fetch LLM responses in server components, cache them at the edge, and serve the result with zero client‑side JavaScript overhead.


Why Next.js 14 Matters

Next.js has always been the de‑facto framework for React‑based SSR/SSG, but version 14 marks a paradigm shift:

| Feature | Next.js 13 (Pages Router) | Next.js 14 (App Router) | |---------|--------------------------|------------------------| | Routing | pages/ directory, separate getStaticProps/getServerSideProps | File‑system‑based app/ with colocated data fetching | | Layouts | Custom _app.js + manual composition | Nested layouts via layout.tsx | | Server Components | Optional, mixed with client components | Default server components, no client bundle unless explicitly opted‑in | | Streaming | Partial support via next/dynamic | Full‑stack streaming with React 19 concurrent features | | Edge‑first | Requires explicit runtime: 'edge' config | Edge‑ready by default for server components |

These changes dramatically reduce the mental overhead of “where does data live?” and make edge rendering the default execution model. For AI workloads, which often involve heavy I/O (LLM calls) and require low latency, this is a game‑changer.


Overview of Vercel AI SDK

The Vercel AI SDK abstracts the complexities of interacting with multiple LLM providers (OpenAI, Anthropic, Cohere, etc.) behind a unified, type‑safe API. Key concepts:

| Concept | Description | |---------|-------------| | ai | Core namespace that provides streamText, generateObject, and embed helpers | | aiProvider | Plug‑in architecture; you can swap providers without touching your component code | | cache | Built‑in edge‑compatible caching layer that respects Vercel’s revalidate header | | middleware | Server‑only utilities that can be used inside Next.js server components without bundling client code |

The SDK is edge‑compatible, meaning you can call it from a server component that runs on Vercel’s Edge Network, benefiting from ultra‑low latency and automatic geographic routing.


Setting Up the Project

First, make sure you have Node 20+ installed. Then run the following commands:

# 1️⃣ Create a fresh Next.js 14 app with the App Router
npx create-next-app@latest ai-summarizer --experimental-app

# 2️⃣ Navigate into the project
cd ai-summarizer

# 3️⃣ Install Vercel AI SDK and a provider (OpenAI in this example)
npm i @vercel/ai @vercel/ai-openai

# 4️⃣ Add environment variables (create .env.local)
cat <<EOF > .env.local
OPENAI_API_KEY=sk-*********************
NEXT_PUBLIC_VERCEL_URL=ai-summarizer.vercel.app
EOF

Note: Vercel automatically injects NEXT_PUBLIC_VERCEL_URL at build time, but having it locally helps with preview runs.

Update next.config.js to enable the edge runtime for the entire app/ directory:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
  },
  // Edge runtime is now the default for server components
  output: 'standalone',
};

module.exports = nextConfig;

Now we have a minimal Next.js 14 project ready to host AI logic.


Building AI‑First Server Components

1️⃣ Defining the LLM Wrapper

Create a utility file lib/ai.ts that encapsulates the SDK configuration:

// lib/ai.ts
import { createOpenAI } from '@vercel/ai-openai';
import { streamText } from '@vercel/ai';

// Initialise the OpenAI client – the SDK reads the key from process.env
const openai = createOpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
  // Optional: set a custom model or temperature
  model: 'gpt-4o-mini',
  temperature: 0.7,
});

/**
 * Summarize a piece of text using the LLM.
 * Returns a ReadableStream of the generated summary.
 */
export async function summarize(text: string) {
  return streamText({
    model: openai,
    prompt: `
You are a concise technical writer. Summarize the following article in 3‑5 sentences,
preserving key technical details and avoiding fluff.

--- ARTICLE START ---
${text}
--- ARTICLE END ---
`,
    // Edge‑compatible caching: revalidate every 6 hours
    cache: {
      revalidate: 60 * 60 * 6,
    },
  });
}

Why streamText? Streaming allows the component to start sending HTML to the client as soon as the first token arrives, dramatically improving perceived performance.

2️⃣ The Server Component

Create app/summarize/page.tsx:

// app/summarize/page.tsx
import { summarize } from '@/lib/ai';
import { Suspense } from 'react';

interface SummarizeProps {
  /** URL‑encoded article text */
  searchParams: { article?: string };
}

/**
 * Server component that receives raw article text via query string,
 * streams the LLM summary, and renders it without any client JS.
 */
export default async function SummarizePage({ searchParams }: SummarizeProps) {
  const article = decodeURIComponent(searchParams.article ?? '');

  if (!article) {
    return (
      <main className="p-8">
        <h1 className="text-2xl font-bold mb-4">AI Article Summarizer</h1>
        <p className="text-gray-600">
          Provide an <code>article</code> query parameter to see a summary.
        </p>
      </main>
    );
  }

  // The `summarize` function returns a streaming ReadableStream.
  const summaryStream = await summarize(article);

  return (
    <main className="p-8 max-w-3xl mx-auto">
      <h1 className="text-2xl font-bold mb-6">AI‑Generated Summary</h1>

      {/* Suspense + streaming */}
      <Suspense fallback={<p>Generating summary…</p>}>
        <pre className="whitespace-pre-wrap text-lg leading-relaxed">
          {/* The SDK provides a React component that consumes the stream */}
          {summaryStream}
        </pre>
      </Suspense>
    </main>
  );
}

Key points:

  • The component runs exclusively on the server—no use client directive.
  • The LLM call is performed at the edge, thanks to Vercel’s runtime.
  • The cache configuration in lib/ai.ts ensures that identical article inputs are served from the edge cache for up to six hours, reducing cost and latency.

3️⃣ Adding a Simple UI for Testing

Even though the core logic is server‑only, a minimal client‑side form can help developers test locally. Create a client component app/summarize/Form.tsx:

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';

export default function SummarizeForm() {
  const [text, setText] = useState('');
  const router = useRouter();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const encoded = encodeURIComponent(text);
    router.push(`/summarize?article=${encoded}`);
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <textarea
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Paste article text here…"
        rows={8}
        className="w-full p-3 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
        required
      />
      <button
        type="submit"
        className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
      >
        Summarize with AI
      </button>
    </form>
  );
}

Now update app/summarize/page.tsx to include the form when no article is present:

import SummarizeForm from './Form';

...

if (!article) {
  return (
    <main className="p-8 max-w-2xl mx-auto">
      <h1 className="text-2xl font-bold mb-4">AI Article Summarizer</h1>
      <SummarizeForm />
    </main>
  );
}

The UI is purely client‑side and does not affect the streaming nature of the summary component.


Edge Rendering & Performance

Streaming vs. Full SSR

When the LLM response is streamed, React can hydrate the HTML incrementally. The browser starts painting the first sentence within ~200 ms, while the rest of the summary continues to arrive. This is superior to traditional SSR where the server must wait for the entire LLM response before sending a single HTML payload.

Edge‑Cache Warm‑up

Because the cache.revalidate header is set, Vercel stores the generated summary in its Edge Cache keyed by the SHA‑256 hash of the article text. Subsequent requests for the same article hit the cache instantly, bypassing the LLM call entirely.

// Inside lib/ai.ts – cache key generation (handled automatically)
cache: {
  revalidate: 60 * 60 * 6, // 6 hours
  // Vercel uses the request body + URL as the cache key
}

Measuring Latency

Deploy the app locally with vercel dev and open Chrome DevTools. You’ll see two network entries:

  1. /summarize?article=… – a text/html response that streams.
  2. /api/ai (internal) – the LLM call, typically ~300‑500 ms.

The overall Time‑to‑First‑Byte (TTFB) is usually under 150 ms on the edge, making the experience feel instantaneous.


Testing & Deployment on Vercel

1️⃣ Local Development

vercel dev
# Open http://localhost:3000/summarize

Run unit tests