Agent skill
vite-2-next
Zero-downtime migration playbook for React+Vite+Tailwind apps to Next.js 16 App Router with modern tooling (Radix UI, shadcn/ui, TypeScript). Trigger when asked to migrate, convert, or upgrade from React/Vite to Next.js; it handles router conversion, data fetching transformation, build config migration, component refactoring, and deployment setup.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/vite-2-next
SKILL.md
Migrate React + Vite to Next.js 16
Overview
This skill helps AI agents:
- Audit existing React+Vite projects (routing, state management, data fetching, dependencies)
- Bootstrap a parallel Next.js 16 App Router project with Tailwind, shadcn/ui, and modern tooling
- Translate routes, layouts, components, and data-fetching patterns into idiomatic Server/Client Components
- Migrate environment variables, static assets, and API routes to Next.js 16 conventions (including
proxy.ts) - Optimize & deploy using React Compiler, Turbopack, and platform-specific deployment guidance
This migration preserves UI components while transforming the underlying architecture to leverage SSR, streaming, and Server Actions.
FIRST: Verify React Project Structure
Confirm the project is a React application by checking:
reactandreact-dominpackage.jsondependencies- Presence of
vite.config.ts/js,craco.config.js, orwebpack.config.js - Either
src/directory with components or root-level component files
If none found, STOP — this skill does not apply.
Detect the package manager from the lockfile:
| Lockfile | Manager | Install | Uninstall | Execute |
|---|---|---|---|---|
pnpm-lock.yaml |
pnpm | pnpm add |
pnpm remove |
pnpm dlx |
yarn.lock |
yarn | yarn add |
yarn remove |
yarn dlx |
bun.lockb / bun.lock |
bun | bun add |
bun remove |
bunx |
package-lock.json or none |
npm | npm install |
npm uninstall |
npx |
Detect routing system:
- React Router — Check for
react-router-domin dependencies - TanStack Router — Check for
@tanstack/react-router - Wouter — Check for
wouter - No router — Single-page app with manual navigation
Migration Strategy Overview
| React/Vite Pattern | Next.js 16 App Router Equivalent |
|---|---|
Client-side routing (<BrowserRouter>) |
File-system routing (app/ directory) |
useEffect data fetching |
Server Components with async/await |
useState for server data |
Server Actions, useActionState |
index.html with <div id="root"> |
app/layout.tsx with RootLayout |
import.meta.env.VITE_* |
process.env.NEXT_PUBLIC_* |
middleware.ts |
proxy.ts (renamed in Next.js 16) |
| Vite plugins for CSS/assets | Built-in optimization + Turbopack |
| Client-only rendering | Hybrid rendering (Server + Client Components) |
Phase 1: Compatibility Audit
1a. Check Critical Dependencies
Generally Compatible: Material-UI, Ant Design, Chakra UI, Radix UI, Headless UI, Zustand, Jotai, Valtio, React Hook Form, Formik, Framer Motion, date-fns, lodash, clsx
Requires Refactoring: Redux/Redux Toolkit (wrap in useRef provider), React Query/TanStack Query (configure for SSR), Apollo Client (SSR setup), SWR (configure for SSR)
Incompatible / Not Needed: react-router-dom, Vite plugins, react-helmet, dotenv
See references/compatibility.md for the full library compatibility matrix.
1b. Analyze Application Architecture
Categorize components by type:
- Page Components — Top-level routes (Home, About, Dashboard)
- Layout Components — Shared structure (Header, Sidebar, Footer)
- UI Components — Reusable elements (Button, Card, Modal)
- Data Components — Components with API calls/side effects
- Context Providers — Global state (Theme, Auth, etc.)
Phase 2: Project Initialization
2a. Create Next.js Project Alongside
CRITICAL: Do not delete or modify the existing React project yet. Create Next.js in a parallel directory for gradual migration and easy rollback.
npx create-next-app@latest [project-name]-nextjs --typescript --tailwind --app --src-dir --import-alias "@/*"
Node.js 20.9.0 or later is required for Next.js 16.
2b. Install Core Dependencies
[package-manager] add @radix-ui/react-slot class-variance-authority clsx tailwind-merge
[package-manager-exec] shadcn@latest init
[package-manager] add lucide-react zod react-hook-form @hookform/resolvers
2c. Copy Tailwind Configuration
See references/tailwind-migration.md for detailed examples.
Phase 3: Core File Structure Migration
3a. Convert Routing Structure
| React Router | Next.js App Router |
|---|---|
<Route path="/" element={<Home />} /> |
app/page.tsx |
<Route path="/about" element={<About />} /> |
app/about/page.tsx |
<Route path="/blog/:slug" element={<Post />} /> |
app/blog/[slug]/page.tsx |
<Route path="/blog/*" element={<Blog />} /> |
app/blog/[...slug]/page.tsx |
<Outlet /> in layout |
{children} in layout.tsx |
<Navigate to="/" /> |
redirect('/') from next/navigation |
Next.js 16:
paramsandsearchParamsare async Promises — alwaysawaitthem.
// app/blog/[slug]/page.tsx
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <h1>{slug}</h1>;
}
See references/routing-patterns.md for complex routing scenarios.
3b. Convert Root Layout
Before (React):
// src/App.tsx
function App() {
return (
<ThemeProvider>
<BrowserRouter>
<Header />
<Routes>{/* routes */}</Routes>
<Footer />
</BrowserRouter>
</ThemeProvider>
);
}
After (Next.js 16):
// src/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/providers/theme-provider";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "My App",
description: "App description",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}
3c. Migrate Components
Conversion rules:
- No hooks, no events, no browser APIs → Server Component (no directive needed)
- Has
useState/useEffect/ event handlers → Add'use client' - Uses React Context → Wrap in a
'use client'Provider - Uses
React.lazy→ Replace withnext/dynamic - Uses
<a>for internal links → Replace withnext/link - Uses
useNavigate→ Replace withuseRouterfromnext/navigation
See references/component-patterns.md for detailed examples.
Phase 4: Data Fetching Transformation
4a. Replace useEffect with Server Components
Before (React):
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
After (Next.js 16 — Server Component):
async function UserProfile({ userId }: { userId: string }) {
const user = await fetch(`https://api.example.com/users/${userId}`).then(
(res) => res.json(),
);
return <div>{user.name}</div>;
}
4b. Caching in Next.js 16
Everything is dynamic by default. To opt into caching, use the "use cache" directive:
"use cache";
export default async function ProductsPage() {
const products = await fetch("https://api.example.com/products").then((r) =>
r.json(),
);
return <ProductList products={products} />;
}
The old
fetch(url, { next: { revalidate: 60 } })andcache: 'force-cache'patterns are deprecated in Next.js 16. Use"use cache"instead.
4c. Server Actions for Mutations
// app/posts/create/actions.ts
"use server";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
await fetch("https://api.example.com/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title }),
});
revalidatePath("/posts");
redirect("/posts");
}
// app/posts/create/page.tsx
import { createPost } from "./actions";
export default function CreatePostPage() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Submit</button>
</form>
);
}
See references/data-fetching.md for comprehensive patterns.
Phase 5: Environment Variables
| Vite | Next.js 16 | Scope |
|---|---|---|
import.meta.env.VITE_API_URL |
process.env.NEXT_PUBLIC_API_URL |
Client + Server |
import.meta.env.VITE_* |
process.env.NEXT_PUBLIC_* |
Client + Server |
process.env.* (server) |
process.env.* |
Server only |
See references/environment.md for detailed configuration.
Phase 6: Static Assets
Move src/assets/ → public/. See references/assets.md for optimization.
Phase 7: State Management
Context providers must be 'use client'. All global stores (Zustand, Redux, Jotai) must use a useRef-based provider — never module-level singletons — to prevent state leaking between server requests.
See references/state-management.md for detailed patterns.
Phase 8: API Routes and Proxy
Route Handlers
// app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET() {
const posts = await db.posts.findMany();
return NextResponse.json(posts);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const post = await db.posts.create({ data: body });
return NextResponse.json(post, { status: 201 });
}
Dynamic route
paramsare async in Next.js 16:{ params }: { params: Promise<{ id: string }> }— alwaysawait params.
proxy.ts (replaces middleware.ts)
// proxy.ts ← was middleware.ts in Next.js 13–15
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function proxy(request: NextRequest) {
return NextResponse.next();
}
export const config = { matcher: ["/dashboard/:path*"] };
See references/api-routes.md for comprehensive examples.
Phase 9: Build Configuration
next.config.ts
import type { NextConfig } from "next";
const config: NextConfig = {
// React Compiler — stable in Next.js 16
reactCompiler: true,
experimental: {
// Turbopack persistent FS cache — faster cold starts
turbopackFileSystemCacheForDev: true,
},
images: {
remotePatterns: [{ protocol: "https", hostname: "**.example.com" }],
},
async redirects() {
return [{ source: "/old-path", destination: "/new-path", permanent: true }];
},
};
export default config;
Turbopack is the default bundler in Next.js 16. Custom webpack configs will not work — rewrite for Turbopack or
next.config.ts.
See references/config-options.md for all available options.
Phase 10: Testing and Validation
Feature Parity Checklist
- All routes render correctly
- Navigation between pages works
- Forms submit and handle validation
- Data fetching displays correct content
- Authentication flow works
- Protected routes redirect properly
- Search, filtering, and sorting work
- Dark mode / theme switching works
- Responsive design intact
- Third-party scripts load
Common Migration Issues
See references/troubleshooting.md for solutions.
Phase 11: Deployment
Vercel (Recommended):
npm install -g vercel && vercel login && vercel
Alternatives: Netlify, Cloudflare Pages (@cloudflare/next-on-pages), AWS Amplify, Docker, Node.js server.
See references/deployment.md for platform-specific guides.
Phase 12: Post-Migration Optimization
Convert Client Components to Server Components where possible. Use loading.tsx and <Suspense> for streaming. Add generateMetadata for SEO. Use parallel Promise.all() fetching.
See references/optimization.md for advanced techniques.
Anti-Patterns and Common Mistakes
Do NOT:
- Use
useEffectfor data fetching in Server Components - Add
'use client'to every file by default - Import Server Components into Client Components
- Use
window,localStorage,documentin Server Components - Use
next: { revalidate }orcache: 'force-cache'— use"use cache"directive instead - Call
cookies()orheaders()synchronously — they are async in Next.js 16 - Use a file named
middleware.ts— it is nowproxy.tsin Next.js 16 - Use module-level store singletons — always wrap in
useRef-based providers
DO:
- Default to Server Components
- Add
'use client'only when needed - Use Server Actions for mutations
- Leverage
next/image,next/font,next/script - Always
await params,await cookies(),await headers() - Follow App Router conventions:
layout,loading,error,not-found
Success Criteria
- All pages accessible via Next.js routes
- Build completes without errors
- Lighthouse scores improved (FCP, LCP, TTFB)
- No console errors
- Production deployment successful
Complexity estimate:
- Simple — <10 routes, basic data fetching (2–4 hours)
- Moderate — Multiple routes, React Query/SWR, some global state (1–2 days)
- Complex — 20+ routes, Redux, complex auth, many integrations (3–7 days)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?