✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

Ecosystem15 min read

How to Create an AI Chatbot with Vercel AI SDK and Strapi

November 10, 2025Updated on June 14, 2026
How to Create an AI Chatbot

You launch a chatbot, ship the code, and three days later a stakeholder asks to swap "Hello" for "Hey there!" in the welcome message. Because that greeting is hard-coded, you push a commit, wait through a 3-minute build, redeploy, then test the change—only to repeat this when legal wants a new disclaimer.

Repeat this across a week and you've burned hours on copy edits instead of features.

The solution is separating concerns: keep conversational logic in your Next.js repo while moving all mutable text—and the bot's personality—to a headless CMS. Pairing the streaming power of the Vercel AI SDK with Strapi's real-time content APIs creates an architecture where non-developers update responses instantly and you focus on code that matters.

In Brief

  • Separate your concerns by keeping conversational logic in Next.js while storing mutable content in a headless CMS to eliminate repetitive deployments for text changes.
  • Empower non-developers to update bot responses and personality instantly through a content management interface without touching code.
  • Leverage streaming capabilities of the Vercel AI SDK to deliver responsive, token-by-token chat experiences with minimal latency.
  • Build a maintainable architecture that scales independently, letting you focus on core features instead of content updates.

Prerequisites

Before you wire up APIs or tweak prompts, confirm your environment checks out. Each requirement is familiar territory for experienced developers, but skipping any one of them will slow you down later.

  • Node.js 18+ as the runtime for both Strapi and your local Vercel dev workflow. Install it system-wide so scripts run consistently across your development process.
  • Next.js knowledge becomes essential here—you'll build the chat UI and server actions that communicate with the SDK.
  • Vercel account setup for environment variables, edge functions, and zero-config deploys. After signing in, review the AI SDK docs to understand the integration patterns you'll implement.
  • Strapi 5 familiarity pays off when you model content and expose it via REST or GraphQL—the integration guide walks through the complete setup process.
  • Basic AI/LLM concepts like prompts, tokens, and provider APIs. The SDK handles the complexity, but reading the introduction helps you craft effective system messages that deliver the responses your users expect.

Setting Up Your Project

Create a TypeScript-ready Next.js app and install the SDK:

npx create-next-app@latest ai-chatbot --typescript
cd ai-chatbot
npm install ai        # Vercel AI SDK

The generator outputs:

✔ Project initialized   Created ai-chatbot/
✔ Typescript enabled    tsconfig.json generated
✔ Next steps            cd ai-chatbot && npm run dev

The SDK's server helpers and React hooks are exposed from the ai package. Create a .env.local file at your project root:

OPENAI_API_KEY=pk_live_***
STRAPI_URL=http://localhost:1337

Vercel automatically injects these variables during deployment.

Your folder structure should look like this:

ai-chatbot/
 ├─ pages/
 │   └─ api/
 │      └─ chat.ts        # your streaming route
 ├─ components/
 │   └─ Chat.tsx          # React UI
 ├─ .env.local
 ├─ tsconfig.json
 └─ package.json

TypeScript catches mismatched message shapes and missing environment variables before runtime. You now have a live-reload dev server (npm run dev), typed endpoints, and a flexible foundation ready for Strapi content integration. The SDK's pluggable model layer allows you to swap AI providers as needed.

Integrating the Vercel AI SDK

The package includes server-side helpers for Next.js API routes and client-side hooks that handle state management automatically. Create a chat API route that streams model output in real time:

// app/api/chat/route.ts
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import type { Message } from "ai";

export async function POST(req: Request) {
  const { messages } = (await req.json()) as { messages: Message[] };

  // streamText returns a ReadableStream so the client sees tokens instantly
  const response = await streamText({
    model: openai("gpt-4o-mini"), // swap to "anthropic:claude" or any other provider
    messages,
    maxTokens: 800,
  });

  // Next.js converts the stream to a proper response object
  return response.toDataStreamResponse();
}

streamText handles chunking and HTTP headers automatically. The browser receives tokens as the model produces them, reducing perceived latency through streaming responses.

The useChat hook manages input, message history, loading states, and errors:

// components/Chat.tsx
"use client";

import { useChat } from "ai/react";

export default function Chat() {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    isLoading,
    error,
  } = useChat({ api: "/api/chat" });

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <ul>
        {messages.map((m) => (
          <li key={m.id} className={m.role === "user" ? "text-right" : ""}>
            {m.content}
          </li>
        ))}
      </ul>

      <input
        value={input}
        onChange={handleInputChange}
        placeholder="Ask me anything…"
        className="w-full border p-2"
      />

      {isLoading && <p className="text-sm">Generating…</p>}
      {error && <p className="text-sm text-red-600">{error.message}</p>}
    </form>
  );
}

useChat posts to your route, appends streamed tokens, and handles transient failures. You can extend it with callbacks like onFinish for analytics or onError for custom logging.

Provider selection uses string identifiers in the format 'provider/model' (e.g., 'openai/gpt-5'), so switching models requires only an environment variable change. No code modifications needed.

To test your integration locally, set your OPENAI_API_KEY in .env.local and run npm run dev. Navigate to /chat and type a message—you should see words appear token by token. If nothing renders, check the Network tab. Most issues stem from missing API keys or CORS configuration.

Deploy to production with vercel --prod. The platform automatically injects environment variables and scales your serverless function to handle traffic spikes. When the model encounters errors (rate limits, context length exceeded), the hook exposes them via the error property. This lets you display graceful fallbacks instead of silent failures.

With fewer than fifty lines of code, you have a functional, provider-agnostic streaming foundation.

Connecting AI Chatbot to Strapi

Your assistant needs dynamic content that updates without redeployment. Strapi provides an API layer for the knowledge your content team maintains, letting you query current information in milliseconds.

Start by spinning up a local Strapi instance:

npx create-strapi-app@latest acme-chatbot-cms --quickstart

The --quickstart flag uses SQLite for local development. Switch to PostgreSQL for production to handle better concurrency and backups. The Strapi × Next.js integration guide covers database migration in detail.

Once the Admin Panel opens, create a Collection Type called FAQ with four fields:

  1. question (Text)
  2. answer (Rich Text)
  3. category (Enumeration)
  4. tags (UID or Repeatable Component)

This schema optimizes for semantic matching—question and answer feed embeddings while category and tags filter results. Strapi auto-saves changes, so content editors iterate without touching Git.

Enable API access by navigating to Settings → Roles & Permissions and checking find and findOne on faq for the Public role. Keep write actions disabled to protect your data while exposing read-only endpoints.

Test the autogenerated REST endpoint:

curl https://your-strapi-url.com/api/faqs?populate=*&filters[category][$eq]=general

Expect this response structure in Strapi 5:

{
  "data": [
    {
      "id": 12,
      "documentId": "abc123xyz",
      "question": "How do I reset my password?",
      "answer": "Click \"Forgot password\" on the login screen and follow the email instructions.",
      "category": "account",
      "tags": ["security", "account"],
      "createdAt": "2024-03-06T13:42:05.098Z",
      "updatedAt": "2024-03-06T13:42:05.098Z",
      "publishedAt": "2024-03-06T13:42:05.103Z",
      "locale": "en"
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "pageSize": 25,
      "pageCount": 1,
      "total": 42
    }
  }
}

To whitelist your Vercel app origin for CORS in Strapi, use the following configuration in ./config/middlewares.js and remove 'enabled: true':

module.exports = [
  'strapi::logger',
  'strapi::errors',
  'strapi::security',
  {
    name: 'strapi::cors',
    config: {
      origin: ['https://your-vercel-app.vercel.app'],
      methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
      headers: ['Content-Type', 'Authorization', 'Origin', 'Accept'],
    },
  },
  'strapi::poweredBy',
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];

Create a utility function that fetches content and handles empty results:

// lib/getFaq.ts
export async function getFaq(query: string) {
  const url = new URL('/api/faqs', process.env.STRAPI_URL);
  url.searchParams.append('filters[question][$containsi]', query);
  url.searchParams.append('populate', '*');

  const res = await fetch(url.href, { headers: { Authorization: `Bearer ${process.env.STRAPI_TOKEN}` } });

  if (!res.ok) throw new Error(`Strapi error: ${res.status}`);
  const { data } = await res.json();
  return data.length ? data[0].attributes.answer : null;
}

Integrate the retrieved answer into your conversation workflow:

// app/api/chat/route.ts
import { streamText } from 'ai';
import { getFaq } from '@/lib/getFaq';

export async function POST(req: Request) {
  const { messages } = await req.json();
  const question = messages.at(-1)?.content ?? '';

  const canned = await getFaq(question);
  const system = canned
    ? `[Use the following answer when relevant:\n\n$](https://strapi.io/blog/eight-headless-cms-use-cases){canned}`
    : 'Answer conversationally.';

  return streamText({
    model: 'openai:gpt-4o-mini',
    messages: [{ role: 'system', content: system }, ...messages],
  }).toDataStreamResponse();
}

Your assistant now pulls live answers from Strapi. Content editors update copy through the CMS while you focus on features instead of content deployments.

Supercharging Your AI Chatbot with Strapi AI

Strapi AI introduces powerful capabilities that can transform your chatbot project from basic FAQ handling to an intelligent content delivery system. Let's explore how to integrate these advanced features.

Generate Content Types with Natural Language

Instead of manually defining your FAQ structure, use Strapi AI to generate it through conversation:

  1. Ensure you have Strapi version 5.26 or higher with a valid Growth plan license
  2. Navigate to the Content-Type Builder in the Admin Panel
  3. Click the Strapi AI icon in the bottom-right corner
  4. Use this prompt to generate a complete chatbot content structure:
Create content types for a customer service chatbot with FAQ categories, response templates, conversation flows, and feedback tracking

Strapi AI will generate a comprehensive schema including:

  • FAQ collections with appropriate fields
  • Response templates with variable placeholders
  • Conversation flow models with conditional paths
  • User feedback tracking

This approach creates a more sophisticated content structure than our basic example, with proper relationships and optimized field types—all without writing a single line of schema code.

Import from Existing Project

If you've already built a frontend with hardcoded responses, you can reverse-engineer your content model:

  1. Export your Next.js project as a ZIP file
  2. In Strapi AI, click "Import from computer" and upload the ZIP
  3. The AI will analyze your code to identify:
  • Hardcoded messages and responses
  • UI components displaying content
  • Data structures and types
  1. Review and confirm the generated content types

This approach bridges the gap between your existing frontend and a proper CMS-backed solution, accelerating the transition to dynamic content.

Enhance Your FAQ Collection

Once your base structure is created, use Strapi AI to improve it with follow-up prompts:

Add a markdown rich text field called 'additionalContext' to the FAQ
Create an SEO component with meta fields and add it to all content types
Organize the response templates with a blocks dynamic zone for flexible content

These improvements make your content structure more robust and flexible, supporting rich formatting, SEO optimization, and modular content building—all critical for delivering high-quality chatbot responses.

Building Intelligence Into Your Chatbot

You already have a working chat endpoint, but real value comes when the bot feels genuinely informed and remembers what's happening in the conversation. Let's add that intelligence layer by combining Strapi-managed content with thoughtful prompt controls.

Context-Aware Content Retrieval

Users rarely phrase questions exactly as they appear in your knowledge base. Your first task is finding the right Strapi entry even when wording differs. Start simple with text matching—query Strapi's REST endpoint using a case-insensitive contains filter—then evolve to semantic search when you need deeper understanding.

// lib/semanticSearch.ts
import { embed, similaritySearch } from "@pinecone-database/client";
import { fetchArticles } from "./strapi"; // wraps Strapi REST call

export async function semanticSearch(query: string) {
  // 1. Embed the incoming query
  const queryVector = await embed(query);

  // 2. Retrieve the top 3 semantically similar pieces from Pinecone
  const matches = await similaritySearch(queryVector, { topK: 3 });

  // 3. Fallback to direct Strapi search if no vector match
  if (matches.length === 0) {
    return fetchArticles({ filters: { question: { $containsi: query } } });
  }
  return matches.map((m) => m.metadata.text);
}

Once you have candidate snippets, inject them into a system prompt:

const system = `
You are a helpful assistant. Base your answer strictly on the context below.
Context:
${context.join("\n\n")}
`;

Watch your token limits. Cap the concatenated context at 2,000 tokens and let the LLM decide what matters. For performance, cache successful Strapi lookups in memory or Redis. Vercel Edge cache works well for read-heavy FAQ data, giving you microsecond retrievals close to users.

If no content surfaces, send a concise apology and route the query directly to the model—never return empty strings that could confuse the prompt. When multiple pieces match, rank by semantic score, then by recency. This surfaces the most current answer without extra manual curation.

Query Understanding and Context Management

Retrieving good data solves only half the problem; your assistant also needs to carry on coherent conversations. The useChat hook keeps message state on the client, but you decide how much history to forward to the model. A sliding window of the last six user/assistant pairs balances relevance and cost. Older messages get truncated:

function trimHistory(messages: Message[], limit = 12) {
  return messages.slice(-limit);
}

System prompts deserve equal care. A minimal template includes role, tone, and capabilities:

const baseSystem = `
You are StrapiBot, an expert on our headless CMS.
Answer in under 120 words and cite internal IDs when relevant.
`;

Before each request, merge baseSystem, the retrieved Strapi context, and the trimmed history. Intent classification decides whether you even need the database lookup. Lightweight heuristics work: if the user message contains "how", "what", or "why" plus a Strapi keyword, fetch content; otherwise, let the model improvise. Refine this with embeddings or an external classifier later.

Edge cases matter. If a follow-up question references "that plugin we discussed," pull the last referenced plugin name from history and include it explicitly in the next system prompt. For ambiguous queries, ask clarifying questions rather than guessing—misinformation erodes trust faster than a short delay.

Stream responses (streamText) so users see progress instantly. The SDK's default error handling retries transient network hiccups, but you should still catch 4xx or 5xx responses from Strapi and surface polite, actionable messages.

By layering smart retrieval, disciplined prompt construction, and careful state management, you transform a basic chat interface into a context-aware assistant that feels tailored to every user while staying maintainable in both code and content.

Testing and Deployment

Before you push to production, test like your reputation depends on it. A tight feedback loop catches logic gaps, API failures, and prompt-injection surprises before real users encounter them.

Run through this checklist every time you iterate:

  • Edge-case phrasing: slang, typos, multi-sentence prompts
  • Follow-up questions that rely on previous context
  • Queries that must map to Strapi content—and ones that should bypass it
  • Failure conditions: Strapi offline, bad API key, model timeout

When something breaks, start with local logs, then reproduce in a Vercel preview deployment. Vercel surfaces request traces and streamed responses in its dashboard, so you can inspect every token the model returns. If the issue starts in your content layer, Strapi's detailed 4xx/5xx responses make the root cause obvious.

Deploying is straightforward. Commit to GitHub, run vercel once to link the project, and every subsequent push spins up a preview URL. Promote a tested commit to production from the Vercel UI or via protected branches.

Environment variables (OPENAI_API_KEY, STRAPI_URL, etc.) live in the Vercel dashboard. Missing vars cause most "works on my machine" bugs, so validate them in both Preview and Production scopes.

Next, lock down access. Configure Strapi to only accept traffic from your frontend origin:

// config/middleware.js
module.exports = [
  {
    name: 'strapi::cors',
    config: {
      origin: ['https://your-vercel-app.vercel.app'],
      methods: ['GET', 'POST'],
      headers: ['Content-Type', 'Authorization'],
      credentials: true,
    },
  },
];

Finally, protect your wallet and uptime with rate limiting. For production use, implement a distributed rate limiter—such as one backed by Redis or a dedicated service—rather than a simple in-memory Map, to prevent abusive clients from hammering model APIs reliably across all instances.

Taking It Further with Strapi AI

The Strapi AI Media Library can supercharge your chatbot with intelligent content processing. Here's how to incorporate these premium capabilities:

Automated Media Enhancement

For chatbots that handle image uploads or provide visual responses:

  1. Configure Strapi AI Media Library to automatically:
  • Generate alt-text for all images
  • Create descriptive captions
  • Apply intelligent tagging
  1. Use the tagged media in your chat responses for more contextually relevant answers:
async function getRelevantMedia(query) {
  const url = new URL('/api/media-library', process.env.STRAPI_URL);
  url.searchParams.append('filters[tags][$containsi]', query);

  const res = await fetch(url.href);
  return res.json();
}

// In your chat route
const relevantMedia = await getRelevantMedia(question);
if (relevantMedia.length) {
  system += `\nRefer to this image if relevant: ${relevantMedia[0].url}`;
}

This allows your chatbot to provide visual examples alongside text responses, creating a richer user experience with minimal development effort.

Future-Proofing Your Chatbot

As Strapi AI evolves, your chatbot can take advantage of upcoming features:

  1. AI Translation: When internationalization support releases, your chatbot will automatically respond in the user's language without code changes.
  2. Intelligent Tagging: Leverage automatic content categorization to improve retrieval accuracy.
  3. AI Content Generation: Enable dynamic response creation based on user queries that don't match existing content.

To prepare for these capabilities:

// Extensible retrieval function that can incorporate new AI features
async function getContent(query, options = {}) {
  const { language = 'en', useAIGeneration = false } = options;
  const url = new URL('/api/content', process.env.STRAPI_URL);

  // Standard filters
  url.searchParams.append('filters[question][$containsi]', query);
  url.searchParams.append('locale', language);

  // Flag for AI-enhanced responses when available
  if (useAIGeneration) {
    url.searchParams.append('ai-generate', 'true');
  }

  const res = await fetch(url.href);
  return res.json();
}

This future-proof approach ensures your chatbot can easily adopt new Strapi AI capabilities as they become available.

Ready to Ship Your Intelligent Chatbot

You've solved the original problem: no more fragile, hard-coded responses that break every time content changes. Your assistant now updates itself the moment someone edits content in Strapi. Deployments no longer gatekeep simple copy changes, and stakeholders can safely adjust tone or FAQs without touching your codebase.

Streaming responses keep conversations fast, while Strapi's API-driven architecture maintains clean separation of concerns. Both services scale independently on their own infrastructure.

What's next? Explore the Retrieval-Augmented template on Vercel's marketplace for deeper knowledge grounding. Add multilingual support with Strapi's i18n plugin. Check out the community resources around Strapi + Vercel integration for advanced patterns.

Your foundation handles the basics—now you can iterate on user experience, measure conversation quality, and build more sophisticated AI applications.

Paul BratslavskyDeveloper Advocate

Related Posts

Strapi AI
Product·5 min read

Strapi AI is now Generally Available, DX Upgrades and Cloud Improvements!

Today, we are excited to announce several new features and improvements, with a focus on enhancing developer...

·October 8, 2025
Strapi AI translations
Product·2 min read

Shipping Faster with Strapi AI Translations & More Homepage Customizations

Automate Multilingual Content with Strapi AI Translations Ship multilingual content faster with Strapi AI Translations....

·November 3, 2025
Tutorials·24 min read

Content Modeling in Strapi 5: Build a Page Builder with Strapi AI and v0

Introduction Structuring content is hard when you’re starting a site or app, whether it’s a company website, an...

·September 11, 2025