A cutting-edge, production-ready portfolio website built with Next.js 15, React 18, TypeScript, TailwindCSS, and Framer Motion. This project showcases modern web development practices, including server-side rendering, API routes, email functionality, analytics integration, and stunning animations.
- Live-Demo: https://arnob-mahmud.vercel.app/
- Features
- Tech Stack
- Project Structure
- Installation & Setup
- Environment Variables
- How to Run
- Components Overview
- API Endpoints
- Pages & Routes
- Custom Hooks
- Styling & Animations
- Email Configuration
- Analytics Integration
- Deployment
- Reusable Components Guide
- Best Practices
- Keywords & SEO
- Troubleshooting
- Contributing
- License
- β‘ Next.js 15 with App Router and Server Components
- π¨ Modern UI/UX with TailwindCSS and Shadcn UI components
- π Smooth Animations powered by Framer Motion
- π± Fully Responsive design for all devices
- π Dark Mode optimized color scheme
- π Type-Safe with TypeScript
- π§ Contact Form with email notifications and auto-reply
- π Analytics with Google Analytics & Vercel Analytics
- π― SEO Optimized with meta tags and Open Graph
- βΏ Accessible components following WCAG guidelines
- π Fast Performance with optimized images and lazy loading
- πͺ Typewriter Effect on homepage hero section
- π Project Carousel with Swiper.js
- π Grid/List View Toggle for projects showcase
- π Animated Counter for statistics
- π¬ Page Transitions with smooth animations
- πͺ Stair Transition Effect between pages
- π Scroll-to-Top button functionality
- π― Custom Tooltips for enhanced UX
- π± Mobile Navigation with hamburger menu
- π Loading States for async operations
- Framework: Next.js 15.0.0 (React 18.3.1)
- Language: TypeScript 5.7.2
- Styling: TailwindCSS 3.4.17
- Animations: Framer Motion 12.23.24
- UI Components: Radix UI, Shadcn UI
- Icons: React Icons 5.5.0, Lucide React 0.546.0
- Carousel: Swiper 12.0.2
- Runtime: Node.js
- Email Service: Nodemailer 7.0.9
- HTTP Client: Axios 1.12.2
- Email Templates: @react-email/render 1.4.0
- Web Analytics: Google Analytics 4
- Performance: Vercel Analytics 1.5.0
- Package Manager: npm/yarn/pnpm
- Linting: ESLint 9.17.0
- Build Tool: Turbopack (Next.js 15)
- Deployment: Vercel
portfolio-arnob-new/
βββ app/ # Next.js App Router
β βββ api/ # API Routes
β β βββ send-email/ # Main contact form handler
β β β βββ route.ts
β β βββ send-auto-reply/ # Auto-reply email handler
β β βββ route.ts
β βββ contact/ # Contact page
β β βββ page.tsx
β βββ resume/ # Resume/CV page
β β βββ page.tsx
β βββ services/ # Services offered page
β β βββ page.tsx
β βββ work/ # Portfolio/Projects page
β β βββ page.tsx
β βββ globals.css # Global styles & animations
β βββ layout.tsx # Root layout with metadata
β βββ page.tsx # Homepage
β
βββ components/ # React Components
β βββ pages/ # Page-specific components
β β βββ ContactPage.tsx # Contact form with validation
β β βββ HomePage.tsx # Hero section with typewriter
β β βββ ResumePage.tsx # Tabbed resume/skills section
β β βββ ServicesPage.tsx # Service cards grid
β β βββ WorkPage.tsx # Projects showcase with carousel
β β
β βββ ui/ # Reusable UI components (Shadcn)
β β βββ alert.tsx # Alert/notification component
β β βββ button.tsx # Custom button variants
β β βββ card.tsx # Card component
β β βββ input.tsx # Form input field
β β βββ scroll-area.tsx # Custom scrollbar
β β βββ select.tsx # Dropdown select
β β βββ sheet.tsx # Mobile navigation sheet
β β βββ tabs.tsx # Tab navigation
β β βββ textarea.tsx # Multi-line input
β β βββ tooltip.tsx # Tooltip component
β β
β βββ GoogleAnalytics.tsx # GA4 integration
β βββ Header.tsx # Main navigation header
β βββ MobileNav.tsx # Mobile hamburger menu
β βββ Nav.tsx # Desktop navigation links
β βββ PageTransition.tsx # Page animation wrapper
β βββ Photo.tsx # Profile photo with effects
β βββ ScrollToTop.tsx # Scroll-to-top button
β βββ Social.tsx # Social media links
β βββ Stairs.tsx # Stair animation component
β βββ StairTransition.tsx # Stair transition wrapper
β βββ Stats.tsx # Animated statistics counter
β βββ WorkSliderBtns.tsx # Carousel navigation buttons
β
βββ hooks/ # Custom React Hooks
β βββ useTypewriter.ts # Typewriter text effect hook
β
βββ lib/ # Utility functions
β βββ utils.ts # Helper functions (cn, etc.)
β
βββ public/ # Static assets
β βββ assets/ # Images, icons, etc.
β β βββ resume/ # Resume-related assets
β β βββ skills/ # Skill icons
β β βββ work/ # Project screenshots
β βββ favicon.ico # Site favicon
β βββ photo.png # Profile photo
β
βββ .env.local # Environment variables (not in repo)
βββ .gitignore # Git ignore rules
βββ eslint.config.mjs # ESLint configuration
βββ global.d.ts # Global TypeScript declarations
βββ next-env.d.ts # Next.js TypeScript definitions
βββ next.config.js # Next.js configuration
βββ package.json # Project dependencies
βββ postcss.config.mjs # PostCSS configuration
βββ tailwind.config.js # TailwindCSS configuration
βββ tsconfig.json # TypeScript configuration
βββ README.md # This fileBefore you begin, ensure you have the following installed:
- Node.js 18.17 or higher
- npm (comes with Node.js) or yarn or pnpm
- Git for version control
- A Gmail account (for email functionality)
- A Google Analytics 4 account (optional, for analytics)
# Clone using HTTPS
git clone https://github.com/arnobt78/MyPortfolio--NextJS-FullStack-Website.git
# Or clone using SSH
git clone git@github.com:arnobt78/MyPortfolio--NextJS-FullStack-Website.git
# Navigate to project directory
cd MyPortfolio--NextJS-FullStack-Website# Using npm
npm install
# Or using yarn
yarn install
# Or using pnpm
pnpm installThis will install all the required dependencies listed in package.json.
Create a .env.local file in the root directory of your project. This file contains sensitive information and should never be committed to version control.
# =================================
# EMAIL CONFIGURATION (Required)
# =================================
# Your Gmail address (used for sending/receiving contact form emails)
EMAIL_USER=your-email@gmail.com
# Gmail App Password (NOT your regular Gmail password)
# Generate this from: https://myaccount.google.com/apppasswords
EMAIL_PASS=your-16-character-app-password
# =================================
# GOOGLE ANALYTICS (Optional)
# =================================
# Google Analytics 4 Measurement ID
# Find this in GA4: Admin > Data Streams > Your Stream
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX- Simply use your existing Gmail address
- Example:
your-email@gmail.com
Important: This is NOT your regular Gmail password. It's a special 16-character password generated by Google.
Steps to generate:
- Go to Google Account Settings
- Click on Security in the left sidebar
- Enable 2-Step Verification (required for App Passwords)
- Once 2FA is enabled, return to Security settings
- Scroll down to App passwords (may appear after 2FA setup)
- Click App passwords
- Select Mail as the app
- Select Other (Custom name) as the device
- Enter a name like "Portfolio Website"
- Click Generate
- Copy the 16-character password (format:
xxxx xxxx xxxx xxxx) - Remove spaces and use it as
EMAIL_PASS
Example: abcdwxyzpqrsjklm
Steps to get your Measurement ID:
- Go to Google Analytics
- Create an account if you don't have one
- Create a new GA4 Property (not Universal Analytics)
- Navigate to Admin (gear icon at bottom left)
- Under Property, click Data Streams
- Click on your Web stream
- Find Measurement ID (format:
G-XXXXXXXXXX) - Copy and paste into
.env.local
Example: G-7CTQNDTW0G
.env.local to Git!
The .gitignore file is already configured to exclude:
.env.local.env.development.local.env.test.local.env.production.local
If you accidentally commit sensitive data:
- Remove it from Git history
- Immediately regenerate all compromised credentials
- Update
.env.localwith new credentials
Start the development server with hot-reload:
# Using npm
npm run dev
# Using yarn
yarn dev
# Using pnpm
pnpm devThe application will be available at:
- Local: http://localhost:3000
- Network: Check terminal for network URL
If you encounter cache issues, run:
npm run dev:cleanThis removes the .next folder and starts a fresh development build.
Build the application for production:
# Build the project
npm run build
# Start production server
npm run startCheck code quality and fix issues:
npm run lintPurpose: Landing page hero section with introduction and call-to-action.
Key Features:
- Typewriter effect for name animation
- Profile photo with circular border effect
- Download resume button
- Social media links
- Animated statistics counter
Reusability:
import HomePage from "@/components/pages/HomePage";
// Use in any page
export default function Home() {
return <HomePage />;
}Customization:
- Edit personal information in the component
- Update resume link in the Button href
- Modify typewriter text in
useTypewriterhook - Change animation delays in style props
Purpose: Display services offered in a grid layout with hover effects.
Key Features:
- Responsive grid (1 column mobile, 2 columns desktop)
- Hover effects with color transitions
- Arrow icon that rotates on hover
- Service cards with numbering
Data Structure:
interface Service {
num: string; // Service number (e.g., "01")
title: string; // Service title
description: string; // Service description
href: string; // Link to contact page
}How to Add/Edit Services:
const services: Service[] = [
{
num: "01",
title: "Web Development",
description: "Building modern web applications...",
href: "/contact",
},
// Add more services here
];Reusability in Other Projects:
- Extract the service card into a separate component
- Pass services array as props
- Style using Tailwind utility classes
Purpose: Tabbed interface displaying resume, experience, education, and skills.
Key Features:
- Tab navigation (About, Experience, Education, Skills)
- Scrollable content areas
- Icon-based skill display with tooltips
- Timeline cards for experience/education
Data Structures:
// About section
interface InfoItem {
fieldName: string;
fieldValue: string;
}
// Experience section
interface ExperienceItem {
company: string;
position: string;
duration: string;
}
// Skills section
interface SkillItem {
icon: JSX.Element;
name: string;
}How to Update Content:
- Add Experience:
const experience: ExperienceData = {
items: [
{
company: "Your Company",
position: "Your Position",
duration: "Jan 2024 - Present",
},
],
};- Add Skills:
import { FaReact } from "react-icons/fa";
const skills: SkillsData = {
skillList: [{ icon: <FaReact />, name: "React.js" }],
};Reusability:
- Convert to a generic tabbed component
- Pass data as props
- Use in team pages, product showcases, etc.
Purpose: Showcase portfolio projects with grid/list view toggle.
Key Features:
- Swiper carousel for image slideshow
- Grid view (original) and list view modes
- Project filtering by category
- Live demo and GitHub repository links
- Technology stack display
Project Data Structure:
interface Project {
num: string; // Project number
category: string; // Frontend/Fullstack/Backend
title: string; // Project name
description: string; // Detailed description
stack: ProjectStack[]; // Technologies used
image: string; // Screenshot path
live: string; // Live demo URL
github: string; // GitHub repository URL
}How to Add New Projects:
const projects: Project[] = [
{
num: "01",
category: "Fullstack",
title: "Your Project Name",
description: "Detailed project description...",
stack: [{ name: "Next.js" }, { name: "TypeScript" }],
image: "/assets/work/project-image.png",
live: "https://your-live-demo.com",
github: "https://github.com/yourusername/repo",
},
];View Mode Toggle:
- Grid View: Carousel with single project focus
- List View: All projects in scrollable list
Reusability:
- Perfect for freelancer portfolios
- Agency project showcases
- Product galleries
- Case study presentations
Purpose: Contact form with email functionality and validation.
Key Features:
- Form validation (required fields, email format)
- Loading states during submission
- Success/error alerts with icons
- Auto-reply email to user
- Smooth scroll to alert message
- Contact information display
Form Data Structure:
interface FormData {
fullname: string;
email: string;
message: string;
}API Integration:
// Send main email
const response = await axios.post("/api/send-email", formData);
// Send auto-reply
const autoReply = await axios.post("/api/send-auto-reply", formData);Error Handling:
- Network errors
- Authentication errors
- Validation errors
- Timeout errors
Reusability:
- Extract form into separate component
- Add file upload capability
- Integrate with other backend services (Firebase, Supabase)
- Add CAPTCHA for spam protection
These are Shadcn UI components - fully customizable, accessible, and ready to use.
Versatile button component with multiple variants.
Variants:
default- Primary accent buttondestructive- Red danger buttonoutline- Bordered buttonsecondary- Muted secondary buttonghost- Transparent buttonlink- Link-styled button
Sizes:
default- Standard sizesm- Small buttonlg- Large buttonicon- Square icon button
Usage:
import { Button } from "@/components/ui/button";
<Button variant="default" size="lg">
Click Me
</Button>
<Button variant="outline" size="sm">
Secondary Action
</Button>Styled text input field with focus states.
Usage:
import { Input } from "@/components/ui/input";
<Input
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>;Multi-line text input for longer content.
Usage:
import { Textarea } from "@/components/ui/textarea";
<Textarea placeholder="Your message" className="h-[200px]" />;Notification component for success/error messages.
Variants:
default- Blue informationaldestructive- Red errorsuccess- Green success (custom)
Usage:
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
<Alert variant="success">
<AlertTitle>Success!</AlertTitle>
<AlertDescription>Your message has been sent.</AlertDescription>
</Alert>;Tabbed interface for organizing content.
Usage:
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content for Tab 1</TabsContent>
<TabsContent value="tab2">Content for Tab 2</TabsContent>
</Tabs>;Hover tooltip for additional information.
Usage:
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
<TooltipProvider>
<Tooltip>
<TooltipTrigger>Hover me</TooltipTrigger>
<TooltipContent>
<p>Tooltip content here</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>;Main navigation header with logo and links.
Features:
- Logo with accent dot
- Desktop navigation menu
- Mobile hamburger menu
- "Hire me" CTA button
Customization:
// Change logo text
<h1 className="text-4xl font-semibold">
YourBrand<span className="text-accent">.</span>
</h1>Desktop navigation links with active state highlighting.
Navigation Links:
const links = [
{ name: "home", path: "/" },
{ name: "services", path: "/services" },
{ name: "resume", path: "/resume" },
{ name: "work", path: "/work" },
{ name: "contact", path: "/contact" },
];How to Add Links:
Simply add to the links array with name and path.
Mobile-friendly sheet navigation.
Features:
- Hamburger icon trigger
- Slide-in navigation sheet
- Logo display
- Close button
- Mobile-optimized link styles
Social media icon links.
Social Platforms:
const socials = [
{ icon: <FaGithub />, path: "https://github.com/username" },
{ icon: <FaLinkedinIn />, path: "https://linkedin.com/in/username" },
{ icon: <FaYoutube />, path: "https://youtube.com/@username" },
{ icon: <FaInstagram />, path: "https://instagram.com/username" },
];Usage:
<Social
containerStyles="flex gap-6"
iconStyles="w-9 h-9 border border-accent rounded-full flex justify-center items-center text-accent hover:bg-accent hover:text-primary transition-all duration-500"
/>Animated statistics counter.
Features:
- Counts from start value to target value
- Smooth easing animation
- Responsive grid layout
- Customizable duration
Data Structure:
const stats = [
{
num: 5, // Target number
text: "Years of Experience",
startFrom: 0, // Starting number
},
];How It Works:
Uses requestAnimationFrame for smooth 60fps animation with ease-out curve.
Profile photo with circular border animation.
Features:
- Circular shape with rotating border effect
- Responsive sizing
- Image optimization with Next.js Image
Wrapper component for page transition animations.
Usage:
import PageTransition from "@/components/PageTransition";
export default function RootLayout({ children }) {
return <PageTransition>{children}</PageTransition>;
}Animation:
Uses Framer Motion's AnimatePresence with fade and slide effects.
Creative stair-step page transition effect.
How It Works:
- Creates multiple div elements
- Animates them in sequence
- Creates a "stair" effect during page transitions
Button that appears when scrolling down, returns to top when clicked.
Features:
- Only visible after scrolling 300px
- Smooth scroll to top
- Fixed position in bottom-right corner
- Fade in/out animation
Google Analytics 4 integration component.
Features:
- Loads GA4 script
- Initializes
gtagfunction - Logs status in development mode
- Silent fail for ad blockers
Usage:
Already included in layout.tsx root layout.
Purpose: Sends contact form submission to your email address.
Request Body:
{
fullname: string; // User's name (1-100 characters)
email: string; // User's email (valid format)
message: string; // User's message (1-5000 characters)
}Response (Success):
{
message: "Email sent successfully";
}Response (Error):
{
error: "Validation failed" | "Authentication failed" | "Connection failed",
details: string // Specific error message
}Validation Rules:
- All fields required
- Email must be valid format
- Name: 1-100 characters
- Message: 1-5000 characters
- Input sanitization to prevent XSS
Email Configuration:
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 587,
secure: false,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});Error Handling:
EAUTH- Authentication failedECONNECTION- Connection failed- Invalid email format
- Missing fields
Purpose: Sends automatic confirmation email to user after form submission.
Request Body:
{
fullname: string;
email: string;
message: string;
}Response (Success):
{
message: "Auto-reply sent successfully",
referenceNumber: string // Format: ARN-{timestamp}-{random}
}Email Template Features:
- Professional HTML email design
- Reference number for tracking
- Message preview (truncated at 200 chars)
- Submission date
- Contact information
- Brand colors and fonts
- Responsive design
Sample Reference Number:
ARN-1729699200000-742HTML Email Template:
- Header with gradient background
- Message preview box
- Reference number display
- Next steps section
- Contact information footer
- Disclaimer section
Next.js 15 uses the App Router with file-based routing.
| Route | File | Component | Description |
|---|---|---|---|
/ |
app/page.tsx |
HomePage |
Landing page with hero |
/services |
app/services/page.tsx |
ServicesPage |
Services offered |
/resume |
app/resume/page.tsx |
ResumePage |
Resume/Skills |
/work |
app/work/page.tsx |
WorkPage |
Portfolio projects |
/contact |
app/contact/page.tsx |
ContactPage |
Contact form |
Example: Add /blog page
- Create folder and file:
mkdir app/blog
touch app/blog/page.tsx- Create page component:
// app/blog/page.tsx
export default function BlogPage() {
return (
<div>
<h1>Blog</h1>
{/* Your blog content */}
</div>
);
}- Add to navigation:
// components/Nav.tsx
const links = [
// ... existing links
{ name: "blog", path: "/blog" },
];Example: Blog post with dynamic slug
mkdir -p app/blog/[slug]
touch app/blog/[slug]/page.tsx// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
return (
<div>
<h1>Blog Post: {params.slug}</h1>
</div>
);
}Access at: /blog/my-first-post
File: hooks/useTypewriter.ts
Purpose: Creates a typewriter text effect with customizable speed and delay.
Interface:
interface UseTypewriterOptions {
text: string; // Text to animate
speed?: number; // Typing speed in ms (default: 100)
delay?: number; // Initial delay before typing starts (default: 0)
}
interface UseTypewriterReturn {
displayText: string; // Currently displayed text
isComplete: boolean; // Whether animation is complete
}Usage:
import { useTypewriter } from "@/hooks/useTypewriter";
function MyComponent() {
const { displayText, isComplete } = useTypewriter({
text: "Hello, World!",
speed: 150,
delay: 1000,
});
return (
<h1>
{displayText}
{!isComplete && <span className="cursor">|</span>}
</h1>
);
}How It Works:
- Delays initial render by
delaymilliseconds - Types one character every
speedmilliseconds - Updates
displayTextstate progressively - Sets
isCompletetotruewhen finished
Reusability:
- Hero headlines
- Product descriptions
- Loading messages
- Interactive tutorials
- Chat interfaces
Customization Ideas:
- Add backspace/delete effect
- Loop animation
- Type multiple strings in sequence
- Add realistic typing pauses
File: tailwind.config.js
Custom Theme:
theme: {
extend: {
colors: {
primary: "#1c1c22",
accent: {
DEFAULT: "#00ff99",
hover: "#00e187",
},
},
fontFamily: {
primary: "var(--font-jetbrainsMono)",
},
}
}Custom Animations:
keyframes: {
"fade-in": {
from: { opacity: "0" },
to: { opacity: "1" },
},
"ease-in-out": {
"0%": { opacity: "0", transform: "translateY(10px)" },
"100%": { opacity: "1", transform: "translateY(0)" },
},
}File: app/globals.css
Key Features:
- Custom animations with delays
- Scrollbar styling
- Text outline effects
- Typewriter cursor animation
Typewriter Cursor:
.typewriter-cursor {
display: inline-block;
width: 2px;
height: 1em;
background-color: #00ff99;
margin-left: 2px;
animation: blink 1s infinite;
}
@keyframes blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}Custom Scrollbar:
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #1c1c22;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #00ff99;
border-radius: 4px;
}Page Transitions:
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>Hover Effects:
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
Click me
</motion.div>Staggered Children:
<motion.div
variants={{
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
}}
>
{items.map((item) => (
<motion.div
variants={{
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
}}
>
{item}
</motion.div>
))}
</motion.div>Step 1: Enable 2-Step Verification
- Go to Google Account Security
- Click "2-Step Verification"
- Follow the setup wizard
Step 2: Generate App Password
- Return to Security settings
- Click "App passwords"
- Select "Mail" and "Other (Custom name)"
- Enter "Portfolio Website"
- Click "Generate"
- Copy the 16-character password
- Add to
.env.localasEMAIL_PASS
Step 3: Verify Configuration
// Test email configuration
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 587,
secure: false,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
// Verify connection
await transporter.verify();const transporter = nodemailer.createTransport({
host: "smtp.mail.yahoo.com",
port: 587,
secure: false,
auth: {
user: "youremail@yahoo.com",
pass: "your-app-password",
},
});const transporter = nodemailer.createTransport({
host: "smtp-mail.outlook.com",
port: 587,
secure: false,
auth: {
user: "youremail@outlook.com",
pass: "your-password",
},
});const transporter = nodemailer.createTransport({
host: "smtp.yourdomain.com",
port: 465,
secure: true,
auth: {
user: "youremail@yourdomain.com",
pass: "your-password",
},
});Error: "Invalid login"
- Ensure 2FA is enabled
- Regenerate app password
- Check for typos in
.env.local
Error: "Connection timeout"
- Check firewall settings
- Verify port 587 is open
- Try port 465 with
secure: true
Emails going to spam
- Add SPF record to your domain
- Set up DKIM authentication
- Use authenticated sender in "from" field
Setup in Project:
- Component already created:
components/GoogleAnalytics.tsx - Imported in
app/layout.tsx - Uses
NEXT_PUBLIC_GA_MEASUREMENT_IDfrom.env.local
Tracking Events:
// Custom event tracking
window.gtag("event", "button_click", {
event_category: "engagement",
event_label: "hire_me_button",
value: 1,
});Page Views: Automatically tracked on route changes.
View Reports:
- Go to Google Analytics
- Select your property
- Navigate to "Reports" > "Realtime" for live data
- Navigate to "Reports" > "Engagement" for detailed analytics
Setup:
Already integrated in app/layout.tsx:
import { Analytics } from "@vercel/analytics/react";
export default function RootLayout({ children }) {
return (
<html>
<body>
<Analytics />
{children}
</body>
</html>
);
}Features:
- Automatic page view tracking
- Web Vitals monitoring (CLS, FID, LCP, FCP, TTFB)
- No configuration needed
- Works automatically on Vercel deployments
View Analytics:
- Go to Vercel Dashboard
- Select your project
- Click "Analytics" tab
- View traffic, performance, and Web Vitals
Option 1: Deploy via GitHub (Automatic)
- Push to GitHub:
git add .
git commit -m "Initial commit"
git push origin main-
Connect to Vercel:
- Go to Vercel
- Click "Add New" > "Project"
- Import your GitHub repository
- Vercel auto-detects Next.js
-
Configure Environment Variables:
- Click "Environment Variables"
- Add all variables from
.env.local:EMAIL_USEREMAIL_PASSNEXT_PUBLIC_GA_MEASUREMENT_ID
- Click "Deploy"
-
Auto-Deployment:
- Every push to
mainbranch triggers new deployment - Pull requests create preview deployments
- Every push to
Option 2: Deploy via Vercel CLI
# Install Vercel CLI
npm install -g vercel
# Login to Vercel
vercel login
# Deploy
vercel
# Deploy to production
vercel --prod-
Build Settings:
- Build command:
npm run build - Publish directory:
.next
- Build command:
-
Environment Variables: Add all variables from
.env.localin Netlify dashboard -
Netlify Configuration: Create
netlify.toml:
[build]
command = "npm run build"
publish = ".next"
[[plugins]]
package = "@netlify/plugin-nextjs"- Connect GitHub repository
- Configure build settings:
- Build command:
npm run build - Output directory:
.next
- Build command:
- Add environment variables
- Deploy
Requirements:
- Node.js 18+ installed
- PM2 for process management
- Nginx for reverse proxy
Steps:
- Build the application:
npm run build- Start with PM2:
# Install PM2
npm install -g pm2
# Start application
pm2 start npm --name "portfolio" -- start
# Save PM2 configuration
pm2 save
# Set up auto-start
pm2 startup- Configure Nginx:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}- Enable SSL with Certbot:
sudo certbot --nginx -d yourdomain.comExtract from ServicesPage:
// components/ServiceCard.tsx
import Link from "next/link";
import { BsArrowDownRight } from "react-icons/bs";
interface ServiceCardProps {
num: string;
title: string;
description: string;
href: string;
}
export default function ServiceCard({
num,
title,
description,
href,
}: ServiceCardProps) {
return (
<div className="flex-1 flex flex-col justify-between gap-4 group">
<div className="w-full flex justify-between items-center">
<div className="text-5xl font-extrabold text-outline text-transparent group-hover:text-outline-hover transition-all duration-500">
{num}
</div>
<Link
href={href}
className="w-[50px] h-[50px] rounded-full bg-white group-hover:bg-accent transition-all duration-500 flex justify-center items-center hover:-rotate-45"
>
<BsArrowDownRight className="text-primary text-3xl" />
</Link>
</div>
<h2 className="text-[32px] font-bold leading-none text-white group-hover:text-accent transition-all duration-500">
{title}
</h2>
<p className="text-white/60 text-justify">{description}</p>
<div className="border-b border-white/20 w-full"></div>
</div>
);
}Usage:
import ServiceCard from '@/components/ServiceCard';
const services = [...];
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-16">
{services.map((service, index) => (
<ServiceCard key={index} {...service} />
))}
</div>
);Extract from ContactPage:
// components/ContactForm.tsx
import { useState } from "react";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Textarea } from "./ui/textarea";
interface ContactFormProps {
onSubmit: (data: FormData) => Promise<void>;
loading?: boolean;
}
interface FormData {
fullname: string;
email: string;
message: string;
}
export default function ContactForm({
onSubmit,
loading = false,
}: ContactFormProps) {
const [formData, setFormData] = useState<FormData>({
fullname: "",
email: "",
message: "",
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await onSubmit(formData);
setFormData({ fullname: "", email: "", message: "" });
};
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<Input
type="text"
name="fullname"
placeholder="Enter your name"
value={formData.fullname}
onChange={(e) =>
setFormData((prev) => ({ ...prev, fullname: e.target.value }))
}
required
/>
<Input
type="email"
name="email"
placeholder="Enter your email"
value={formData.email}
onChange={(e) =>
setFormData((prev) => ({ ...prev, email: e.target.value }))
}
required
/>
<Textarea
name="message"
placeholder="Type your message here"
value={formData.message}
onChange={(e) =>
setFormData((prev) => ({ ...prev, message: e.target.value }))
}
className="h-[200px]"
required
/>
<Button type="submit" disabled={loading}>
{loading ? "Sending..." : "Send Message"}
</Button>
</form>
);
}Make Stats.tsx more flexible:
// components/StatsCounter.tsx
interface Stat {
num: number;
text: string;
startFrom?: number;
}
interface StatsCounterProps {
stats: Stat[];
columns?: number;
}
export default function StatsCounter({
stats,
columns = 4,
}: StatsCounterProps) {
const gridClass = `grid-cols-${columns}`;
return (
<div className={`grid ${gridClass} gap-6`}>
{stats.map((stat, index) => (
<StatItem key={index} {...stat} />
))}
</div>
);
}Usage in different projects:
// E-commerce dashboard
const ecommerceStats = [
{ num: 1250, text: "Total Orders" },
{ num: 45678, text: "Revenue ($)" },
{ num: 98, text: "Customer Satisfaction (%)" },
];
<StatsCounter stats={ecommerceStats} columns={3} />;
// Blog analytics
const blogStats = [
{ num: 50000, text: "Monthly Visitors" },
{ num: 234, text: "Published Articles" },
];
<StatsCounter stats={blogStats} columns={2} />;β Make components prop-driven
- Accept data via props instead of hardcoding
- Use TypeScript interfaces for type safety
β Keep components focused
- Single responsibility principle
- Separate logic from presentation
β Use composition over inheritance
- Build complex UIs from simple components
- Use children prop for flexibility
β Document component APIs
- Add JSDoc comments
- Provide usage examples
- List all props and their types
β Style with flexibility
- Accept className prop for custom styling
- Use Tailwind utility classes
- Support theme variants
β Handle edge cases
- Empty states
- Loading states
- Error states
1. Component Structure:
Component/
βββ Component.tsx # Main component
βββ Component.test.tsx # Unit tests
βββ Component.stories.tsx # Storybook stories (optional)
βββ index.ts # Export file2. Import Order:
// 1. React and Next.js imports
import { useState } from "react";
import Link from "next/link";
// 2. Third-party libraries
import axios from "axios";
import { motion } from "framer-motion";
// 3. Internal components
import { Button } from "@/components/ui/button";
// 4. Utilities and helpers
import { cn } from "@/lib/utils";
// 5. Types and interfaces
import type { FormData } from "@/types";
// 6. Styles and assets
import "./styles.css";3. TypeScript Best Practices:
- Always define interfaces for props
- Use
typefor unions and intersections - Avoid
anytype - Enable strict mode
4. Performance Optimization:
- Use
React.memofor expensive components - Implement code splitting with
dynamicimports - Optimize images with Next.js
Imagecomponent - Lazy load components below the fold
5. Accessibility:
- Use semantic HTML elements
- Add ARIA labels where needed
- Ensure keyboard navigation
- Maintain color contrast ratios
- Test with screen readers
6. SEO Best Practices:
- Use Next.js metadata API
- Include Open Graph tags
- Add structured data (JSON-LD)
- Create sitemap.xml
- Implement canonical URLs
File: app/layout.tsx
export const metadata: Metadata = {
metadataBase: new URL("https://arnob-mahmud.vercel.app"),
title: "Arnob Mahmud | Full-Stack Developer | Portfolio",
description: "Professional portfolio showcasing web development projects...",
keywords: [
"Arnob Mahmud",
"Full-Stack Developer",
"Web Developer",
"React",
"Next.js",
"TypeScript",
"Portfolio",
],
authors: [{ name: "Arnob Mahmud" }],
openGraph: {
title: "Arnob Mahmud | Full-Stack Developer",
description: "Portfolio of Arnob Mahmud...",
url: "https://arnob-mahmud.vercel.app",
siteName: "Arnob Mahmud Portfolio",
images: [{ url: "/assets/photo.png" }],
locale: "en_US",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Arnob Mahmud | Full-Stack Developer",
description: "Portfolio of Arnob Mahmud...",
images: ["/assets/photo.png"],
},
};General:
- Full-Stack Developer
- Web Developer
- Software Engineer
- Frontend Developer
- Backend Developer
- Portfolio Website
Technologies:
- React.js
- Next.js
- TypeScript
- JavaScript
- Node.js
- TailwindCSS
- Framer Motion
Services:
- Web Development
- UI/UX Design
- API Development
- Database Design
- Cloud Deployment
- SEO Optimization
Location-based:
- [Frankfurt] Developer
- [Germany] Web Developer
- Remote Developer
Symptoms:
- "Authentication failed" error
- "Connection timeout" error
- Emails not arriving
Solutions:
# Check environment variables
echo $EMAIL_USER
echo $EMAIL_PASS
# Verify .env.local exists
ls -la .env.local
# Restart development server
npm run dev:cleanChecklist:
- β 2FA enabled on Gmail
- β App password generated (not regular password)
- β No spaces in app password
- β
Correct email in
EMAIL_USER - β Port 587 not blocked by firewall
Symptoms:
- No data in GA4 dashboard
- Real-time reports empty
Solutions:
- Check Measurement ID:
# .env.local
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX- Verify in Browser Console:
// Check if gtag is loaded
console.log(window.gtag);
console.log(window.dataLayer);- Disable Ad Blocker:
- Ad blockers prevent GA from loading
- Test in incognito mode without extensions
- Wait 24-48 hours:
- GA4 data processing takes time
- Real-time reports update faster
Error: "Module not found"
# Clear node_modules and reinstall
rm -rf node_modules
rm package-lock.json
npm installError: "Type error in component"
# Check TypeScript errors
npm run build
# Fix type errors in the reported filesError: "Cannot find module './public/...'"
# Ensure asset files exist
ls -la public/assets/
# Check file paths are correct (case-sensitive)Tailwind classes not working:
# Restart development server
npm run dev:clean
# Check tailwind.config.js includes your files
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
]Custom fonts not loading:
// Verify font is imported in layout.tsx
import { JetBrains_Mono } from "next/font/google";
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-jetbrainsMono",
});Environment variables not working:
- Go to Vercel Dashboard
- Project Settings β Environment Variables
- Add all variables
- Redeploy the project
Build fails on Vercel:
# Check build works locally
npm run build
# Review build logs in Vercel dashboard
# Fix reported errorsWe welcome contributions! Here's how you can help:
- Check if the bug is already reported in Issues
- Create a new issue with:
- Clear title
- Steps to reproduce
- Expected vs actual behavior
- Screenshots (if applicable)
- Environment details (OS, browser, Node version)
- Open a new issue with the label "enhancement"
- Describe the feature and its benefits
- Provide examples or mockups
- Fork the repository
# Click "Fork" on GitHub
git clone https://github.com/yourusername/MyPortfolio--NextJS-FullStack-Website.git- Create a feature branch
git checkout -b feature/amazing-feature- Make your changes
- Follow existing code style
- Add comments for complex logic
- Update documentation if needed
- Commit your changes
git add .
git commit -m "feat: add amazing feature"Commit Message Format:
feat:- New featurefix:- Bug fixdocs:- Documentation changesstyle:- Code style changes (formatting)refactor:- Code refactoringtest:- Adding testschore:- Maintenance tasks
- Push to your fork
git push origin feature/amazing-feature- Open a Pull Request
- Go to original repository on GitHub
- Click "New Pull Request"
- Select your branch
- Describe your changes
- Wait for review
This project is open-source and available under the MIT License.
MIT License
Copyright (c) 2025 Arnob Mahmud
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.What this means:
- β Free to use for personal and commercial projects
- β Modify and distribute as you wish
- β Include in private and public repositories
β οΈ Must include the license noticeβ οΈ No warranty provided
Special Thanks To:
- Next.js Team - For the incredible React framework
- Vercel - For seamless deployment platform
- Shadcn - For beautiful UI components
- Framer Motion - For smooth animations
- TailwindCSS - For utility-first CSS framework
- React Icons - For comprehensive icon library
Inspiration & Resources:
Arnob Mahmud
- π§ Email: arnobt78@gmail.com
- π Portfolio: https://arnob-mahmud.vercel.app/
- πΌ LinkedIn: linkedin.com/in/arnob-mahmud-05839655/
- π GitHub: github.com/arnobt78
- π± Phone: +49 157 34664351
- π Location: Frankfurt am Main, Germany
If you find this project helpful:
- β Star the repository on GitHub
- π Report bugs and suggest features
- π€ Contribute via pull requests
- π’ Share with other developers
- π¬ Provide feedback and testimonials
Current Version: 1.0.0
Status: β Production Ready
Last Updated: October 2025
Roadmap:
- Add blog functionality
- Implement i18n (internationalization)
- Add dark/light theme toggle
- Create admin dashboard
- Integrate CMS (Sanity/Contentful)
- Add unit tests (Jest/React Testing Library)
- Implement E2E tests (Playwright/Cypress)
- Add PWA support
- Optimize for Core Web Vitals
- Add more project categories
Built With This Template:
Have you built something amazing with this portfolio template? Let me know!
Send your project details to arnobt78@gmail.com to be featured here.
Recommended for Beginners:
- Next.js Learn Course - Official interactive course
- React Tutorial - Official React documentation
- TypeScript Handbook
- TailwindCSS Course
- Framer Motion Tutorial
YouTube Channels:
Feel free to use this project repository and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.
Enjoy building and learning! π
Thank you! π




