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 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, 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 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/) 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 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:
useChatfrom@ai-sdk/react(we use^2.0.0, same as Chatbot UI)streamText,convertToModelMessages,tool,UIMessagefromai(^6.0.0)openaifrom@ai-sdk/openai,anthropicfrom@ai-sdk/anthropicresult.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:
- 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:
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.
-
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 — 43 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 your
OPENAI_API_KEY(orANTHROPIC_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
/componentsto browse everything available before you start building.
Version reference
AgentKit v1.2.0 pins these versions in package.json:
| Package | Version |
|---|---|
next | 16.2.4 |
react / react-dom | 19.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.