Back to Projects

Full-Stack Web Application

Dynamic Dual-Portfolio Website

About this Project

Project Overview

This project is the very portfolio you are browsing now. It was conceived as a "hub-and-spoke" platform to professionally showcase two distinct skill sets—software development and photography—under a single, cohesive brand. The architecture is a modern monorepo containing a headless Strapi backend and a multi-domain Next.js frontend.

Core Features

  • Multi-Domain Routing: The application intelligently serves different content based on the domain name (codeby.joeldettinger.de for software, photosby.joeldettinger.de for photography) using Next.js Middleware.
  • Headless CMS Integration: All project details, skill listings, photo albums, and testimonials are dynamically fetched from a self-hosted Strapi CMS, allowing for easy content updates without code changes.
  • Full Internationalization (i18n): The entire user interface, including all content from the CMS, is available in both English and German, with locale detection based on browser settings and a manual language switcher.
  • Custom Strapi Controllers: The backend features custom controllers, such as for the testimonial submission form, which integrates Google reCAPTCHA for spam protection and custom logic for organizing file uploads into specific media library folders.
  • Dynamic Theming: A "Paper-like" design with full support for system, light, and dark modes, built with Tailwind CSS and next-themes.

Technical Challenges & Solutions

One of the main challenges was creating a seamless local development experience that could accurately simulate the multi-domain production environment. This was solved by implementing a DEV_FORCE_DOMAIN environment variable that instructs the Next.js Middleware to route localhost traffic as if it were coming from a specific subdomain.

Another challenge was handling file uploads from a public-facing form. This required careful configuration of Strapi's user roles, permissions for the upload plugin, and a custom controller to validate the request before passing the file to Strapi's upload service.