Tools
Tools: GitHub Actions workflow to deploy the Next.js Application on Vercel Cloud
2026-02-24
0 views
admin
What We'll Build ## π Prerequisites ## - Basic knowledge of Git and terminal ## STEP 1: Install Node.js and Required Tools on local machine ## STEP 2: Create Next.js Application ## Create Project Structure ## File 1: package.json ## File 2: next.config.js ## File 3: tsconfig.json ## File 4: .gitignore ## File 5: .env.local ## File 6: app/layout.tsx ## File 7: app/page.tsx ## File 8: app/about/page.tsx ## File 9: app/api/hello/route.ts ## File 10: app/globals.css ## File 11: tailwind.config.ts ## File 12: postcss.config.js ## File 13: jest.config.js ## File 14: jest.setup.js ## File 15: tests/page.test.tsx ## File 16: ecosystem.config.js (PM2 Configuration) ## File 17: README.md ## STEP 3: Install Dependencies ## STEP 4: Build the Application ## STEP 5: Run on Port 80 (Requires sudo) ## Using PM2 ## STEP 6: Test the Application ## STEP 7: Push to GitHub ## STEP 8: Create GitHub Actions Workflow ## STEP 9: Setup Vercel ## 9.1: Install Vercel CLI ## 9.2: Login to Vercel ## 9.3: Link Project to Vercel ## 9.4: Get Vercel Credentials ## STEP 10: Add GitHub Secrets ## STEP 11: Test Full Pipeline ## π¨ How to Make Changes and See Automatic Deployment ## Understanding the Workflow ## π Quick Test: Make Your First Change ## Change Homepage Text (Easiest) ## π€ Push Changes to GitHub ## π Watch the Automatic Deployment ## Method 1: Watch on Vercel Dashboard ## π View Your Changes In this article, Iβll walk you through setting up a GitHub Actions workflow to automatically deploy a Next.js application to Vercel.
Continuous deployment helps streamline development and ensures every push is production-ready.
Weβll configure secrets, create a workflow file, and connect GitHub with Vercel for seamless deployments.
This setup enables automated builds and previews for every commit.
Letβs dive into building a simple CI/CD pipeline for your Next.js app. A complete Next.js application that: Now, let's create each file with content: Output will look like: Go to: https://github.com/YOUR_USERNAME/nextjs-app Click: Settings β Secrets and variables β Actions Click: "New repository secret" Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
# Update system
sudo apt update && sudo apt upgrade -y # Install Node.js 20.x (LTS)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs # Verify installation
node --version # Should show v20.x.x
npm --version # Should show 10.x.x # Install additional tools
sudo apt install -y git curl build-essential # Install PM2 (process manager for background running)
sudo npm install -g pm2 # Verify PM2
pm2 --version Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Update system
sudo apt update && sudo apt upgrade -y # Install Node.js 20.x (LTS)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs # Verify installation
node --version # Should show v20.x.x
npm --version # Should show 10.x.x # Install additional tools
sudo apt install -y git curl build-essential # Install PM2 (process manager for background running)
sudo npm install -g pm2 # Verify PM2
pm2 --version COMMAND_BLOCK:
# Update system
sudo apt update && sudo apt upgrade -y # Install Node.js 20.x (LTS)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs # Verify installation
node --version # Should show v20.x.x
npm --version # Should show 10.x.x # Install additional tools
sudo apt install -y git curl build-essential # Install PM2 (process manager for background running)
sudo npm install -g pm2 # Verify PM2
pm2 --version COMMAND_BLOCK:
# Create project directory
mkdir -p ~/nextjs-app
cd ~/nextjs-app # Create Next.js app structure
# We'll create all files manually for complete control Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Create project directory
mkdir -p ~/nextjs-app
cd ~/nextjs-app # Create Next.js app structure
# We'll create all files manually for complete control COMMAND_BLOCK:
# Create project directory
mkdir -p ~/nextjs-app
cd ~/nextjs-app # Create Next.js app structure
# We'll create all files manually for complete control COMMAND_BLOCK:
# Create directories
mkdir -p app/{api/hello,components,lib,styles}
mkdir -p public/images
mkdir -p .github/workflows # Create files
touch next.config.js
touch package.json
touch tsconfig.json
touch .gitignore
touch .env.local
touch README.md
touch ecosystem.config.js Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Create directories
mkdir -p app/{api/hello,components,lib,styles}
mkdir -p public/images
mkdir -p .github/workflows # Create files
touch next.config.js
touch package.json
touch tsconfig.json
touch .gitignore
touch .env.local
touch README.md
touch ecosystem.config.js COMMAND_BLOCK:
# Create directories
mkdir -p app/{api/hello,components,lib,styles}
mkdir -p public/images
mkdir -p .github/workflows # Create files
touch next.config.js
touch package.json
touch tsconfig.json
touch .gitignore
touch .env.local
touch README.md
touch ecosystem.config.js COMMAND_BLOCK:
cat > package.json << 'EOF'
{ "name": "nextjs-app", "version": "1.0.0", "description": "Complete Next.js application with CI/CD", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start -p 80", "lint": "next lint", "test": "jest", "test:watch": "jest --watch" }, "dependencies": { "next": "14.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@types/node": "^20.10.6", "@types/react": "^18.2.46", "@types/react-dom": "^18.2.18", "autoprefixer": "^10.4.16", "eslint": "^8.56.0", "eslint-config-next": "14.1.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "postcss": "^8.4.32", "tailwindcss": "^3.4.0", "typescript": "^5.3.3" }
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > package.json << 'EOF'
{ "name": "nextjs-app", "version": "1.0.0", "description": "Complete Next.js application with CI/CD", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start -p 80", "lint": "next lint", "test": "jest", "test:watch": "jest --watch" }, "dependencies": { "next": "14.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@types/node": "^20.10.6", "@types/react": "^18.2.46", "@types/react-dom": "^18.2.18", "autoprefixer": "^10.4.16", "eslint": "^8.56.0", "eslint-config-next": "14.1.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "postcss": "^8.4.32", "tailwindcss": "^3.4.0", "typescript": "^5.3.3" }
}
EOF COMMAND_BLOCK:
cat > package.json << 'EOF'
{ "name": "nextjs-app", "version": "1.0.0", "description": "Complete Next.js application with CI/CD", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start -p 80", "lint": "next lint", "test": "jest", "test:watch": "jest --watch" }, "dependencies": { "next": "14.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@types/node": "^20.10.6", "@types/react": "^18.2.46", "@types/react-dom": "^18.2.18", "autoprefixer": "^10.4.16", "eslint": "^8.56.0", "eslint-config-next": "14.1.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "postcss": "^8.4.32", "tailwindcss": "^3.4.0", "typescript": "^5.3.3" }
}
EOF COMMAND_BLOCK:
cat > next.config.js << 'EOF'
/** @type {import('next').NextConfig} */
const nextConfig = { reactStrictMode: true, swcMinify: true, output: 'standalone', poweredByHeader: false, // Environment variables env: { NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME || 'Next.js App', NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:80', }, // Image optimization images: { domains: ['localhost'], formats: ['image/avif', 'image/webp'], }, // Headers async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'Referrer-Policy', value: 'origin-when-cross-origin', }, ], }, ]; },
}; module.exports = nextConfig;
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > next.config.js << 'EOF'
/** @type {import('next').NextConfig} */
const nextConfig = { reactStrictMode: true, swcMinify: true, output: 'standalone', poweredByHeader: false, // Environment variables env: { NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME || 'Next.js App', NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:80', }, // Image optimization images: { domains: ['localhost'], formats: ['image/avif', 'image/webp'], }, // Headers async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'Referrer-Policy', value: 'origin-when-cross-origin', }, ], }, ]; },
}; module.exports = nextConfig;
EOF COMMAND_BLOCK:
cat > next.config.js << 'EOF'
/** @type {import('next').NextConfig} */
const nextConfig = { reactStrictMode: true, swcMinify: true, output: 'standalone', poweredByHeader: false, // Environment variables env: { NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME || 'Next.js App', NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:80', }, // Image optimization images: { domains: ['localhost'], formats: ['image/avif', 'image/webp'], }, // Headers async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'Referrer-Policy', value: 'origin-when-cross-origin', }, ], }, ]; },
}; module.exports = nextConfig;
EOF COMMAND_BLOCK:
cat > tsconfig.json << 'EOF'
{ "compilerOptions": { "target": "ES2020", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"]
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > tsconfig.json << 'EOF'
{ "compilerOptions": { "target": "ES2020", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"]
}
EOF COMMAND_BLOCK:
cat > tsconfig.json << 'EOF'
{ "compilerOptions": { "target": "ES2020", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"]
}
EOF COMMAND_BLOCK:
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
/.pnp
.pnp.js # Testing
/coverage # Next.js
/.next/
/out/
.next # Production
/build
dist # Misc
.DS_Store
*.pem
.vercel # Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log* # Local env files
.env*.local
.env.production # PM2
.pm2 # IDE
.idea/
.vscode/
*.swp
*.swo
*~ # Logs
logs
*.log
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
/.pnp
.pnp.js # Testing
/coverage # Next.js
/.next/
/out/
.next # Production
/build
dist # Misc
.DS_Store
*.pem
.vercel # Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log* # Local env files
.env*.local
.env.production # PM2
.pm2 # IDE
.idea/
.vscode/
*.swp
*.swo
*~ # Logs
logs
*.log
EOF COMMAND_BLOCK:
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
/.pnp
.pnp.js # Testing
/coverage # Next.js
/.next/
/out/
.next # Production
/build
dist # Misc
.DS_Store
*.pem
.vercel # Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log* # Local env files
.env*.local
.env.production # PM2
.pm2 # IDE
.idea/
.vscode/
*.swp
*.swo
*~ # Logs
logs
*.log
EOF COMMAND_BLOCK:
cat > .env.local << 'EOF'
# Application
NEXT_PUBLIC_APP_NAME=My Next.js App
NEXT_PUBLIC_API_URL=http://localhost:80 # Environment
NODE_ENV=development
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > .env.local << 'EOF'
# Application
NEXT_PUBLIC_APP_NAME=My Next.js App
NEXT_PUBLIC_API_URL=http://localhost:80 # Environment
NODE_ENV=development
EOF COMMAND_BLOCK:
cat > .env.local << 'EOF'
# Application
NEXT_PUBLIC_APP_NAME=My Next.js App
NEXT_PUBLIC_API_URL=http://localhost:80 # Environment
NODE_ENV=development
EOF COMMAND_BLOCK:
cat > app/layout.tsx << 'EOF'
import type { Metadata } from 'next'
import './globals.css' export const metadata: Metadata = { title: 'Next.js App', description: 'Complete Next.js application with CI/CD',
} export default function RootLayout({ children,
}: { children: React.ReactNode
}) { return ( <html lang="en"> <body>{children}</body> </html> )
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > app/layout.tsx << 'EOF'
import type { Metadata } from 'next'
import './globals.css' export const metadata: Metadata = { title: 'Next.js App', description: 'Complete Next.js application with CI/CD',
} export default function RootLayout({ children,
}: { children: React.ReactNode
}) { return ( <html lang="en"> <body>{children}</body> </html> )
}
EOF COMMAND_BLOCK:
cat > app/layout.tsx << 'EOF'
import type { Metadata } from 'next'
import './globals.css' export const metadata: Metadata = { title: 'Next.js App', description: 'Complete Next.js application with CI/CD',
} export default function RootLayout({ children,
}: { children: React.ReactNode
}) { return ( <html lang="en"> <body>{children}</body> </html> )
}
EOF COMMAND_BLOCK:
cat > app/page.tsx << 'EOF'
import Link from 'next/link' export default function Home() { return ( <main className="min-h-screen flex flex-col items-center justify-center p-24 bg-gradient-to-br from-blue-50 to-indigo-100"> <div className="max-w-4xl w-full space-y-8 text-center"> <div className="space-y-4"> <h1 className="text-6xl font-bold text-gray-900"> Welcome to <span className="text-blue-600">Next.js</span> </h1> <p className="text-xl text-gray-600"> A complete Next.js application with CI/CD pipeline </p> </div> <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12"> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π¦ Features</h3> <p className="text-gray-600"> TypeScript, Tailwind CSS, API Routes </p> </div> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π Deploy</h3> <p className="text-gray-600"> Automated deployment to Vercel </p> </div> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π§ CI/CD</h3> <p className="text-gray-600"> GitHub Actions workflow included </p> </div> </div> <div className="mt-12 space-x-4"> <Link href="/about" className="inline-block px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors" > About Page </Link> <Link href="/api/hello" className="inline-block px-6 py-3 bg-gray-800 text-white rounded-lg hover:bg-gray-900 transition-colors" > Test API </Link> </div> <div className="mt-8 text-sm text-gray-500"> <p>Running on port 80 β’ Managed by PM2</p> </div> </div> </main> )
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > app/page.tsx << 'EOF'
import Link from 'next/link' export default function Home() { return ( <main className="min-h-screen flex flex-col items-center justify-center p-24 bg-gradient-to-br from-blue-50 to-indigo-100"> <div className="max-w-4xl w-full space-y-8 text-center"> <div className="space-y-4"> <h1 className="text-6xl font-bold text-gray-900"> Welcome to <span className="text-blue-600">Next.js</span> </h1> <p className="text-xl text-gray-600"> A complete Next.js application with CI/CD pipeline </p> </div> <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12"> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π¦ Features</h3> <p className="text-gray-600"> TypeScript, Tailwind CSS, API Routes </p> </div> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π Deploy</h3> <p className="text-gray-600"> Automated deployment to Vercel </p> </div> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π§ CI/CD</h3> <p className="text-gray-600"> GitHub Actions workflow included </p> </div> </div> <div className="mt-12 space-x-4"> <Link href="/about" className="inline-block px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors" > About Page </Link> <Link href="/api/hello" className="inline-block px-6 py-3 bg-gray-800 text-white rounded-lg hover:bg-gray-900 transition-colors" > Test API </Link> </div> <div className="mt-8 text-sm text-gray-500"> <p>Running on port 80 β’ Managed by PM2</p> </div> </div> </main> )
}
EOF COMMAND_BLOCK:
cat > app/page.tsx << 'EOF'
import Link from 'next/link' export default function Home() { return ( <main className="min-h-screen flex flex-col items-center justify-center p-24 bg-gradient-to-br from-blue-50 to-indigo-100"> <div className="max-w-4xl w-full space-y-8 text-center"> <div className="space-y-4"> <h1 className="text-6xl font-bold text-gray-900"> Welcome to <span className="text-blue-600">Next.js</span> </h1> <p className="text-xl text-gray-600"> A complete Next.js application with CI/CD pipeline </p> </div> <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12"> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π¦ Features</h3> <p className="text-gray-600"> TypeScript, Tailwind CSS, API Routes </p> </div> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π Deploy</h3> <p className="text-gray-600"> Automated deployment to Vercel </p> </div> <div className="p-6 border border-gray-200 rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow"> <h3 className="text-xl font-semibold mb-2">π§ CI/CD</h3> <p className="text-gray-600"> GitHub Actions workflow included </p> </div> </div> <div className="mt-12 space-x-4"> <Link href="/about" className="inline-block px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors" > About Page </Link> <Link href="/api/hello" className="inline-block px-6 py-3 bg-gray-800 text-white rounded-lg hover:bg-gray-900 transition-colors" > Test API </Link> </div> <div className="mt-8 text-sm text-gray-500"> <p>Running on port 80 β’ Managed by PM2</p> </div> </div> </main> )
}
EOF COMMAND_BLOCK:
mkdir -p app/about
cat > app/about/page.tsx << 'EOF'
import Link from 'next/link' export default function About() { return ( <main className="min-h-screen flex flex-col items-center justify-center p-24 bg-gradient-to-br from-purple-50 to-pink-100"> <div className="max-w-2xl w-full space-y-8"> <h1 className="text-5xl font-bold text-gray-900 text-center"> About This App </h1> <div className="bg-white p-8 rounded-lg shadow-md space-y-4"> <h2 className="text-2xl font-semibold text-gray-800">Features</h2> <ul className="list-disc list-inside space-y-2 text-gray-600"> <li>Next.js 14 with App Router</li> <li>TypeScript for type safety</li> <li>Tailwind CSS for styling</li> <li>API Routes for backend logic</li> <li>PM2 for process management</li> <li>GitHub Actions for CI/CD</li> <li>Vercel deployment ready</li> </ul> </div> <div className="text-center"> <Link href="/" className="inline-block px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors" > β Back to Home </Link> </div> </div> </main> )
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
mkdir -p app/about
cat > app/about/page.tsx << 'EOF'
import Link from 'next/link' export default function About() { return ( <main className="min-h-screen flex flex-col items-center justify-center p-24 bg-gradient-to-br from-purple-50 to-pink-100"> <div className="max-w-2xl w-full space-y-8"> <h1 className="text-5xl font-bold text-gray-900 text-center"> About This App </h1> <div className="bg-white p-8 rounded-lg shadow-md space-y-4"> <h2 className="text-2xl font-semibold text-gray-800">Features</h2> <ul className="list-disc list-inside space-y-2 text-gray-600"> <li>Next.js 14 with App Router</li> <li>TypeScript for type safety</li> <li>Tailwind CSS for styling</li> <li>API Routes for backend logic</li> <li>PM2 for process management</li> <li>GitHub Actions for CI/CD</li> <li>Vercel deployment ready</li> </ul> </div> <div className="text-center"> <Link href="/" className="inline-block px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors" > β Back to Home </Link> </div> </div> </main> )
}
EOF COMMAND_BLOCK:
mkdir -p app/about
cat > app/about/page.tsx << 'EOF'
import Link from 'next/link' export default function About() { return ( <main className="min-h-screen flex flex-col items-center justify-center p-24 bg-gradient-to-br from-purple-50 to-pink-100"> <div className="max-w-2xl w-full space-y-8"> <h1 className="text-5xl font-bold text-gray-900 text-center"> About This App </h1> <div className="bg-white p-8 rounded-lg shadow-md space-y-4"> <h2 className="text-2xl font-semibold text-gray-800">Features</h2> <ul className="list-disc list-inside space-y-2 text-gray-600"> <li>Next.js 14 with App Router</li> <li>TypeScript for type safety</li> <li>Tailwind CSS for styling</li> <li>API Routes for backend logic</li> <li>PM2 for process management</li> <li>GitHub Actions for CI/CD</li> <li>Vercel deployment ready</li> </ul> </div> <div className="text-center"> <Link href="/" className="inline-block px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors" > β Back to Home </Link> </div> </div> </main> )
}
EOF COMMAND_BLOCK:
cat > app/api/hello/route.ts << 'EOF'
import { NextResponse } from 'next/server' export async function GET() { return NextResponse.json({ message: 'Hello from Next.js API!', timestamp: new Date().toISOString(), status: 'success', environment: process.env.NODE_ENV, })
} export async function POST(request: Request) { const body = await request.json() return NextResponse.json({ message: 'Data received successfully', receivedData: body, timestamp: new Date().toISOString(), })
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > app/api/hello/route.ts << 'EOF'
import { NextResponse } from 'next/server' export async function GET() { return NextResponse.json({ message: 'Hello from Next.js API!', timestamp: new Date().toISOString(), status: 'success', environment: process.env.NODE_ENV, })
} export async function POST(request: Request) { const body = await request.json() return NextResponse.json({ message: 'Data received successfully', receivedData: body, timestamp: new Date().toISOString(), })
}
EOF COMMAND_BLOCK:
cat > app/api/hello/route.ts << 'EOF'
import { NextResponse } from 'next/server' export async function GET() { return NextResponse.json({ message: 'Hello from Next.js API!', timestamp: new Date().toISOString(), status: 'success', environment: process.env.NODE_ENV, })
} export async function POST(request: Request) { const body = await request.json() return NextResponse.json({ message: 'Data received successfully', receivedData: body, timestamp: new Date().toISOString(), })
}
EOF COMMAND_BLOCK:
cat > app/globals.css << 'EOF'
@tailwind base;
@tailwind components;
@tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255;
} body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb));
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > app/globals.css << 'EOF'
@tailwind base;
@tailwind components;
@tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255;
} body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb));
}
EOF COMMAND_BLOCK:
cat > app/globals.css << 'EOF'
@tailwind base;
@tailwind components;
@tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255;
} body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb));
}
EOF COMMAND_BLOCK:
cat > tailwind.config.ts << 'EOF'
import type { Config } from 'tailwindcss' const config: Config = { content: [ './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, }, plugins: [],
}
export default config
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > tailwind.config.ts << 'EOF'
import type { Config } from 'tailwindcss' const config: Config = { content: [ './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, }, plugins: [],
}
export default config
EOF COMMAND_BLOCK:
cat > tailwind.config.ts << 'EOF'
import type { Config } from 'tailwindcss' const config: Config = { content: [ './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, }, plugins: [],
}
export default config
EOF COMMAND_BLOCK:
cat > postcss.config.js << 'EOF'
module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, },
}
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > postcss.config.js << 'EOF'
module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, },
}
EOF COMMAND_BLOCK:
cat > postcss.config.js << 'EOF'
module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, },
}
EOF COMMAND_BLOCK:
cat > jest.config.js << 'EOF'
const nextJest = require('next/jest') const createJestConfig = nextJest({ dir: './',
}) const customJestConfig = { setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], testEnvironment: 'jest-environment-jsdom', moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1', }, testMatch: [ '**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', ],
} module.exports = createJestConfig(customJestConfig)
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > jest.config.js << 'EOF'
const nextJest = require('next/jest') const createJestConfig = nextJest({ dir: './',
}) const customJestConfig = { setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], testEnvironment: 'jest-environment-jsdom', moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1', }, testMatch: [ '**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', ],
} module.exports = createJestConfig(customJestConfig)
EOF COMMAND_BLOCK:
cat > jest.config.js << 'EOF'
const nextJest = require('next/jest') const createJestConfig = nextJest({ dir: './',
}) const customJestConfig = { setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], testEnvironment: 'jest-environment-jsdom', moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1', }, testMatch: [ '**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', ],
} module.exports = createJestConfig(customJestConfig)
EOF COMMAND_BLOCK:
cat > jest.setup.js << 'EOF'
import '@testing-library/jest-dom'
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > jest.setup.js << 'EOF'
import '@testing-library/jest-dom'
EOF COMMAND_BLOCK:
cat > jest.setup.js << 'EOF'
import '@testing-library/jest-dom'
EOF COMMAND_BLOCK:
mkdir -p __tests__
cat > __tests__/page.test.tsx << 'EOF'
import { render, screen } from '@testing-library/react'
import Home from '@/app/page' describe('Home Page', () => { it('renders the main heading', () => { render(<Home />) const heading = screen.getByRole('heading', { level: 1 }) expect(heading).toBeInTheDocument() }) it('contains welcome text', () => { render(<Home />) expect(screen.getByText(/Welcome to/i)).toBeInTheDocument() })
})
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
mkdir -p __tests__
cat > __tests__/page.test.tsx << 'EOF'
import { render, screen } from '@testing-library/react'
import Home from '@/app/page' describe('Home Page', () => { it('renders the main heading', () => { render(<Home />) const heading = screen.getByRole('heading', { level: 1 }) expect(heading).toBeInTheDocument() }) it('contains welcome text', () => { render(<Home />) expect(screen.getByText(/Welcome to/i)).toBeInTheDocument() })
})
EOF COMMAND_BLOCK:
mkdir -p __tests__
cat > __tests__/page.test.tsx << 'EOF'
import { render, screen } from '@testing-library/react'
import Home from '@/app/page' describe('Home Page', () => { it('renders the main heading', () => { render(<Home />) const heading = screen.getByRole('heading', { level: 1 }) expect(heading).toBeInTheDocument() }) it('contains welcome text', () => { render(<Home />) expect(screen.getByText(/Welcome to/i)).toBeInTheDocument() })
})
EOF COMMAND_BLOCK:
cat > ecosystem.config.js << 'EOF'
module.exports = { apps: [ { name: 'nextjs-app', script: 'npm', args: 'start', cwd: './', instances: 1, autorestart: true, watch: false, max_memory_restart: '1G', env: { NODE_ENV: 'production', PORT: 80, }, error_file: './logs/err.log', out_file: './logs/out.log', log_file: './logs/combined.log', time: true, }, ],
}
EOF # Create logs directory
mkdir -p logs Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > ecosystem.config.js << 'EOF'
module.exports = { apps: [ { name: 'nextjs-app', script: 'npm', args: 'start', cwd: './', instances: 1, autorestart: true, watch: false, max_memory_restart: '1G', env: { NODE_ENV: 'production', PORT: 80, }, error_file: './logs/err.log', out_file: './logs/out.log', log_file: './logs/combined.log', time: true, }, ],
}
EOF # Create logs directory
mkdir -p logs COMMAND_BLOCK:
cat > ecosystem.config.js << 'EOF'
module.exports = { apps: [ { name: 'nextjs-app', script: 'npm', args: 'start', cwd: './', instances: 1, autorestart: true, watch: false, max_memory_restart: '1G', env: { NODE_ENV: 'production', PORT: 80, }, error_file: './logs/err.log', out_file: './logs/out.log', log_file: './logs/combined.log', time: true, }, ],
}
EOF # Create logs directory
mkdir -p logs COMMAND_BLOCK:
cat > README.md << 'EOF'
# Next.js Application Complete Next.js application with CI/CD pipeline. ## Features - β
Next.js 14 with App Router
- β
TypeScript
- β
Tailwind CSS
- β
API Routes
- β
Jest Testing
- β
PM2 Process Management
- β
GitHub Actions CI/CD
- β
Vercel Deployment ## Development npm install
npm run dev ## Production npm run build
npm start ## Testing npm test ## Deployment Automatically deploys to Vercel on push to main branch.
EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
cat > README.md << 'EOF'
# Next.js Application Complete Next.js application with CI/CD pipeline. ## Features - β
Next.js 14 with App Router
- β
TypeScript
- β
Tailwind CSS
- β
API Routes
- β
Jest Testing
- β
PM2 Process Management
- β
GitHub Actions CI/CD
- β
Vercel Deployment ## Development npm install
npm run dev ## Production npm run build
npm start ## Testing npm test ## Deployment Automatically deploys to Vercel on push to main branch.
EOF COMMAND_BLOCK:
cat > README.md << 'EOF'
# Next.js Application Complete Next.js application with CI/CD pipeline. ## Features - β
Next.js 14 with App Router
- β
TypeScript
- β
Tailwind CSS
- β
API Routes
- β
Jest Testing
- β
PM2 Process Management
- β
GitHub Actions CI/CD
- β
Vercel Deployment ## Development npm install
npm run dev ## Production npm run build
npm start ## Testing npm test ## Deployment Automatically deploys to Vercel on push to main branch.
EOF COMMAND_BLOCK:
# Install all dependencies
npm install # This will take a few minutes Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Install all dependencies
npm install # This will take a few minutes COMMAND_BLOCK:
# Install all dependencies
npm install # This will take a few minutes COMMAND_BLOCK:
# Build for production
npm run build # Expected output:
# β Compiled successfully
# β Linting and checking validity of types
# β Collecting page data
# β Generating static pages Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Build for production
npm run build # Expected output:
# β Compiled successfully
# β Linting and checking validity of types
# β Collecting page data
# β Generating static pages COMMAND_BLOCK:
# Build for production
npm run build # Expected output:
# β Compiled successfully
# β Linting and checking validity of types
# β Collecting page data
# β Generating static pages COMMAND_BLOCK:
# Build first
npm run build # Give Node.js permission to bind to port 80
sudo setcap 'cap_net_bind_service=+ep' $(which node) # Start with PM2
pm2 start ecosystem.config.js # Check status
pm2 status # View logs
pm2 logs nextjs-app # Save PM2 process list
pm2 save # Setup PM2 to start on system boot
pm2 startup
# Follow the command it outputs Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Build first
npm run build # Give Node.js permission to bind to port 80
sudo setcap 'cap_net_bind_service=+ep' $(which node) # Start with PM2
pm2 start ecosystem.config.js # Check status
pm2 status # View logs
pm2 logs nextjs-app # Save PM2 process list
pm2 save # Setup PM2 to start on system boot
pm2 startup
# Follow the command it outputs COMMAND_BLOCK:
# Build first
npm run build # Give Node.js permission to bind to port 80
sudo setcap 'cap_net_bind_service=+ep' $(which node) # Start with PM2
pm2 start ecosystem.config.js # Check status
pm2 status # View logs
pm2 logs nextjs-app # Save PM2 process list
pm2 save # Setup PM2 to start on system boot
pm2 startup
# Follow the command it outputs COMMAND_BLOCK:
# Test locally
curl http://localhost:80 # Or open in browser
# http://localhost:80 Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Test locally
curl http://localhost:80 # Or open in browser
# http://localhost:80 COMMAND_BLOCK:
# Test locally
curl http://localhost:80 # Or open in browser
# http://localhost:80 COMMAND_BLOCK:
# Initialize git
git init # Add all files
git add . # Commit
git commit -m "Initial commit: Complete Next.js app with CI/CD" # Add remote (replace with your repo)
git remote add origin [email protected]:YOUR_USERNAME/nextjs-app.git # Push
git push -u origin main Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Initialize git
git init # Add all files
git add . # Commit
git commit -m "Initial commit: Complete Next.js app with CI/CD" # Add remote (replace with your repo)
git remote add origin [email protected]:YOUR_USERNAME/nextjs-app.git # Push
git push -u origin main COMMAND_BLOCK:
# Initialize git
git init # Add all files
git add . # Commit
git commit -m "Initial commit: Complete Next.js app with CI/CD" # Add remote (replace with your repo)
git remote add origin [email protected]:YOUR_USERNAME/nextjs-app.git # Push
git push -u origin main COMMAND_BLOCK:
# Create workflow file
cat > .github/workflows/deploy.yml << 'EOF'
name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main] env: NODE_VERSION: '20.x' jobs: test: name: Test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Run linter run: npm run lint - name: Run tests run: npm test build: name: Build runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Build application run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: nextjs-build path: .next retention-days: 7 deploy: name: Deploy to Vercel runs-on: ubuntu-latest needs: [test, build] if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to Vercel uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} vercel-args: '--prod'
EOF # Commit and push
git add .github/workflows/deploy.yml
git commit -m "Add GitHub Actions CI/CD workflow"
git push origin main Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Create workflow file
cat > .github/workflows/deploy.yml << 'EOF'
name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main] env: NODE_VERSION: '20.x' jobs: test: name: Test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Run linter run: npm run lint - name: Run tests run: npm test build: name: Build runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Build application run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: nextjs-build path: .next retention-days: 7 deploy: name: Deploy to Vercel runs-on: ubuntu-latest needs: [test, build] if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to Vercel uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} vercel-args: '--prod'
EOF # Commit and push
git add .github/workflows/deploy.yml
git commit -m "Add GitHub Actions CI/CD workflow"
git push origin main COMMAND_BLOCK:
# Create workflow file
cat > .github/workflows/deploy.yml << 'EOF'
name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main] env: NODE_VERSION: '20.x' jobs: test: name: Test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Run linter run: npm run lint - name: Run tests run: npm test build: name: Build runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Build application run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: nextjs-build path: .next retention-days: 7 deploy: name: Deploy to Vercel runs-on: ubuntu-latest needs: [test, build] if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to Vercel uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} vercel-args: '--prod'
EOF # Commit and push
git add .github/workflows/deploy.yml
git commit -m "Add GitHub Actions CI/CD workflow"
git push origin main COMMAND_BLOCK:
npm install -g vercel Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
npm install -g vercel COMMAND_BLOCK:
npm install -g vercel CODE_BLOCK:
vercel login 1. Select "Continue with GitHub"
2. Vercel will open a browser window
3. Browser URL: https://vercel.com/auth/github
4. Click "Authorize Vercel"
5. You'll see: "Successfully logged in!"
6. Return to terminal
7. Terminal shows: β
Success! GitHub authentication complete Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
vercel login 1. Select "Continue with GitHub"
2. Vercel will open a browser window
3. Browser URL: https://vercel.com/auth/github
4. Click "Authorize Vercel"
5. You'll see: "Successfully logged in!"
6. Return to terminal
7. Terminal shows: β
Success! GitHub authentication complete CODE_BLOCK:
vercel login 1. Select "Continue with GitHub"
2. Vercel will open a browser window
3. Browser URL: https://vercel.com/auth/github
4. Click "Authorize Vercel"
5. You'll see: "Successfully logged in!"
6. Return to terminal
7. Terminal shows: β
Success! GitHub authentication complete CODE_BLOCK:
vercel link Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
vercel link CODE_BLOCK:
vercel link COMMAND_BLOCK:
## Follow These Prompts Exactly: ### Prompt 1: ? Set up "~/nextjs-app"? (Y/n) **Answer:** Type `y` or just press **Enter** (default is Yes) ### Prompt 2 ? Which scope should contain your project? β― yourname (Personal Account) your-team (Team Account) **Answer:** Use arrow keys to select your account, then press **Enter** ### Prompt 3 ? Link to existing project? (y/N) **Answer:** Type `n` or just press **Enter** (we're creating new project) ### Prompt 4 ? What's your project's name? (nextjs-app) **Answer:** Press **Enter** (keep default name) or type a custom name ### Prompt 5 ? In which directory is your code located? (./) **Answer:** Press **Enter** (keep default `./`) ## Expected Success Output β
Linked to yourname/nextjs-app (created .vercel and added it to .gitignore) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
## Follow These Prompts Exactly: ### Prompt 1: ? Set up "~/nextjs-app"? (Y/n) **Answer:** Type `y` or just press **Enter** (default is Yes) ### Prompt 2 ? Which scope should contain your project? β― yourname (Personal Account) your-team (Team Account) **Answer:** Use arrow keys to select your account, then press **Enter** ### Prompt 3 ? Link to existing project? (y/N) **Answer:** Type `n` or just press **Enter** (we're creating new project) ### Prompt 4 ? What's your project's name? (nextjs-app) **Answer:** Press **Enter** (keep default name) or type a custom name ### Prompt 5 ? In which directory is your code located? (./) **Answer:** Press **Enter** (keep default `./`) ## Expected Success Output β
Linked to yourname/nextjs-app (created .vercel and added it to .gitignore) COMMAND_BLOCK:
## Follow These Prompts Exactly: ### Prompt 1: ? Set up "~/nextjs-app"? (Y/n) **Answer:** Type `y` or just press **Enter** (default is Yes) ### Prompt 2 ? Which scope should contain your project? β― yourname (Personal Account) your-team (Team Account) **Answer:** Use arrow keys to select your account, then press **Enter** ### Prompt 3 ? Link to existing project? (y/N) **Answer:** Type `n` or just press **Enter** (we're creating new project) ### Prompt 4 ? What's your project's name? (nextjs-app) **Answer:** Press **Enter** (keep default name) or type a custom name ### Prompt 5 ? In which directory is your code located? (./) **Answer:** Press **Enter** (keep default `./`) ## Expected Success Output β
Linked to yourname/nextjs-app (created .vercel and added it to .gitignore) COMMAND_BLOCK:
# Get Vercel Token
# Go to: https://vercel.com/account/tokens
# Click "Create Token"
# Name: GitHub Actions
# Scope: Full Account
# Copy the token # Get Project ID and Org ID
cat .vercel/project.json Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Get Vercel Token
# Go to: https://vercel.com/account/tokens
# Click "Create Token"
# Name: GitHub Actions
# Scope: Full Account
# Copy the token # Get Project ID and Org ID
cat .vercel/project.json COMMAND_BLOCK:
# Get Vercel Token
# Go to: https://vercel.com/account/tokens
# Click "Create Token"
# Name: GitHub Actions
# Scope: Full Account
# Copy the token # Get Project ID and Org ID
cat .vercel/project.json CODE_BLOCK:
{ "projectId": "prj_abc123xyz", "orgId": "team_xyz789abc"
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ "projectId": "prj_abc123xyz", "orgId": "team_xyz789abc"
} CODE_BLOCK:
{ "projectId": "prj_abc123xyz", "orgId": "team_xyz789abc"
} CODE_BLOCK:
| Secret Name | Value Description | Where to Get It |
|--------------------|-----------------------------|----------------------------------------------|
| VERCEL_TOKEN | Your Vercel personal token | https://vercel.com/account/tokens |
| VERCEL_ORG_ID | Vercel Organization ID | From `.vercel/project.json` β `cat .vercel/project.json` |
| VERCEL_PROJECT_ID | Vercel Project ID | From `.vercel/project.json` β `cat .vercel/project.json` | Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
| Secret Name | Value Description | Where to Get It |
|--------------------|-----------------------------|----------------------------------------------|
| VERCEL_TOKEN | Your Vercel personal token | https://vercel.com/account/tokens |
| VERCEL_ORG_ID | Vercel Organization ID | From `.vercel/project.json` β `cat .vercel/project.json` |
| VERCEL_PROJECT_ID | Vercel Project ID | From `.vercel/project.json` β `cat .vercel/project.json` | CODE_BLOCK:
| Secret Name | Value Description | Where to Get It |
|--------------------|-----------------------------|----------------------------------------------|
| VERCEL_TOKEN | Your Vercel personal token | https://vercel.com/account/tokens |
| VERCEL_ORG_ID | Vercel Organization ID | From `.vercel/project.json` β `cat .vercel/project.json` |
| VERCEL_PROJECT_ID | Vercel Project ID | From `.vercel/project.json` β `cat .vercel/project.json` | COMMAND_BLOCK:
# Make a change
echo "# Test deployment" >> README.md # Commit and push
git add README.md
git commit -m "Test CI/CD pipeline"
git push origin main # Watch workflow
# Go to: GitHub β Actions tab
# Watch the pipeline run:
# β
Test
# β
Build # β
Deploy to Vercel Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Make a change
echo "# Test deployment" >> README.md # Commit and push
git add README.md
git commit -m "Test CI/CD pipeline"
git push origin main # Watch workflow
# Go to: GitHub β Actions tab
# Watch the pipeline run:
# β
Test
# β
Build # β
Deploy to Vercel COMMAND_BLOCK:
# Make a change
echo "# Test deployment" >> README.md # Commit and push
git add README.md
git commit -m "Test CI/CD pipeline"
git push origin main # Watch workflow
# Go to: GitHub β Actions tab
# Watch the pipeline run:
# β
Test
# β
Build # β
Deploy to Vercel CODE_BLOCK:
Local Machine (Ubuntu) β (make changes) β (git commit) β (git push) β
GitHub Repository β (triggers) β
GitHub Actions (if configured) β (or directly) β
Vercel Deployment β (automatic) β
Live Website Updated β
Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Local Machine (Ubuntu) β (make changes) β (git commit) β (git push) β
GitHub Repository β (triggers) β
GitHub Actions (if configured) β (or directly) β
Vercel Deployment β (automatic) β
Live Website Updated β
CODE_BLOCK:
Local Machine (Ubuntu) β (make changes) β (git commit) β (git push) β
GitHub Repository β (triggers) β
GitHub Actions (if configured) β (or directly) β
Vercel Deployment β (automatic) β
Live Website Updated β
COMMAND_BLOCK:
# Navigate to project
cd ~/nextjs-app # Edit the home page
nano app/page.tsx Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Navigate to project
cd ~/nextjs-app # Edit the home page
nano app/page.tsx COMMAND_BLOCK:
# Navigate to project
cd ~/nextjs-app # Edit the home page
nano app/page.tsx COMMAND_BLOCK:
# Check what changed
git status # Stage the changes
git add app/page.tsx # Commit with a message
git commit -m "Update homepage: Changed title and description" # Push to GitHub
git push origin main Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Check what changed
git status # Stage the changes
git add app/page.tsx # Commit with a message
git commit -m "Update homepage: Changed title and description" # Push to GitHub
git push origin main COMMAND_BLOCK:
# Check what changed
git status # Stage the changes
git add app/page.tsx # Commit with a message
git commit -m "Update homepage: Changed title and description" # Push to GitHub
git push origin main CODE_BLOCK:
1. Open browser
2. Go to: https://vercel.com/dashboard
3. Click on your project: sowmiya-next-js-app
4. You'll see: "Building..." 5. Wait 30-60 seconds
6. Status changes to: "Ready" β
7. Click "Visit" to see your changes live! Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
1. Open browser
2. Go to: https://vercel.com/dashboard
3. Click on your project: sowmiya-next-js-app
4. You'll see: "Building..." 5. Wait 30-60 seconds
6. Status changes to: "Ready" β
7. Click "Visit" to see your changes live! CODE_BLOCK:
1. Open browser
2. Go to: https://vercel.com/dashboard
3. Click on your project: sowmiya-next-js-app
4. You'll see: "Building..." 5. Wait 30-60 seconds
6. Status changes to: "Ready" β
7. Click "Visit" to see your changes live! - β
Runs on Ubuntu 24 on port 80 as a background service
- β
Uses TypeScript and Tailwind CSS
- β
Has automated testing with Jest
- β
Auto-deploys to Vercel on every push
- β
Includes GitHub Actions CI/CD pipeline - Next.js 14 (App Router)
- Tailwind CSS
- PM2 (Process Manager)
- GitHub Actions - Ubuntu 24.04 server (local or cloud)
- GitHub account
- Vercel account (free) - Go to: https://github.com/YOUR_USERNAME/nextjs-app
- Click: Settings β Secrets and variables β Actions
- Click: "New repository secret"
- Add three secrets:
how-totutorialguidedev.toaimlubuntuserverbashrouternodesslgitgithub