--- allowed-tools: Read, Write, Edit, Bash, Grep, Glob argument-hint: [--pages-to-app] [--js-to-ts] [--class-to-hooks] [--analyze] description: Comprehensive Next.js migration assistant for Pages Router to App Router, JavaScript to TypeScript, and modern patterns --- ## Next.js Migration Helper **Migration Type**: $ARGUMENTS ## Current Project Analysis ### Project Structure Analysis - Next.js version: !`grep '"next"' package.json | head -1` - Current router: !`ls -la pages/ 2>/dev/null && echo "Pages Router detected" || echo "No pages/ directory found"` - App router: !`ls -la app/ 2>/dev/null && echo "App Router detected" || echo "No app/ directory found"` - TypeScript: @tsconfig.json (if exists) ### File Structure Overview - Pages directory: @pages/ (if exists) - App directory: @app/ (if exists) - Components: @components/ (if exists) - API routes: @pages/api/ or @app/api/ - Styles: @styles/ (if exists) ## Migration Strategies ### 1. Pages Router to App Router Migration #### Pre-Migration Analysis ```typescript // Migration analysis tool interface MigrationAnalysis { currentStructure: 'pages' | 'app' | 'hybrid'; pagesCount: number; apiRoutesCount: number; customApp: boolean; customDocument: boolean; customError: boolean; middlewareExists: boolean; complexityScore: number; } const analyzeMigrationComplexity = (): MigrationAnalysis => { return { currentStructure: 'pages', // Detected from file structure pagesCount: 0, // Count .js/.tsx files in pages/ apiRoutesCount: 0, // Count files in pages/api/ customApp: false, // Check for pages/_app customDocument: false, // Check for pages/_document customError: false, // Check for pages/_error or 404 middlewareExists: false, // Check for middleware.ts complexityScore: 0, // 1-10 scale }; }; ``` #### Migration Steps ##### Step 1: Create App Directory Structure ```bash #!/bin/bash # Create app directory structure echo "๐Ÿš€ Creating App Router directory structure..." # Create base app directory mkdir -p app mkdir -p app/globals mkdir -p app/api # Create layout files echo "๐Ÿ“ Creating layout structure..." # Root layout cat > app/layout.tsx << 'EOF' import type { Metadata } from 'next' import { Inter } from 'next/font/google' import './globals.css' const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: 'Your App', description: 'Migrated to App Router', } export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( {children} ) } EOF # Global CSS cat > app/globals.css << 'EOF' /* Global styles for App Router */ :root { --max-width: 1100px; --border-radius: 12px; --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Code', 'Segoe UI Mono', 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', 'Fira Code', 'Droid Sans Mono', 'Courier New', monospace; } * { box-sizing: border-box; padding: 0; margin: 0; } html, body { max-width: 100vw; overflow-x: hidden; } body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb)); } a { color: inherit; text-decoration: none; } @media (prefers-color-scheme: dark) { html { color-scheme: dark; } } EOF echo "โœ… App Router structure created" ``` ##### Step 2: Migrate Pages to App Router ```typescript // Page migration utility interface PageMigration { source: string; destination: string; type: 'page' | 'api' | 'dynamic' | 'nested'; hasGetServerSideProps: boolean; hasGetStaticProps: boolean; hasGetStaticPaths: boolean; } const migratePage = async (pagePath: string): Promise => { const pageContent = readFileSync(pagePath, 'utf-8'); // Extract page component const componentMatch = pageContent.match(/export default function (\w+)/); const componentName = componentMatch?.[1] || 'Page'; // Check for data fetching methods const hasGetServerSideProps = pageContent.includes('getServerSideProps'); const hasGetStaticProps = pageContent.includes('getStaticProps'); const hasGetStaticPaths = pageContent.includes('getStaticPaths'); // Convert to App Router format let appRouterCode = ''; // Add metadata if page has Head component if (pageContent.includes('from \'next/head\'')) { appRouterCode += `import type { Metadata } from 'next'\n\n`; appRouterCode += generateMetadata(pageContent); } // Convert data fetching if (hasGetServerSideProps) { appRouterCode += convertGetServerSideProps(pageContent); } else if (hasGetStaticProps) { appRouterCode += convertGetStaticProps(pageContent); } // Convert component appRouterCode += convertPageComponent(pageContent); return appRouterCode; }; const convertGetServerSideProps = (content: string): string => { // Extract getServerSideProps logic and convert to Server Component const gsspMatch = content.match(/export async function getServerSideProps[\s\S]*?(?=export|$)/); if (!gsspMatch) return ''; return ` // Server Component with direct data fetching async function fetchData(context: any) { // Converted from getServerSideProps // Add your data fetching logic here return { data: null }; } `; }; const generateMetadata = (content: string): string => { // Extract Head component content and convert to metadata return ` export const metadata: Metadata = { title: 'Page Title', description: 'Page description', } `; }; const convertPageComponent = (content: string): string => { // Convert page component to App Router format return content .replace(/import Head from \'next\/head\'/g, '') .replace(/[\s\S]*?<\/Head>/g, '') .replace(/export async function getServerSideProps[\s\S]*?(?=export)/g, '') .replace(/export async function getStaticProps[\s\S]*?(?=export)/g, '') .replace(/export async function getStaticPaths[\s\S]*?(?=export)/g, ''); }; ``` ##### Step 3: Migrate API Routes ```typescript // API route migration const migrateApiRoute = (apiPath: string): string => { const apiContent = readFileSync(apiPath, 'utf-8'); // Convert to App Router API format let newApiContent = `import { NextRequest, NextResponse } from 'next/server'\n\n`; // Extract handler functions const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']; methods.forEach(method => { const handlerRegex = new RegExp(`if.*req\\.method.*===.*['"]${method}['"]`, 'i'); if (apiContent.match(handlerRegex)) { newApiContent += ` export async function ${method}( request: NextRequest, { params }: { params: { [key: string]: string } } ) { try { // Migrated ${method} handler // Add your logic here return NextResponse.json({ message: '${method} success' }) } catch (error) { console.error('${method} error:', error) return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ) } } `; } }); return newApiContent; }; ``` ### 2. JavaScript to TypeScript Migration #### TypeScript Configuration Setup ```json // tsconfig.json { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "es6"], "allowJs": true, "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "baseUrl": ".", "paths": { "@/*": ["./*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } ``` #### File Conversion Process ```bash #!/bin/bash # Convert JavaScript files to TypeScript echo "๐Ÿ”„ Converting JavaScript files to TypeScript..." # Find all .js and .jsx files find . -name "*.js" -o -name "*.jsx" | grep -v node_modules | grep -v .next | while read file; do # Skip if TypeScript version already exists ts_file="${file%.*}.ts" tsx_file="${file%.*}.tsx" if [[ -f "$ts_file" ]] || [[ -f "$tsx_file" ]]; then echo "โญ๏ธ Skipping $file (TypeScript version exists)" continue fi # Determine if file contains JSX if grep -q "jsx\|<.*>" "$file"; then new_file="${file%.*}.tsx" else new_file="${file%.*}.ts" fi echo "๐Ÿ“ Converting $file -> $new_file" # Copy file with new extension cp "$file" "$new_file" # Add basic type annotations sed -i.bak ' # Add React import for TSX files /^import.*React/!{ /\.tsx$/s/^/import React from '\''react'\''\n/ } # Add basic prop types s/function \([A-Z][a-zA-Z]*\)(\([^)]*\))/function \1(\2: any)/g # Add return type annotations for simple functions s/const \([a-zA-Z][a-zA-Z0-9]*\) = (/const \1 = (/g ' "$new_file" # Remove backup file rm "${new_file}.bak" 2>/dev/null || true echo "โœ… Converted $file" done echo "๐ŸŽ‰ JavaScript to TypeScript conversion completed" echo "โš ๏ธ Please review and add proper type annotations" ``` ### 3. Class Components to Function Components Migration #### Component Analysis and Conversion ```typescript // Class to function component converter const convertClassComponent = (componentCode: string): string => { // Extract class component parts const classMatch = componentCode.match(/class (\w+) extends (?:React\.)?Component/); const componentName = classMatch?.[1] || 'Component'; // Extract state const stateMatch = componentCode.match(/state\s*=\s*{([^}]+)}/); const initialState = stateMatch?.[1] || ''; // Extract lifecycle methods const lifecycleMethods = extractLifecycleMethods(componentCode); // Extract render method const renderMatch = componentCode.match(/render\(\)\s*{([\s\S]*?)(?=^\s*})/m); const renderContent = renderMatch?.[1] || ''; // Generate function component let functionComponent = `import React, { useState, useEffect } from 'react';\n\n`; // Add prop types if they exist const propsMatch = componentCode.match(/(\w+)Props/); if (propsMatch) { functionComponent += `interface ${propsMatch[1]}Props {\n // Add prop definitions here\n}\n\n`; } functionComponent += `const ${componentName}: React.FC<${componentName}Props> = (props) => {\n`; // Convert state if (initialState) { const stateVars = parseState(initialState); stateVars.forEach(({ name, value }) => { functionComponent += ` const [${name}, set${capitalize(name)}] = useState(${value});\n`; }); } // Convert lifecycle methods to hooks if (lifecycleMethods.componentDidMount) { functionComponent += `\n useEffect(() => {\n`; functionComponent += ` ${lifecycleMethods.componentDidMount}\n`; functionComponent += ` }, []);\n`; } if (lifecycleMethods.componentDidUpdate) { functionComponent += `\n useEffect(() => {\n`; functionComponent += ` ${lifecycleMethods.componentDidUpdate}\n`; functionComponent += ` });\n`; } if (lifecycleMethods.componentWillUnmount) { functionComponent += `\n useEffect(() => {\n`; functionComponent += ` return () => {\n`; functionComponent += ` ${lifecycleMethods.componentWillUnmount}\n`; functionComponent += ` };\n`; functionComponent += ` }, []);\n`; } // Add render return functionComponent += `\n return (\n`; functionComponent += renderContent.replace(/this\.state\./g, '').replace(/this\.props\./g, 'props.'); functionComponent += ` );\n`; functionComponent += `};\n\n`; functionComponent += `export default ${componentName};`; return functionComponent; }; const extractLifecycleMethods = (code: string) => { return { componentDidMount: extractMethod(code, 'componentDidMount'), componentDidUpdate: extractMethod(code, 'componentDidUpdate'), componentWillUnmount: extractMethod(code, 'componentWillUnmount'), }; }; const extractMethod = (code: string, methodName: string): string | null => { const regex = new RegExp(`${methodName}\\(\\)\\s*{([\\s\\S]*?)(?=^\\s*})`); const match = code.match(regex); return match?.[1] || null; }; const parseState = (stateString: string) => { // Simple state parser - would need more robust implementation return [ { name: 'example', value: 'null' } ]; }; const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); ``` ### 4. Modern React Patterns Migration #### Hook Conversion Patterns ```typescript // Convert common patterns to modern hooks // State management const convertStateManagement = ` // โŒ Old class component state class MyComponent extends Component { state = { count: 0, name: '' }; updateCount = () => { this.setState({ count: this.state.count + 1 }); }; } // โœ… Modern function component with hooks const MyComponent = () => { const [count, setCount] = useState(0); const [name, setName] = useState(''); const updateCount = () => { setCount(prev => prev + 1); }; }; `; // Effect management const convertEffects = ` // โŒ Old lifecycle methods componentDidMount() { this.fetchData(); } componentDidUpdate(prevProps) { if (prevProps.id !== this.props.id) { this.fetchData(); } } componentWillUnmount() { clearInterval(this.timer); } // โœ… Modern useEffect useEffect(() => { fetchData(); }, []); // componentDidMount useEffect(() => { fetchData(); }, [id]); // componentDidUpdate with dependency useEffect(() => { return () => { clearInterval(timer); }; }, []); // componentWillUnmount `; // Context usage const convertContext = ` // โŒ Old context usage import { ThemeContext } from './context'; class MyComponent extends Component { static contextType = ThemeContext; render() { const theme = this.context; return
Content
; } } // โœ… Modern context with hooks import { useContext } from 'react'; import { ThemeContext } from './context'; const MyComponent = () => { const theme = useContext(ThemeContext); return
Content
; }; `; ``` ## Comprehensive Migration Process ### 1. Pre-Migration Checklist ```bash #!/bin/bash # Pre-migration validation echo "๐Ÿ” Running pre-migration checks..." # Check Next.js version NEXT_VERSION=$(grep '"next"' package.json | grep -o '[0-9.]*') echo "๐Ÿ“ฆ Next.js version: $NEXT_VERSION" # Check for potential blockers BLOCKERS=0 # Check for custom server if [ -f "server.js" ] || [ -f "server.ts" ]; then echo "โš ๏ธ Custom server detected - may need special handling" ((BLOCKERS++)) fi # Check for pages/_document with custom logic if [ -f "pages/_document.js" ] || [ -f "pages/_document.tsx" ]; then if grep -q "getInitialProps" pages/_document.*; then echo "โš ๏ธ Custom _document with getInitialProps - needs manual migration" ((BLOCKERS++)) fi fi # Check for pages/_error if [ -f "pages/_error.js" ] || [ -f "pages/_error.tsx" ]; then echo "โ„น๏ธ Custom error page found - will need to migrate to error.tsx" fi # Check for middleware if [ -f "middleware.ts" ] || [ -f "middleware.js" ]; then echo "โœ… Middleware already exists" else echo "โ„น๏ธ No middleware found" fi echo "" if [ $BLOCKERS -eq 0 ]; then echo "โœ… Ready for migration!" else echo "โš ๏ธ Found $BLOCKERS potential blockers - review before proceeding" fi ``` ### 2. Migration Execution ```bash #!/bin/bash # Execute migration echo "๐Ÿš€ Starting Next.js migration process..." # Step 1: Backup current project echo "๐Ÿ“ฆ Creating backup..." tar -czf "project-backup-$(date +%Y%m%d_%H%M%S).tar.gz" \ --exclude=node_modules \ --exclude=.next \ --exclude=.git \ . # Step 2: Install dependencies echo "๐Ÿ“ฅ Installing required dependencies..." npm install --save-dev @types/react @types/react-dom @types/node npm install --save-dev typescript # Step 3: Create TypeScript config if [ ! -f "tsconfig.json" ]; then echo "โš™๏ธ Creating TypeScript configuration..." npx tsc --init --jsx preserve --esModuleInterop --allowJs --strict fi # Step 4: Create App Router structure echo "๐Ÿ—๏ธ Creating App Router structure..." mkdir -p app # ... (creation logic from previous steps) # Step 5: Migrate pages echo "๐Ÿ“„ Migrating pages..." # ... (migration logic) # Step 6: Migrate API routes echo "๐Ÿ”Œ Migrating API routes..." # ... (API migration logic) # Step 7: Update configurations echo "โš™๏ธ Updating configurations..." # Update next.config.js, package.json scripts, etc. echo "โœ… Migration completed!" echo "โš ๏ธ Please review the migrated code and test thoroughly" ``` ### 3. Post-Migration Validation ```bash #!/bin/bash # Post-migration validation echo "๐Ÿ” Running post-migration validation..." # Check if project builds echo "๐Ÿ—๏ธ Testing build..." npm run build if [ $? -eq 0 ]; then echo "โœ… Build successful" else echo "โŒ Build failed - check errors above" exit 1 fi # Check TypeScript compilation echo "๐Ÿ” Checking TypeScript..." npx tsc --noEmit if [ $? -eq 0 ]; then echo "โœ… TypeScript validation passed" else echo "โš ๏ธ TypeScript errors found - review and fix" fi # Run tests if they exist if [ -f "package.json" ] && grep -q '"test"' package.json; then echo "๐Ÿงช Running tests..." npm test fi # Check for unused files echo "๐Ÿงน Checking for unused files..." if [ -d "pages" ]; then echo "โ„น๏ธ Original pages/ directory still exists" echo "๐Ÿ’ก Review and remove after confirming migration is complete" fi echo "โœ… Post-migration validation completed" ``` ## Migration Documentation and Guides ### 1. Migration Report Generation ```typescript // Generate comprehensive migration report interface MigrationReport { summary: { totalFiles: number; migratedFiles: number; skippedFiles: number; errorFiles: number; }; details: { pages: MigratedFile[]; components: MigratedFile[]; apiRoutes: MigratedFile[]; }; issues: Issue[]; recommendations: string[]; } interface MigratedFile { original: string; migrated: string; status: 'success' | 'warning' | 'error'; notes: string[]; } interface Issue { file: string; type: 'error' | 'warning'; message: string; solution?: string; } const generateMigrationReport = (): MigrationReport => { // Implementation to generate comprehensive migration report return { summary: { totalFiles: 0, migratedFiles: 0, skippedFiles: 0, errorFiles: 0, }, details: { pages: [], components: [], apiRoutes: [], }, issues: [], recommendations: [ 'Test all functionality thoroughly', 'Update any hardcoded imports', 'Review and optimize bundle splitting', 'Update documentation and README', ], }; }; ``` ### 2. Best Practices Guide ```markdown # Migration Best Practices ## Before Migration - [ ] Update to latest Next.js version - [ ] Run full test suite - [ ] Create comprehensive backup - [ ] Review custom configurations ## During Migration - [ ] Migrate incrementally (pages first, then components) - [ ] Test each migration step - [ ] Keep detailed notes of changes - [ ] Handle TypeScript errors immediately ## After Migration - [ ] Update all imports and references - [ ] Test all functionality - [ ] Update documentation - [ ] Monitor performance metrics - [ ] Clean up old files after validation ## Common Gotchas - Dynamic imports syntax changes - Middleware configuration updates - Environment variable handling - CSS and styling adjustments ``` Provide comprehensive migration assistance with automated tools, validation steps, and detailed documentation for successful Next.js modernization.