This is the source code for my personal website jmartinn.com. The site serves as a platform to showcase my work, share my thoughts through blog posts, and provide information about my professional journey.
This portfolio is built with modern web technologies and features a minimalist design focused on content and user experience. While the source code is available for reference, please note that the design, content, and assets are not licensed for reuse.
- Next.js - React framework for production
- TypeScript - Type-safe JavaScript
- Tailwind CSS - Utility-first CSS framework
- MDX - Markdown for the component era
- Vercel - For deployment and analytics
- Responsive minimalist design
- Blog with MDX support
- Dynamic OG image generation
- RSS feed
- Sitemap generation
- View counter for blog posts
- Performance monitoring with Vercel Analytics and Speed Insights
portfolio/
βββ app/ # Next.js 13+ app directory
β βββ (api)/ # API routes (analytics, RSS, resume)
β βββ blog/ # Blog pages and post layouts
β βββ about/ # About page
β βββ uses/ # Uses page
β βββ work/ # Work showcase page
βββ components/ # Reusable React components
β βββ blog/ # Blog-specific components
β βββ layout/ # Layout components (footer, sidebar)
β βββ mdx/ # MDX components
β βββ ui/ # UI components (badge, toast, icons)
βββ content/ # MDX files for blog posts
βββ lib/ # Utility functions and database actions
β βββ config/ # Application configuration
β βββ db/ # Database operations
β βββ constants.ts # Application constants
β βββ utils.ts # Utility functions
β βββ validation.ts # Input validation
βββ public/ # Static assets (fonts, images)
Before you begin, ensure you have the following installed:
- Node.js 18.x or higher
- pnpm 8.x or higher (recommended package manager)
- Git for version control
git clone https://github.com/jmartinn/portfolio.git
cd portfoliopnpm installCopy the example environment file and configure it:
cp .env.example .env.localEdit .env.local and fill in the required values. See Environment Variables section for details.
pnpm devOpen http://localhost:3000 to view your site.
pnpm build
pnpm start- Create account at supabase.com
- Create new project
- Create storage bucket named "resume"
- Upload your resume PDF as "main.pdf"
- Add to
.env.local:NEXT_PUBLIC_SUPABASE_URL=your_project_url SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
- Create app at Spotify Developer Dashboard
- Set redirect URI to
http://localhost:3000/callback - Follow OAuth2 Code Flow to get refresh token
- Generate auth token:
echo -n "CLIENT_ID:CLIENT_SECRET" | base64 - Add to
.env.local:SPOTIFY_CLIENT_ID=your_client_id SPOTIFY_REFRESH_TOKEN=your_refresh_token SPOTIFY_AUTH_TOKEN=your_base64_token
- Deploy to Vercel
- Add Postgres storage in project dashboard
- Run migration:
CREATE TABLE views ( slug TEXT PRIMARY KEY, count INTEGER NOT NULL DEFAULT 0 );
- Environment variables added automatically by Vercel
- Deploy to Vercel
- Add KV storage in project dashboard
- Environment variables added automatically by Vercel
- Create a new
.mdxfile in thecontent/directory:
touch content/my-new-post.mdx- Add frontmatter and content:
---
title: "My New Post"
publishedAt: "2025-10-24"
summary: "A brief description of the post"
keywords: ["nextjs", "react", "typescript"]
---
Your content here with **markdown** and React components!- The post will automatically appear in the blog listing
pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Start production server
pnpm lint # Run ESLint
pnpm format # Format code with Prettier- Push code to GitHub
- Import project in Vercel
- Add environment variables in project settings
- Deploy!
Vercel will automatically:
- Install dependencies
- Run build
- Deploy to CDN
- Provide Postgres and KV storage
- Framework: Next.js 15 (App Router)
- Language: TypeScript 5
- Styling: Tailwind CSS 3
- Content: MDX with custom components
- Database: Vercel Postgres
- Cache: Vercel KV (Redis)
- Storage: Supabase
- Deployment: Vercel
- Analytics: Vercel Analytics + Speed Insights
Blog posts use ISR with 1-hour revalidation:
- Posts are statically generated at build time
- Regenerated at most once per hour
- Provides optimal performance with fresh content
- React Cache: Request deduplication for blog posts
- Vercel KV: 59-minute TTL for Spotify tokens
- ISR: 1-hour revalidation for blog content
/api/track- Get currently playing Spotify track/api/spotify/token- Refresh Spotify access token/resume- Download resume PDF/rss- RSS feed for blog/og- Dynamic OpenGraph image generation
This is a personal portfolio, but feel free to:
- Report bugs via GitHub Issues
- Suggest improvements
- Use as reference for your own portfolio
The code is open source, but content and design are not licensed for reuse.