Your portfolio is often the first impression potential clients or employers have of your work. In today's competitive landscape, having a well-designed, fast, and responsive portfolio website is crucial for standing out.
Why Choose NextJS for Your Portfolio?
NextJS offers several advantages for portfolio websites:
- Performance: Built-in optimization and static generation
- SEO: Server-side rendering for better search visibility
- Developer Experience: Hot reload, TypeScript support, and modern tooling
- Flexibility: Can be deployed anywhere and scales easily
Essential Portfolio Sections
Hero Section
Your hero section should immediately communicate who you are and what you do.
export default function Hero() {
return (
<section className="flex min-h-screen items-center justify-center">
<div className="space-y-6 text-center">
<h1 className="text-4xl font-bold md:text-6xl">
Hi, I'm <span className="text-blue-600">John Doe</span>
</h1>
<p className="mx-auto max-w-2xl text-xl text-gray-600">
Full-stack developer passionate about creating beautiful, functional
web experiences
</p>
<div className="flex justify-center gap-4">
<Button variant="primary">View My Work</Button>
<Button variant="outline">Contact Me</Button>
</div>
</div>
</section>
);
}
Project Showcase
Display your best work with engaging visuals and clear descriptions.
interface Project {
id: string;
title: string;
description: string;
image: string;
technologies: string[];
liveUrl?: string;
githubUrl?: string;
}
export default function ProjectGrid({ projects }: { projects: Project[] }) {
return (
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{projects.map((project) => (
<div
key={project.id}
className="group relative overflow-hidden rounded-lg"
>
<img
src={project.image}
alt={project.title}
className="h-48 w-full object-cover transition-transform group-hover:scale-105"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent opacity-0 transition-opacity group-hover:opacity-100">
<div className="absolute bottom-4 left-4 right-4">
<h3 className="font-semibold text-white">{project.title}</h3>
<p className="text-sm text-gray-200">{project.description}</p>
</div>
</div>
</div>
))}
</div>
);
}
Design Trends for 2024
Minimalist Layouts
Clean, focused designs that put your work front and center:
- Generous white space
- Typography-first approach
- Subtle animations and transitions
- Limited color palettes
Interactive Elements
Engage visitors with interactive components:
import { useState } from 'react';
export default function InteractiveSkillBar() {
const [hoveredSkill, setHoveredSkill] = useState<string | null>(null);
const skills = [
{ name: 'React', level: 90 },
{ name: 'TypeScript', level: 85 },
{ name: 'NextJS', level: 88 },
];
return (
<div className="space-y-4">
{skills.map((skill) => (
<div
key={skill.name}
onMouseEnter={() => setHoveredSkill(skill.name)}
onMouseLeave={() => setHoveredSkill(null)}
className="relative"
>
<div className="mb-1 flex justify-between">
<span className="font-medium">{skill.name}</span>
<span className="text-sm text-gray-600">{skill.level}%</span>
</div>
<div className="h-2 w-full rounded-full bg-gray-200">
<div
className="h-2 rounded-full bg-blue-600 transition-all duration-700 ease-out"
style={{
width: hoveredSkill === skill.name ? `${skill.level}%` : '0%',
}}
/>
</div>
</div>
))}
</div>
);
}
Dark Mode Support
Implement dark mode for better user experience:
'use client';
import { useTheme } from 'next-themes';
import { useState, useEffect } from 'react';
export default function ThemeToggle() {
const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme();
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return (
<button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="rounded-lg bg-gray-200 p-2 dark:bg-gray-800"
>
{theme === 'dark' ? '☀️' : '🌙'}
</button>
);
}
Image Optimization
Use NextJS Image component for optimal loading:
import Image from 'next/image';
export default function OptimizedPortfolioImage() {
return (
<Image
src="/project-screenshot.jpg"
alt="Project Screenshot"
width={600}
height={400}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
className="rounded-lg"
/>
);
}
Static Generation
Pre-generate pages for better performance:
export async function getStaticProps() {
const projects = await getProjects();
return {
props: {
projects,
},
revalidate: 3600,
};
}
SEO Best Practices
import Head from 'next/head';
export default function Portfolio() {
return (
<>
<Head>
<title>John Doe - Full Stack Developer</title>
<meta
name="description"
content="Portfolio of John Doe, a full-stack developer specializing in React and NextJS"
/>
<meta property="og:title" content="John Doe - Full Stack Developer" />
<meta
property="og:description"
content="Portfolio showcasing modern web development projects"
/>
<meta property="og:image" content="/og-image.jpg" />
<meta name="twitter:card" content="summary_large_image" />
</Head>
{/* Your portfolio content */}
</>
);
}
Structured Data
Add JSON-LD structured data for better search visibility:
const portfolioSchema = {
'@context': 'https://schema.org',
'@type': 'Person',
name: 'John Doe',
jobTitle: 'Full Stack Developer',
url: 'https://johndoe.dev',
sameAs: ['https://github.com/johndoe', 'https://linkedin.com/in/johndoe'],
};
export default function Portfolio() {
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(portfolioSchema) }}
/>
{}
</>
);
}
Conclusion
Creating an effective NextJS portfolio requires thoughtful planning, clean design, and optimal performance. Focus on showcasing your best work, ensuring fast load times, and providing a great user experience across all devices.
Remember to regularly update your portfolio with new projects and keep your content fresh and relevant to your target audience.