This website

Crafting this portfolio with Next.js, TailwindCSS, and Contentlayer — a playground for my ideas and code.

Contents
0 sections

On this page

Loading table of contents...

portfolio screenshot

I developed a statically generated, performance-focused portfolio website with integrated blog functionality using Next.js 15, Tailwind CSS, and ContentLayer for content management.

  • Responsive design with a dark theme
  • A structured project showcase with detailed documentation pages
  • A learning curve section to document my journey and learnings
  • A full-featured blog system supporting series, tags, and technical content MDX integration for rich, interactive documentation with code highlighting

Technologies Used

Core Technologies

  • Next.js 15: I chose the latest version of Next.js for its server components, improved routing system, and image optimization capabilities. The App Router provides a clean, file-system-based approach to organizing pages.
  • React 19: Using React's latest version allowed me to leverage improved rendering performance and hooks for state management.
  • TypeScript: For type safety across the codebase, reducing bugs and improving developer experience with better autocomplete and error detection.
  • Tailwind CSS 4: For rapid UI development with utility classes that allow consistent styling while maintaining full design flexibility.

Content Management

  • ContentLayer2: I selected ContentLayer for type-safe MDX content management, which creates - TypeScript types from my content schema. This gives me the best of both worlds: file-based content that's fully typed.
  • MDX: For writing blog posts and project documentation with JSX components embedded in Markdown, allowing for rich interactive content.

Performance & UI Enhancements

  • Sharp: For optimized image processing, generating responsive images and blur placeholders for improved loading experience.
  • Framer Motion: For subtle animations that improve user experience without being distracting.
  • Tocbot: For automatically generating and updating table of contents from markdown headings.

Syntax Highlighting & Code Display

  • Rehype Pretty Code: For enhanced code syntax highlighting with line numbers, line highlighting, and proper theming.
  • Rehype GitHub Slug: For automatically generating anchor links for headings.

Development Process

Planning & Design

  • Started with defining content architecture for both projects and blog posts
  • Designed schema for content types including posts, projects, and series
  • Created wireframes for key pages before implementation
  • Established a color scheme and typography system

Implementation Approach

I followed a component-driven development approach:

  • Built core layout components (Header, Footer, Navigation)
  • Implemented content schema and processing pipeline
  • Developed individual page templates for different content types
  • Added interactive elements and optimizations
  • Implemented responsive design across all breakpoints

Key Challenges & Solutions

Challenge 1: Blog Series Implementation

Problem: Needed to create a system for blog post series that shows relationships between posts, maintains correct ordering, and provides navigation.

Solution: Implemented a nested Series type and navigation utilities:

// Series definition
export const Series = defineNestedType(() => ({
  name: "Series",
  fields: {
    title: {
      type: "string",
      required: true,
    },
    order: {
      type: "number",
      required: true,
    },
  },
}));
 
// Series navigation helper
export function getSeriesNavigation(currentPost: Post): {
  seriesTitle: string;
  currentIndex: number;
  totalPosts: number;
  prevPost: Post | null;
  nextPost: Post | null;
  allPosts: Post[];
} | null {
  if (!currentPost.series) return null;
 
  const seriesTitle = currentPost.series.title;
  const allPosts = getAllSeriesPostsWithDrafts(seriesTitle);
  const currentIndex = allPosts.findIndex(
    (post) => post.slug === currentPost.slug
  );
 
  // Navigation logic...
 
  return {
    seriesTitle,
    currentIndex,
    totalPosts: allPosts.length,
    prevPost,
    nextPost,
    allPosts,
  };
}

Challenge 2: Image Optimization with MDX Content

Problem: While Next.js provides excellent image optimization through its Image component, it has significant limitations when working with MDX content:

  1. Next Image component requires explicit width, height, and src props that are unknown for markdown images
  2. Images referenced in MDX are written as ![alt](src) and are relative to the content file, not the public directory
  3. Next.js doesn't natively optimize images in MDX content during the build process
  4. Standard markdown transformers don't extract image dimensions needed by Next Image

Solution Part 1: Created a custom image processor for featured images in frontmatter:

@utils/image-processor.ts
export const createImageProcessor =
  (contentType: string) => async (doc: any) => {
    try {
      // Get image from relative path in content directory
      const contentRootDir = path.join(process.cwd(), "src/content");
      const relativeDir = doc._raw.sourceFileDir;
      const featuredImageRootPath = path.join(contentRootDir, relativeDir);
      const imagePath = path.join(featuredImageRootPath, doc.featuredImageUrl);
 
      // Process with Sharp to get metadata and create blur placeholder
      const image = sharp(imagePath);
      const metadata = await image.metadata();
      const width = metadata.width ?? 0;
      const height = metadata.height ?? 0;
 
      // Generate blur data URL for placeholder
      const blurdataurl = await image
        .resize(8, Math.round(8 / imgAspectRatio))
        .png({ quality: 75 })
        .toBuffer()
        .then((buffer) => `data:image/png;base64,${buffer.toString("base64")}`);
 
      // Use content hash to prevent duplicates and enable cache invalidation
      const fileHash = await generateFileHash(imagePath);
 
      // Copy to public directory with hash in filename for proper caching
      const destDir = path.join(publicDir, relativeDir, fileNameWithHash);
      const src = path.join(`/assets/${relativeDir}`, fileNameWithHash);
 
      // Now images are accessible to Next.js Image component with all required props
      return {
        src,
        width,
        height,
        blurdataurl,
      };
    } catch (error) {
      // Error handling...
    }
  };

Solution Part 2: Created a custom Rehype plugin (static-media.ts) to transform inline markdown images:

This plugin processes any <img> tags in the MDX content during build time. It:

  1. Locates the original image relative to the MDX file
  2. Processes it with Sharp to extract dimensions
  3. Generates a blur placeholder image
  4. Creates a content hash for cache invalidation
  5. Copies the image to the public directory
  6. Transforms the image tag to include all props needed by Next.js Image

The solution effectively bridges the gap between Markdown's simple image syntax and Next.js Image component's requirements.

Code Architecture Decisions

Content-Driven Architecture

I designed the architecture around content types with clear separation between:

  1. Content Storage: Markdown files organized by content type in src/content/
  2. Content Processing: ContentLayer configuration and utility functions
  3. Component Rendering: React components for different content views
  4. Page Templates: Next.js page components that integrate content and UI

Component Organization

Components are organized by functionality:

  • Shared Components: Reusable UI elements (buttons, tags, sections)
  • Feature-Specific Components: Components for blog, projects, hero section
  • Layout Components: Header, footer, and page layout components

Data Flow & Content Service

I created a content-service.ts module that centralizes all content fetching logic, providing a clean API for page components. This approach:

  1. Encapsulates ContentLayer-specific implementation details
  2. Provides a unified interface for both blog and project content
  3. Handles environment-specific logic (showing drafts in development only)
  4. Manages series relationships and navigation

Lazy Loading

Lazy-loaded images with blur placeholders

Key Features

  1. MDX Content with Code Highlighting

The portfolio incorporates a robust MDX rendering system featuring:

  • Syntax highlighting for code blocks with support for multiple languages
  • Line highlighting for emphasizing specific code sections
  • Automatic code block copy functionality
  • Custom component integration in markdown content
  1. Blog Series Support

A complete series management system that includes:

  • Automated series navigation with previous/next post links
  • Series progress tracking with visual indicators
  • Series table of contents with expandable post list
  • Support for planned posts in series (visible but not accessible)
  1. Project Documentation

Detailed project documentation pages featuring:

  • Technology tag system with icon representation
  • Project categorization and filtering
  • Detailed markdown documentation with table of contents
  • GitHub integration for source code access
  1. Responsive Design System

A fully responsive design with:

  • Mobile-first approach with adaptive layouts
  • Custom navigation for mobile devices
  • Optimized typography and spacing across device sizes
  • Performance optimizations for mobile networks
  1. Image Optimization Pipeline

Solved the challenging problem of integrating Next.js Image optimization with MDX content through:

  • Custom image processing for both featured images and inline MDX images
  • Automatic generation of width, height and blur placeholders for all images
  • Content-hash based file naming for proper cache invalidation
  • Rehype plugin (static-media.ts) that transforms inline markdown images during the build process

Performance & Outcomes

performance report

Performance Metrics

  • Lighthouse Scores:

    • Performance: 95+
    • Accessibility: 98+
    • Best Practices: 95+
    • SEO: 100
  • Core Web Vitals:

    • Largest Contentful Paint (LCP): < 1.5s
    • First Input Delay (FID): < 100ms
    • Cumulative Layout Shift (CLS): < 0.1

SEO Optimization

  • Semantic HTML structure
  • Proper heading hierarchy
  • Meta descriptions and OpenGraph tags
  • Structured data for blog posts
  • Sitemap generation
  • robots.txt

Content Management Efficiency

  • Simplified content publishing workflow
  • Type-safe content with schema validation
  • Automated image processing
  • Draft/published status system

Future Enhancements

  • Search Functionality: Adding full-text search across projects and blog posts
  • Accessibility: Work more on accessibility
  • Dark/Light Theme Toggle: Supporting user preference for color scheme
  • Newsletter Integration: Adding email subscription for blog updates
  • Analytics Integration: Adding privacy-focused analytics to track content performance