Migrating

Migrating

Coming from shadcn / ShipFast / Chatbot UI / vanilla Next.

Sourcedocs/MIGRATING.md

AgentKit 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.node is >=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/anthropic and @ai-sdk/react move to their v7-era majors. Read node_modules/ai/docs/ before changing AI code.
  • A third provider, OpenRouter. ai.provider: 'openrouter' with OPENROUTER_API_KEY.
  • New optional ai fields: 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 plays demo.heroChat.
  • Tools moved to src/lib/demo-tools.ts (list_deploys, search_logs, rollback_deploy), with an approval policy in demoToolApproval.
  • Optional TOOL_APPROVAL_SECRET signs tool approvals. Set it in production.
  • "Try it live" panels on five component pages, and the agent-playground starter 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 matching site.config.ts field: hero, features, agentTrace, toolCalling, pricing, socialProof, faq, finalCta, footer.
  • lib/product.ts is removed. Name, URLs, description and the primary CTA are product, seo and og in site.config.ts.
  • Demo scripts (the hero chat exchange, the agent trace, the tool-call demo) are demo.heroChat, demo.trace and demo.toolCall in site.config.ts. src/lib/demo-agent.ts keeps only the streaming-card text.
  • Sections take content props. They no longer hold copy and never import the config; src/app/page.tsx passes each one its field.
  • Live chat is configured by ai in site.config.ts (OpenAI or Anthropic), with AI_PROVIDER / AI_MODEL overrides. You no longer swap an import in route.ts. Production still needs Upstash Redis.
  • public/llms.txt is gone. /llms.txt is generated from the llms block by src/app/llms.txt/route.ts.
  • AGENTS.md, .cursor/rules/agentkit.mdc and prompts/rebrand.md ship with the kit. AGENTS.md is now yours to edit.
  • showcase in site.config.ts turns the kit's catalog pages (/components, /sections, /docs, /starters) on or off. Set it to false before you deploy.

If you customised v1

Moving is a copy job, not a rewrite:

  1. Start from a fresh v2.0 download in a new folder, so you keep your v1 project intact.
  2. Copy your edited strings from the v1 section files into the matching site.config.ts fields. Or paste prompts/rebrand.md into your AI assistant with your v1 copy as the brief.
  3. Move your hero chat, trace and tool-call scripts from demo-agent.ts into demo.
  4. Copy over your brand tokens and fonts from src/app/globals.css and src/app/layout.tsx.
  5. Copy your tools from src/app/api/chat/route.ts, and set ai.provider, ai.model and ai.systemPrompt.
  6. If you changed section markup, re-apply those changes on top of the new props-based components in src/components/sections/.
  7. Set showcase: false, then run bun 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, same clsx + tailwind-merge implementation (src/lib/utils.ts).
  • CVA + asChild. Button uses class-variance-authority and @radix-ui/react-slot exactly like shadcn. <Button asChild><Link href="/x">Go</Link></Button> works the same.
  • Radix under the hood for Dialog, Tabs, Tooltip, and Accordion (@radix-ui/react-dialog, -tabs, -tooltip, -accordion in package.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.tsx and src/components/ui/toast.tsx without Radix. If you want the Radix versions, swap them in; our PopoverTrigger / 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) in src/app/globals.css all live under [data-theme="..."] selectors on <html>. shadcn's .dark class 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% with hsl(var(--primary)). We use --color-brand: #c6f432 directly in the class: bg-[var(--color-brand)]. No hsl() 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. — see src/components/ai/index.ts for 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 — our cn() 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 — .dark or data-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/) including AgentTraceViewer, ToolCallPanel, StreamingMarkdown, ReasoningTree, MultiAgentView, CostTracker.
  • Editorial dark design with three swappable CSS-var themes.
  • A /components live-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, footer in src/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:

  1. 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"] in src/app/globals.css, so they only apply to that subtree. daisyUI's data-theme attribute is the same mechanism — pick distinct names (editorial-dark vs light / corporate) and they don't collide.

  2. 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-theme on <main>, or migrate app-wide.
  • Copy src/app/api/chat/route.ts into your ShipFast app — this is the only backend piece AgentKit ships.
  • Add OPENAI_API_KEY to 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:

  • useChat from @ai-sdk/react (we use the @ai-sdk/react range in package.json)
  • streamText, convertToModelMessages, tool, UIMessage from ai
  • openai from @ai-sdk/openai, anthropic from @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:

  1. Copy the component file from src/components/ai/agent-trace-viewer.tsx and src/components/ai/tool-call-panel.tsx into your project.
  2. Copy the CSS-var block from src/app/globals.css (lines 29–52 — the [data-theme="editorial-dark"] block) into your own globals.css. You can cherry-pick just the vars the component touches (--color-bg-card, --color-border, --color-text, --color-brand).
  3. Copy src/lib/utils.ts (the cn() helper) if you don't have one already.
  4. 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.
  • MessageBubbleRole aligns with UIMessage.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 of src/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) and src/components/ai/ (AI-native).
  • 47 test files (*.test.tsx next to components) using Vitest + Testing Library + jsdom.
  • Live /components docs route at src/app/components/ — every component previews itself with source + props table, driven by src/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/chat to 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 of MessageThread; prefer MessageThread + ChatInput in 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.ts as 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/* (except chat), and DB client first.
  • Decide: landing page, or app? If app, strip sections/ and page.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 /components to browse everything available before you start building.

Version reference

AgentKit v2.1.0 pins these versions in package.json:

PackageVersion
next16.4.2
react / react-dom19.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.