Migrating
Coming from shadcn / ShipFast / Chatbot UI / vanilla Next.
docs/MIGRATING.mdAgentKit is a landing-page kit for AI agent products. It's a copy-paste codebase, not a package —
you own every file. This guide covers upgrading from AgentKit v1.x, then porting from four common starting points: shadcn/ui,
ShipFast, Chatbot UI / a hand-rolled AI SDK app, and vanilla create-next-app.
AgentKit v2.1.0 targets Next.js 16, React 19.3, Tailwind v4, and AI SDK 7 (v2.0.0 used the previous major).
Dependencies are locked in package.json.
v2.0 → v2.1
v2.1 moves the kit to AI SDK 7 and turns the hero chat into a real agent loop. Nothing in site.config.ts has to change: every new field is optional, and an unchanged v2.0 config still validates (the hero keeps playing its two-message demo.heroChat).
What changed
- Node 22.12 or newer.
engines.nodeis>=22.12.0(AI SDK 7 needs Node 22; the test toolchain needs 22.12). Update your runtime and CI before you upgrade. - AI SDK 7.
ai,@ai-sdk/openai,@ai-sdk/anthropicand@ai-sdk/reactmove to their v7-era majors. Readnode_modules/ai/docs/before changing AI code. - A third provider, OpenRouter.
ai.provider: 'openrouter'withOPENROUTER_API_KEY. - New optional
aifields:allowedModels,fallbackModels,reasoning,maxSteps,allowClientSystemPrompt,maxOutputTokens. - New optional
demo.heroRun: the scripted agent run (reasoning, tool steps, approval card, run footer). Without it the hero playsdemo.heroChat. - Tools moved to
src/lib/demo-tools.ts(list_deploys,search_logs,rollback_deploy), with an approval policy indemoToolApproval. - Optional
TOOL_APPROVAL_SECRETsigns tool approvals. Set it in production. - "Try it live" panels on five component pages, and the
agent-playgroundstarter runs the real agent.
If you added your own components
v2.1 also moves three UI dependencies to new majors. The kit's own files are already updated; code you wrote against v2.0 needs these changes.
framer-motion → motion. The package is now motion; import from motion/react. The API is the same.
// v2.0
import { AnimatePresence, motion } from 'framer-motion'
// v2.1
import { AnimatePresence, motion } from 'motion/react'
lucide-react 1 removed its brand icons (Github, Twitter and the rest). Use the inline SVGs in @/components/icons/brand, which take the same size / className props.
// v2.0
import { Github, Twitter } from 'lucide-react'
<Github size={16} />
// v2.1
import { GithubIcon, XIcon } from '@/components/icons/brand'
<GithubIcon size={16} />
zod 4. Most schemas work unchanged; error customisation and a few helpers changed (for example message becomes error, and string formats such as z.string().email() become z.email()). See the zod 4 migration guide.
// v2.0
z.string().email({ message: 'Enter an email' })
// v2.1
z.email({ error: 'Enter an email' })
If you customised the chat route
If you edited src/app/api/chat/route.ts (your own tools, a different provider, custom streaming), re-apply your changes on top of the v2.1 route rather than overwriting it, and check them against the AI SDK 7 docs: the route now runs a tool loop with approvals, builds the response with createUIMessageStreamResponse, and carries run metadata (model, tokens, cost) on each message. Move your tools into src/lib/demo-tools.ts, and list any tool that changes state in demoToolApproval.
v1.x → v2.0
v2.0 moves everything product-specific out of the components and into one file, site.config.ts. v1.8.0 stays downloadable from your original purchase email, so you can finish a v1 launch and move later.
What moved
- Copy that used to live in section files (
hero.tsx,pricing.tsx,faq.tsx, and so on) is now in the matchingsite.config.tsfield:hero,features,agentTrace,toolCalling,pricing,socialProof,faq,finalCta,footer. lib/product.tsis removed. Name, URLs, description and the primary CTA areproduct,seoandoginsite.config.ts.- Demo scripts (the hero chat exchange, the agent trace, the tool-call demo) are
demo.heroChat,demo.traceanddemo.toolCallinsite.config.ts.src/lib/demo-agent.tskeeps only the streaming-card text. - Sections take
contentprops. They no longer hold copy and never import the config;src/app/page.tsxpasses each one its field. - Live chat is configured by
aiinsite.config.ts(OpenAI or Anthropic), withAI_PROVIDER/AI_MODELoverrides. You no longer swap an import inroute.ts. Production still needs Upstash Redis. public/llms.txtis gone./llms.txtis generated from thellmsblock bysrc/app/llms.txt/route.ts.AGENTS.md,.cursor/rules/agentkit.mdcandprompts/rebrand.mdship with the kit.AGENTS.mdis now yours to edit.showcaseinsite.config.tsturns the kit's catalog pages (/components,/sections,/docs,/starters) on or off. Set it tofalsebefore you deploy.
If you customised v1
Moving is a copy job, not a rewrite:
- Start from a fresh v2.0 download in a new folder, so you keep your v1 project intact.
- Copy your edited strings from the v1 section files into the matching
site.config.tsfields. Or pasteprompts/rebrand.mdinto your AI assistant with your v1 copy as the brief. - Move your hero chat, trace and tool-call scripts from
demo-agent.tsintodemo. - Copy over your brand tokens and fonts from
src/app/globals.cssandsrc/app/layout.tsx. - Copy your tools from
src/app/api/chat/route.ts, and setai.provider,ai.modelandai.systemPrompt. - If you changed section markup, re-apply those changes on top of the new props-based components in
src/components/sections/. - Set
showcase: false, then runbun run typecheck && bun run lint && bun run test && bun run build. The config validator names any field it rejects.
1. From shadcn/ui
If you've used shadcn/ui before, AgentKit will feel instantly familiar — same CVA variants, same Radix primitives where installed, same copy-paste philosophy.
What's the same
cn()helper. Identical signature, sameclsx + tailwind-mergeimplementation (src/lib/utils.ts).- CVA +
asChild.Buttonusesclass-variance-authorityand@radix-ui/react-slotexactly like shadcn.<Button asChild><Link href="/x">Go</Link></Button>works the same. - Radix under the hood for
Dialog,Tabs,Tooltip, andAccordion(@radix-ui/react-dialog,-tabs,-tooltip,-accordioninpackage.json). - Card / Button API.
<Card>,<CardHeader>,<CardBody>and<Button variant="primary|secondary|ghost" size="sm|md|lg">behave the way you expect. - Copy-paste model. Every component in
src/components/ui/is a single file with no barrel-only dependencies — delete what you don't use.
What's different
- Popover and Toast are pure React. We ship
src/components/ui/popover.tsxandsrc/components/ui/toast.tsxwithout Radix. If you want the Radix versions, swap them in; ourPopoverTrigger/PopoverContent/toast()/<Toaster />API is deliberately close to shadcn's so imports are a one-line change. - Theming via
data-theme, not.dark. Three themes (editorial-dark,bright,cool-blue) insrc/app/globals.cssall live under[data-theme="..."]selectors on<html>. shadcn's.darkclass toggle works too, but you'll want to consolidate to one model. - CSS vars are hex / rgba, not HSL triplets. shadcn uses
--primary: 222.2 47.4% 11.2%withhsl(var(--primary)). We use--color-brand: #c6f432directly in the class:bg-[var(--color-brand)]. Nohsl()wrapper. - No CLI. You get the full source in the ZIP. There's no
npx shadcn add button— it's already there. - 43 AI-native components shadcn doesn't ship.
MessageBubble,StreamingText,AgentTraceViewer,ToolCallPanel,ReasoningTree,CostTracker,TokenCounter,DiffViewer,PromptPlayground, etc. — seesrc/components/ai/index.tsfor the full list.
Porting a shadcn button you already have
Your shadcn button probably looks like this:
// Your shadcn button.tsx
const buttonVariants = cva('...', {
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
// ...
},
},
})
AgentKit's src/components/ui/button.tsx uses the same pattern, but with CSS vars directly:
// AgentKit button.tsx
const buttonVariants = cva('...', {
variants: {
variant: {
primary: 'bg-[var(--color-brand)] text-[var(--color-bg)] hover:opacity-90',
secondary: 'border border-[var(--color-border-strong)] hover:bg-[var(--color-bg-card-hover)]',
ghost: 'text-[var(--color-text-dim)] hover:text-[var(--color-text)]',
},
},
})
To port: rename your variants (default → primary, outline → secondary), and swap
bg-primary → bg-[var(--color-brand)]. The cn(buttonVariants(...)) call is identical.
Using AgentKit alongside shadcn
They coexist fine — both use cn() + tailwind-merge, and neither registers global styles
that collide. The one gotcha is theming: don't run both .dark and data-theme toggles on
the same tree. Pick one. If you keep shadcn's .dark in your app chrome, scope AgentKit
to the landing section with <section data-theme="editorial-dark">.
Checklist
- Keep your existing
src/lib/utils.ts— ourcn()is a drop-in. - Rename
bg-primary/text-foreground→bg-[var(--color-brand)]/text-[var(--color-text)]if you port our styles into your project. - Pick one theming mechanism —
.darkordata-theme— not both. - Port
src/components/ai/wholesale; these have no shadcn equivalent.
2. From ShipFast (or similar SaaS starters)
ShipFast is a full SaaS backend starter (auth, Stripe, Mongo/Postgres, email); AgentKit is a specialized landing-page kit. They don't overlap — you probably want both.
What AgentKit does NOT have
These belong in ShipFast and should stay there:
- Auth (NextAuth / Clerk / Supabase Auth)
- Payments (Stripe checkout, webhooks, customer portal)
- Database layer (Prisma, Drizzle, Mongoose)
- Transactional email (Resend, Postmark, SES)
- User dashboards, settings pages, billing UI
What AgentKit adds that ShipFast doesn't
- Live agent chat hero wired to AI SDK 7, with tool approval (
src/components/chat-hero.tsx+src/app/api/chat/route.ts). - 43 AI-native components (
src/components/ai/) includingAgentTraceViewer,ToolCallPanel,StreamingMarkdown,ReasoningTree,MultiAgentView,CostTracker. - Editorial dark design with three swappable CSS-var themes.
- A
/componentslive-docs route (src/app/components/) that previews every component. - A pre-built sections library for landing pages:
hero,streaming-features,tool-calling,agent-trace,pricing,faq,social-proof,final-cta,footerinsrc/components/sections/.
Using them together
Keep your ShipFast backend and replace only the marketing pages:
// app/(marketing)/page.tsx — previously ShipFast's landing
import { ChatHero } from '@/components/chat-hero'
import { StreamingFeaturesSection } from '@/components/sections/streaming-features'
import { ToolCallingSection } from '@/components/sections/tool-calling'
import { PricingSection } from '@/components/sections/pricing'
import { FooterSection } from '@/components/sections/footer'
export default function Marketing() {
return (
<>
<ChatHero />
<StreamingFeaturesSection />
<ToolCallingSection />
<PricingSection />
<FooterSection />
</>
)
}
Your /app/(app)/* authenticated routes stay untouched. ShipFast's middleware, Stripe
webhooks, and API routes keep working. Only (marketing) and its children swap over.
Design system conflicts
ShipFast ships with daisyUI (v1–v2) or shadcn (v3+). You can't run two design token sources on the same DOM tree and stay sane. Two strategies:
-
Scope AgentKit to the landing subtree. Put
data-theme="editorial-dark"on just the<main>of(marketing), not<html>. AgentKit's CSS vars are defined under[data-theme="editorial-dark"]insrc/app/globals.css, so they only apply to that subtree. daisyUI'sdata-themeattribute is the same mechanism — pick distinct names (editorial-darkvslight/corporate) and they don't collide. -
Commit fully to AgentKit's tokens app-wide. Replace ShipFast's CSS entrypoint with ours. You'll need to re-skin ShipFast's dashboard components using
var(--color-bg-card),var(--color-text), etc. More work, cleaner result.
Checklist
- Keep ShipFast's
/api/auth,/api/webhooks, and DB layer — don't copy ours over them. - Move ShipFast's marketing routes into an archive folder, replace with AgentKit's sections.
- Decide: scope AgentKit via
data-themeon<main>, or migrate app-wide. - Copy
src/app/api/chat/route.tsinto your ShipFast app — this is the only backend piece AgentKit ships. - Add
OPENAI_API_KEYto your existing.env.local.
3. From Chatbot UI / a custom AI SDK setup
If you already have a chat UI running with AI SDK 7, you can drop individual AgentKit components into your existing app without adopting the whole kit.
What stays the same
The AI SDK imports you already use keep working:
useChatfrom@ai-sdk/react(we use the@ai-sdk/reactrange in package.json)streamText,convertToModelMessages,tool,UIMessagefromaiopenaifrom@ai-sdk/openai,anthropicfrom@ai-sdk/anthropic- a streamed UI message response as the route handler return (we use
createUIMessageStreamResponse)
Our src/app/api/chat/route.ts is a standard AI SDK 7 tool loop plus request limits, fallback and approvals — if you already have a
chat route, yours is probably similar. Keep yours and copy only the pieces you want.
What AgentKit adds
AgentKit is the UI shell around the SDK, not a replacement for it:
MessageBubble,MessageActions,MessageThread— render one or many messages.StreamingText,StreamingMarkdown,StreamingCode,StreamingJSON,StreamingTable,StreamingForm— token-by-token rendering primitives.AgentStatus,AgentTraceViewer,ToolCallPanel,ToolCallInline,ToolResultTable,ToolResultChart— observability for agent runs.ThoughtBubble,ReasoningTree,AgentTimeline,TaskList,AgentMemoryViewer— long-running agent state.PromptPlayground,ModelSelector,TemperatureSlider,SystemPromptEditor,TokenCounter,CostTracker,DiffViewer— dev / tuning UI.ChatInput,ChatSidebar,AttachmentPreview,VoiceInputButton,CitationDrawer,SuggestedPrompts,TypingIndicator— input-side chrome.
All in src/components/ai/ — one file per component, no cross-imports except cn().
Drop-in adoption: lifting just <AgentTraceViewer> and <ToolCallPanel>
Four steps to use a single AgentKit component in your existing Chatbot UI app:
- Copy the component file from
src/components/ai/agent-trace-viewer.tsxandsrc/components/ai/tool-call-panel.tsxinto your project. - Copy the CSS-var block from
src/app/globals.css(lines 29–52 — the[data-theme="editorial-dark"]block) into your ownglobals.css. You can cherry-pick just the vars the component touches (--color-bg-card,--color-border,--color-text,--color-brand). - Copy
src/lib/utils.ts(thecn()helper) if you don't have one already. - Install the peer deps:
bun add lucide-react clsx tailwind-merge class-variance-authority
Now use it:
import { AgentTraceViewer, type TraceStep } from '@/components/ai/agent-trace-viewer'
const steps: TraceStep[] = [
{ id: '1', phase: 'thought', label: 'Parse user request' },
{ id: '2', phase: 'tool_call', label: 'search_flights', tool: 'search_flights' },
{ id: '3', phase: 'tool_result', label: 'Got 3 matches' },
{ id: '4', phase: 'response', label: 'Summarize' },
]
<AgentTraceViewer steps={steps} />
Matching the AI SDK message schema
MessageBubble takes role: 'user' | 'assistant' | 'system' | 'tool'
(MessageBubbleRole in src/components/ai/message-bubble.tsx), which matches AI SDK 7's
UIMessage.role exactly. This just works:
import { useChat } from '@ai-sdk/react'
import { MessageBubble } from '@/components/ai/message-bubble'
export function ChatView() {
const { messages } = useChat()
return (
<>
{messages.map((m) => (
<MessageBubble key={m.id} role={m.role}>
{m.parts.filter((p) => p.type === 'text').map((p) => p.text).join('')}
</MessageBubble>
))}
</>
)
}
For m.parts with type: 'tool-call', render <ToolCallInline> or <ToolCallPanel>
inside the bubble. See src/components/chat-hero.tsx for our reference wiring of the
UIMessage parts array.
Checklist
- Keep your existing
/api/chat/route.ts— ours is a reference, not a requirement. - Copy one component at a time; don't bulk-adopt.
- Lift the CSS-var block before the component, or it will render unstyled.
-
MessageBubbleRolealigns withUIMessage.role— no adapter needed. - If you were streaming plain text, try
StreamingMarkdown— same input, richer output.
4. From vanilla Next.js + Tailwind (no kit)
If you're starting from npx create-next-app and the AI SDK docs, AgentKit is the shell
you would have built yourself over a weekend. Everything here is standard Next.js 16 /
React 19 / Tailwind v4 — no proprietary runtime.
Key deltas from a fresh Next.js project
- Strict TypeScript already configured;
src/aliased to@/. - Self-hosted fonts via
@fontsource-variable/inter,@fontsource/instrument-serif,@fontsource-variable/jetbrains-mono— no Google Fonts race, no FOUC. Imports live at the top ofsrc/app/globals.css. - Three-theme CSS-var system on
data-theme(editorial-dark,bright,cool-blue) — swap with one attribute, no rebuild. - 60 pre-built components across
src/components/ui/(primitives) andsrc/components/ai/(AI-native). - 47 test files (
*.test.tsxnext to components) using Vitest + Testing Library + jsdom. - Live
/componentsdocs route atsrc/app/components/— every component previews itself with source + props table, driven bysrc/lib/components-manifest.ts. - 9 landing sections pre-composed in
src/components/sections/:hero,streaming-features,tool-calling,agent-trace,pricing,faq,social-proof,final-cta,footer.
What to port over from your scratch project
Everything that's specific to your product:
- Your auth guards / middleware
- Your database client and schema
- Your existing
/app/api/*routes (except rename our/api/chatto avoid collision) - Your domain pages (
/app/dashboard,/app/settings, etc.) - Environment variable schema (
.env.example)
Everything else (components, sections, design tokens, fonts) comes from AgentKit.
// Before (your scratch app)
import { Button } from './components/Button' // your hand-rolled one
// After
import { Button } from '@/components/ui/button'
// Same CVA + asChild API; swap <Button variant="default"> → <Button variant="primary">
Things to remove from AgentKit if you're building an app, not a landing page
AgentKit is opinionated for a marketing site. If you're building the app itself (chat product, dashboard, agent platform), remove the landing-specific parts:
- Delete
src/app/page.tsx(the landing composition). - Delete
src/components/sections/(9 files — all marketing sections). - Delete
src/components/chat-hero.tsx(it's the hero-demo variant ofMessageThread; preferMessageThread+ChatInputin a real app). - Keep
src/components/ui/(primitives you'll use anywhere). - Keep
src/components/ai/(the whole point — 48 components for agent UIs). - Keep
src/app/api/chat/route.tsas your reference route, or replace it with your own tools and system prompt. - Keep
src/app/components/if you want an internal Storybook-lite; delete if not. - Keep
src/app/globals.css— the theming system is the most reused piece.
Checklist
- Port your
middleware.ts,/api/*(exceptchat), and DB client first. - Decide: landing page, or app? If app, strip
sections/andpage.tsx. - Add
OPENAI_API_KEY, or complete the documented Anthropic provider swap (including the key guard), in.env.local. - Run
bun install && bun run dev— it should boot on port 3000 with the scripted demo visible even without a key. - Visit
/componentsto browse everything available before you start building.
Version reference
AgentKit v2.1.0 pins these versions in package.json:
| Package | Version |
|---|---|
next | 16.4.2 |
react / react-dom | 19.3.1 |
ai | ^7.0.148 |
@ai-sdk/openai | ^4.0.99 |
@ai-sdk/anthropic | ^4.0.85 |
@ai-sdk/react | ^4.0.152 |
tailwindcss | ^4.3.3 |
class-variance-authority | ^0.7.1 |
clsx | ^2.1.1 |
tailwind-merge | ^3.7.0 |
lucide-react | ^1.61.0 |
zod | ^4.6.7 |
motion | ^14.5.0 |
If your existing project pins older majors of ai or react, upgrade those before
copying components — moving to AI SDK 7 from the previous major changed the stream and tool-approval parts of UIMessage, and React 18 → 19
changed forwardRef ergonomics.