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 porting from four common starting points: shadcn/ui, ShipFast, Chatbot UI / a hand-rolled AI SDK app, and vanilla create-next-app.

AgentKit v1.2.0 targets Next.js 16, React 19.2, Tailwind v4, and AI SDK v6. Dependencies are locked in package.json.


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 (defaultprimary, outlinesecondary), and swap bg-primarybg-[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-foregroundbg-[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 streaming chat hero wired to AI SDK v6 (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 v6, you can drop individual AgentKit components into your existing app without adopting the whole kit.

What stays the same

Every AI SDK v6 import you use keeps working:

  • useChat from @ai-sdk/react (we use ^2.0.0, same as Chatbot UI)
  • streamText, convertToModelMessages, tool, UIMessage from ai (^6.0.0)
  • openai from @ai-sdk/openai, anthropic from @ai-sdk/anthropic
  • result.toUIMessageStreamResponse() as the route handler return

Our src/app/api/chat/route.ts is ~50 lines of standard AI SDK v6 — if you already have a chat route, yours is probably isomorphic.

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:
npm install 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 v6'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 — 43 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 your OPENAI_API_KEY (or ANTHROPIC_API_KEY) to .env.local.
  • Run npm install && npm 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 v1.2.0 pins these versions in package.json:

PackageVersion
next16.2.4
react / react-dom19.2.4
ai^6.0.0
@ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/react^2.0.0
tailwindcss^4
class-variance-authority^0.7.1
clsx^2.1.1
tailwind-merge^2.6.0
lucide-react^0.469.0
zod^3.24.1
framer-motion^11.15.0

If your existing project pins older majors of ai or react, upgrade those before copying components — AI SDK v5 → v6 changed the UIMessage shape, and React 18 → 19 changed forwardRef ergonomics.