HomeProjectsBlogAboutContactEducationSearch
Building My Portfolio Website with Next.js 13

Building My Portfolio Website with Next.js 13

Learn about the tech stack, architecture decisions, and implementation details of my personal portfolio website.

Bhavesh Patil
Bhavesh Patil
·4m read
Link copied to clipboard!
#next.js#tailwind#typescript#portfolio

I recently rebuilt my portfolio website using modern web technologies. In this post, I'll walk you through the tech stack, architecture decisions, and key features.

Tech Stack

  • Next.js 13: For server-side rendering and routing with App Router
  • TypeScript: For type safety and better developer experience
  • Tailwind CSS: For styling with utility-first approach
  • MDX: For blog posts with React components
  • React Icons: For beautiful icons and social media integration
  • Sugar High: For syntax highlighting in code blocks
  • Gray Matter: For MDX frontmatter parsing
  • Next Sitemap: For SEO optimization
  • PWA Support: For installable web app experience

Key Features

1. Dynamic Blog with MDX and Syntax Highlighting

I implemented a blog system using MDX with Sugar High for beautiful code highlighting:

typescript
// lib/blog.ts
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'

export function getPostBySlug(slug: string) {
  const fullPath = path.join(process.cwd(), 'app/blog/posts', `${slug}.mdx`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')
  const { data, content } = matter(fileContents)
  
  return {
    slug,
    content,
    ...data
  }
}

2. Responsive Navigation

The navigation system adapts to different screen sizes:

typescript
'use client'

import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useState } from 'react'
import { FiMenu, FiX } from 'react-icons/fi'
import { SITE_DATA } from '../../lib/shared'

export default function Navbar() {
  const pathname = usePathname()
  const [isMenuOpen, setIsMenuOpen] = useState(false)

  const toggleMenu = () => setIsMenuOpen(!isMenuOpen)

  return (
    <>
      <nav className="fixed top-0 left-0 right-0 bg-black/80 backdrop-blur-md z-50 border-b border-white/10">
        <div className="max-w-6xl mx-auto px-4 flex justify-between items-center py-4">
          <Link href="/" className="text-2xl font-bold">iambhvsh</Link>
          
          <button 
            onClick={toggleMenu}
            className="p-2 hover:bg-white/10 rounded-lg transition-colors"
            aria-label="Toggle menu"
          >
            {isMenuOpen ? <FiX className="w-6 h-6" /> : <FiMenu className="w-6 h-6" />}
          </button>
        </div>
      </nav>

      <div 
        className={`fixed top-[73px] left-0 right-0 bottom-0 z-40 bg-black/95 backdrop-blur-md transition-all duration-200 ease-out transform-gpu ${
          isMenuOpen ? 'translate-y-0 opacity-100' : '-translate-y-4 opacity-0 pointer-events-none'
        }`}
      >
        <div className="max-w-6xl mx-auto px-4 py-6 space-y-2">
          {SITE_DATA.pages.map(({ path, icon: Icon, name }) => (
            <Link
              key={path}
              href={path}
              onClick={toggleMenu}
              className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${
                pathname= path ? 'bg-white/10 text-white' : 'text-gray-400 hover:bg-white/5 hover:text-white'
              }`}
            >
              <Icon className="w-5 h-5" />
              {name}
            </Link>
          ))}
        </div>
      </div>
    </>
  )
}

3. Dark Mode Design

I chose a dark theme for better readability and modern aesthetics.

4. Search Functionality

Implemented a search system that works across pages and blog posts:

typescript
// api/search/route.ts
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const query = searchParams.get('q')

  // Search implementation
  const results = await searchContent(query)
  return NextResponse.json(results)
}

Performance Optimizations

  1. Image Optimization:
typescript
<Image
  src={post.coverImage}
  alt={post.title}
  width={1200}
  height={630}
  className="rounded-xl"
  priority
/>
  1. Code Splitting:
typescript
const MDXContent = dynamic(() => import('@/components/MDXContent'), {
  loading: () => <div>Loading...</div>
})

Deployment

The site is deployed on Vercel, which provides:

  • Automatic deployments
  • Edge functions
  • Analytics
  • Great performance out of the box

Lessons Learned

  1. TypeScript is Worth It: While it requires more initial setup, TypeScript caught many bugs before they reached production.

  2. MDX for Content: Using MDX for blog posts provides great flexibility in mixing markdown with React components.

  3. Tailwind's Utility-First Approach: Tailwind CSS made styling consistent and maintainable across the project.

Future Improvements

  • Add authentication for a comment system
  • Implement a newsletter
  • Add more interactive components
  • Improve search with better algorithms

Conclusion

Building this portfolio was a great learning experience. The combination of Next.js, TypeScript, and Tailwind CSS proved to be powerful and developer-friendly.

Feel free to check out the source code on GitHub and let me know what you think!

"The best way to learn is to build" - Every Developer Ever

Coding Setup


I hope this post helps others who are looking to build their own portfolio websites. If you have any questions, feel free to reach out!