Featured Post

Building a Modern Portfolio with Next.js 15

A comprehensive guide to creating a high-performance portfolio website using Next.js 15, TypeScript, and Tailwind CSS with advanced features like dark mode and scroll animations.

Richard Joseph Porter
5 min read
nextjstypescripttailwindportfolioweb-developmentvercelperformanceseo

Creating a professional portfolio website is crucial for any developer looking to showcase their skills and attract potential clients or employers. In this comprehensive guide, I'll walk you through building a modern, high-performance portfolio using the latest web technologies.

Why Next.js 15?

Next.js 15 introduces several compelling features that make it an excellent choice for portfolio websites:

  • App Router: The new App Router provides a more intuitive file-based routing system
  • Server Components: Improved performance with server-side rendering by default
  • TypeScript Integration: First-class TypeScript support out of the box
  • Optimized Bundle: Smaller bundle sizes and faster loading times
  • SEO Friendly: Built-in optimization for search engines

Project Architecture

Tech Stack Overview

const techStack = {
  framework: "Next.js 15",
  language: "TypeScript",
  styling: "Tailwind CSS",
  animations: "CSS + Intersection Observer",
  deployment: "Vercel",
  performance: "Lighthouse Score 95+"
};

Directory Structure

portfolio/
├── src/
│   ├── app/
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── globals.css
│   ├── components/
│   │   ├── Hero.tsx
│   │   ├── Navigation.tsx
│   │   ├── Projects.tsx
│   │   └── Contact.tsx
│   ├── hooks/
│   │   ├── useScrollReveal.ts
│   │   └── useCountUp.ts
│   └── contexts/
│       └── ThemeContext.tsx
├── public/
│   ├── projects/
│   └── resume.pdf
└── package.json

Key Features Implementation

1. Responsive Navigation

The navigation component adapts to different screen sizes and provides smooth scrolling:

'use client';

import { useState, useEffect } from 'react';

export default function Navigation() {
  const [isScrolled, setIsScrolled] = useState(false);
  const [activeSection, setActiveSection] = useState('hero');

  useEffect(() => {
    const handleScroll = () => {
      setIsScrolled(window.scrollY > 50);
    };
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  return (
    <nav className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
      isScrolled ? 'bg-white/80 backdrop-blur-md shadow-lg' : 'bg-transparent'
    }`}>
      {/* Navigation content */}
    </nav>
  );
}

2. Dark Mode Implementation

Using React Context for theme management. For a detailed implementation guide, see my article on implementing dark mode with Tailwind CSS:

'use client';

import { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark' | 'system';

interface ThemeContextType {
  theme: Theme;
  setTheme: (theme: Theme) => void;
  resolvedTheme: 'light' | 'dark';
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<Theme>('system');
  const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');

  useEffect(() => {
    const root = window.document.documentElement;
    
    if (theme === 'system') {
      const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
      setResolvedTheme(systemTheme);
      root.classList.toggle('dark', systemTheme === 'dark');
    } else {
      setResolvedTheme(theme);
      root.classList.toggle('dark', theme === 'dark');
    }
  }, [theme]);

  return (
    <ThemeContext.Provider value={{ theme, setTheme, resolvedTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

3. Scroll Reveal Animations

Custom hook for intersection observer animations:

import { useEffect, useRef } from 'react';

export function useScrollReveal(threshold = 0.1, delay = 0) {
  const elementRef = useRef<HTMLElement>(null);

  useEffect(() => {
    const element = elementRef.current;
    if (!element) return;

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setTimeout(() => {
            element.classList.add('animate-fadeInUp');
          }, delay);
          observer.unobserve(element);
        }
      },
      { threshold }
    );

    observer.observe(element);

    return () => observer.disconnect();
  }, [threshold, delay]);

  return elementRef;
}

Performance Optimization

Image Optimization

Using Next.js Image component for optimal performance:

import Image from 'next/image';

export default function ProjectCard({ project }: { project: Project }) {
  return (
    <div className="project-card">
      <Image
        src={project.image}
        alt={`${project.title} screenshot`}
        width={600}
        height={400}
        className="w-full h-48 object-cover"
        priority={project.featured}
      />
    </div>
  );
}

Bundle Analysis

Monitor bundle size and optimize imports:

npm install --save-dev @next/bundle-analyzer

# In next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({
  // Next.js config
});

SEO and Meta Tags

Implement proper meta tags for better search engine optimization:

// app/layout.tsx
import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Richard Joseph Porter | Full-Stack Developer',
  description: 'Experienced full-stack developer specializing in modern web technologies...',
  keywords: ['developer', 'portfolio', 'next.js', 'typescript'],
  authors: [{ name: 'Richard Joseph Porter' }],
  openGraph: {
    title: 'Richard Joseph Porter | Full-Stack Developer',
    description: 'Experienced full-stack developer specializing in modern web technologies...',
    url: 'https://richardporter.dev',
    siteName: 'Richard Joseph Porter',
    images: [
      {
        url: '/og-image.png',
        width: 1200,
        height: 630,
      },
    ],
    locale: 'en_US',
    type: 'website',
  },
};

Deployment and Performance

Vercel Deployment

  1. Connect your GitHub repository to Vercel
  2. Configure build settings:
{
  "buildCommand": "npm run build",
  "outputDirectory": ".next",
  "framework": "nextjs"
}

Performance Monitoring

Track Core Web Vitals:

// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  );
}

Results and Metrics

After implementing these optimizations, the portfolio achieves:

  • Lighthouse Performance: 98/100
  • First Contentful Paint: <1.5s
  • Largest Contentful Paint: <2.5s
  • Cumulative Layout Shift: <0.1
  • Bundle Size: <200KB initial load

Conclusion

Building a modern portfolio with Next.js 15 provides an excellent foundation for showcasing your work while demonstrating your technical skills. The combination of TypeScript, Tailwind CSS, and thoughtful performance optimizations creates a professional, fast-loading website that stands out to potential clients and employers.

Once your portfolio is built, consider extending it with additional features like a blog system for sharing your insights and a secure contact form for client inquiries.

Key takeaways:

  1. Performance First: Optimize images, minimize bundle size, and leverage Next.js built-in optimizations
  2. User Experience: Implement smooth animations and responsive design
  3. SEO Optimization: Use proper meta tags and structured data
  4. Modern Stack: Stay current with the latest web technologies
  5. Accessibility: Ensure your portfolio is accessible to all users

The modern web development landscape offers incredible tools for creating impressive portfolio websites. By leveraging Next.js 15's capabilities and following best practices, you can build a portfolio that not only showcases your projects but also demonstrates your commitment to quality and performance.

Richard Joseph Porter - Professional headshot

Richard Joseph Porter

Full-stack developer with expertise in modern web technologies. Passionate about building scalable applications and sharing knowledge through technical writing.