A modern, responsive portfolio website built with Next.js, featuring a single source of truth for content management. The site displays professional experience, skills, and projects, with an integrated CV/resume download feature.
This portfolio website showcases:
- Hero Section - Personal introduction with profile image
- About Section - Professional summary
- Experience - Work history with achievements
- Skills - Technical skills organized by category
- Projects - Portfolio projects (website-only)
- Contact - Contact information and social links
- Resume Download - PDF resume generation and download
- Next.js 16 - React framework with App Router
- React 19 - UI library
- TypeScript - Type safety
- Tailwind CSS v4 - Styling
- shadcn/ui - Accessible component library (base-ui)
- lucide-react - Icon library
- next-themes - Dark mode support
- Markdown - Content files with YAML frontmatter
- gray-matter - Markdown/YAML parsing
- js-yaml - YAML parsing for content generation
- RenderCV - Professional PDF resume generation (Python)
- Biome - Linting and formatting
- Bun - Package manager and runtime
gutheil.dev/
βββ data/ # Single source of truth
β βββ resume.yaml # YAML file with all CV data
βββ content/ # Generated Markdown files (auto-generated)
β βββ hero.md
β βββ about.md
β βββ experiences.md
β βββ skills.md
β βββ projects.md # Manual edits only
βββ public/ # Static assets
β βββ Profile_bw.jpg
β βββ resume.pdf
βββ scripts/ # Generation scripts
β βββ generate-content-from-yaml.ts
β βββ resume/
β βββ generate-pdf.ts
βββ src/
β βββ app/ # Next.js App Router
β βββ components/ # React components
β βββ lib/ # Utilities
β βββ content.ts # Content loading logic
βββ docs/ # Documentation
- Node.js 18+ (or Bun)
- Python 3.12+ (for RenderCV)
- RenderCV - Install with:
pip3 install "rendercv[full]"
-
Clone the repository
git clone <your-repo-url> cd gutheil.dev
-
Install dependencies
bun install # or npm install -
Generate content from YAML
bun run content:generate
This reads
data/resume.yamland generates Markdown files incontent/. -
Generate PDF resume (optional, for first run)
bun run resume:generate
This generates the PDF resume to
public/resume.pdf. -
Start the development server
bun run dev # or npm run dev -
Open your browser Navigate to http://localhost:3000
The project uses a single source of truth approach:
- Edit once: Update
data/resume.yaml - Generate everything: Run
bun run update:all - Result: Both website content and CV PDF are updated
# Generate website content from YAML
bun run content:generate
# Generate PDF resume from YAML
bun run resume:generate
# Generate both (recommended)
bun run update:all
# Development
bun run dev # Start dev server
bun run build # Build for production
bun run start # Start production server
# Code quality
bun run lint # Run linter
bun run format # Format code- Edit
data/resume.yaml - Run
bun run update:all - Commit the generated files (content/.md and public/.pdf)
- Deploy - Changes appear on the website
- Projects (
content/projects.md) - Edit manually, not generated from YAML - Profile Image - Set in generated
content/hero.mdor edit manually
- Environment Variable:
CONTACT_EMAILis the single source of truth for your email - Automatic Override: The email in
content/hero.mdanddata/Alexander_Gutheil_CV.yamlis automatically replaced withCONTACT_EMAILwhen:- Generating content files (
bun run content:generate) - Generating CV PDF (
bun run cv:generate) - Loading content in the application (
getHeroSection())
- Generating content files (
- Set Once: Configure
CONTACT_EMAILin your.env.localfile and Vercel environment variables
- β Responsive Design - Works on all devices
- β Dark Mode - Automatic theme switching
- β Fast Performance - Static generation with ISR
- β SEO Optimized - Structured data and meta tags
- β Accessible - WCAG compliant components
- β Type Safe - Full TypeScript coverage
The project is configured for deployment on Vercel via GitHub:
-
Push to GitHub
git add . git commit -m "Update content" git push origin main
-
Connect to Vercel
- Go to vercel.com
- Import your GitHub repository
- Vercel will auto-detect Next.js
-
Build Settings
- Framework Preset: Next.js
- Build Command:
bun run build(ornpm run build) - Output Directory:
.next(default) - Install Command:
bun install(ornpm install)
-
Environment Variables
Create a
.env.localfile in the root directory:# Required for contact form RESEND_API_KEY=re_xxxxxxxxxxxxx # Required: Your contact email (single source of truth) [email protected] # Optional: Custom "from" email (defaults to [email protected] for testing) # Must be a verified domain in Resend for production RESEND_FROM_EMAIL=Contact Form <[email protected]>
Getting your Resend API Key:
- Sign up at resend.com
- Go to API Keys in the dashboard
- Create a new API key
- Copy it to your
.env.localfile
Setting up Custom Domain (gutheil.dev):
- See the detailed guide: Resend Setup with Custom Domain
- This includes DNS configuration, domain verification, and troubleshooting
Note:
- The
CONTACT_EMAILenvironment variable is the single source of truth for your email address - It will override any email in
content/hero.mdanddata/Alexander_Gutheil_CV.yaml - For production on Vercel, add these environment variables in the Vercel dashboard under Project Settings β Environment Variables
-
Deploy
- Vercel automatically deploys on every push to main
- Preview deployments for pull requests
Vercel will:
- Install dependencies (
bun install) - Run build (
bun run build) - Deploy the static site
Note: The PDF resume must be generated locally and committed to the repository, as RenderCV (Python) is not available in Vercel's build environment. The PDF in public/ is served statically.
If you need to update the resume PDF:
-
Generate locally
bun run resume:generate
-
Commit the PDF
git add public/resume.pdf git commit -m "Update resume PDF" git push -
Vercel automatically redeploys
- Content Architecture - How content flows through the system
- Single Source of Truth - Content management workflow
- Resume Workflow - CV generation details
- Resend Setup - Setting up Resend with custom domain
- Create component in
src/components/sections/ - Add content type in
src/lib/content.ts - Generate content file or add to YAML
- Import and use in
src/app/page.tsx
- Uses Tailwind CSS v4
- Components from shadcn/ui
- Custom styles in
src/app/globals.css - Dark mode via
next-themes
Private project - All rights reserved
Alexander Gutheil - a.gutheil.dev