logo
Landing Page Examples
5 min read

23 NextJS Portfolio Template Examples For Design Inspiration

Explore the best NextJS portfolio templates and examples to showcase your work professionally and stand out from the competition.

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>
  );
}

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>
  );
}

Performance Optimization

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, // Revalidate every hour
  };
}

SEO Best Practices

Meta Tags

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) }}
      />
      {/* Your content */}
    </>
  );
}

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.

More great blogs...

logo

© 2008 - 2025  AppSphere.  All Rights Reserved.

Trading as Asyym Technology Ltd